Skip to content

Commit 7f251db

Browse files
committed
Update tests
1 parent e25f9d4 commit 7f251db

File tree

10 files changed

+160
-206
lines changed

10 files changed

+160
-206
lines changed

core/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@
158158
<artifactId>openapi-generator-maven-plugin</artifactId>
159159
<version>5.15.0-SNAPSHOT</version>
160160
<configuration>
161-
<!-- <skip>true</skip>-->
161+
<skip>true</skip>
162162
<!-- TODO: remove this once Cloud SDK 5.15.0 is released -->
163163
<outputDirectory>${project.basedir}/src/main/java</outputDirectory>
164164
<apiMaturity>released</apiMaturity>

core/src/main/java/com/sap/ai/sdk/core/AiCoreService.java

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
import com.sap.cloud.sdk.cloudplatform.connectivity.exception.DestinationAccessException;
1212
import com.sap.cloud.sdk.cloudplatform.connectivity.exception.DestinationNotFoundException;
1313
import com.sap.cloud.sdk.services.openapi.apiclient.ApiClient;
14-
import java.util.NoSuchElementException;
1514
import java.util.function.Supplier;
1615
import javax.annotation.Nonnull;
1716
import lombok.AccessLevel;
@@ -71,7 +70,8 @@ public AiCoreService withBaseDestination(@Nonnull final HttpDestination destinat
7170
}
7271

7372
/**
74-
* Get the base destination for AI Core service calls. This destination won't have any resource group set.
73+
* Get the base destination for AI Core service calls. This destination won't have any resource
74+
* group set.
7575
*
7676
* @return The base destination.
7777
* @throws DestinationAccessException If there was an issue creating the base destination, e.g. in
@@ -104,12 +104,12 @@ public HttpDestination getDestinationForDeploymentById(
104104
*
105105
* @param model The model to be used for inference calls.
106106
* @return A new instance of the AI Core Deployment.
107-
* @throws NoSuchElementException if no running deployment is found for the model.
107+
* @throws DeploymentResolutionException if no running deployment is found for the model.
108108
*/
109109
@Nonnull
110110
public HttpDestination getDestinationForDeploymentByModel(
111111
@Nonnull final String resourceGroup, @Nonnull final AiModel model)
112-
throws NoSuchElementException {
112+
throws DeploymentResolutionException {
113113
val deploymentId = deploymentResolver.getDeploymentIdByModel(resourceGroup, model);
114114
return toInferenceDestination(resourceGroup, deploymentId);
115115
}
@@ -120,12 +120,12 @@ public HttpDestination getDestinationForDeploymentByModel(
120120
*
121121
* @param scenarioId The scenario id to be used for AI Core service calls.
122122
* @return A new instance of the AI Core Deployment.
123-
* @throws NoSuchElementException if no running deployment is found for the scenario.
123+
* @throws DeploymentResolutionException if no running deployment is found for the scenario.
124124
*/
125125
@Nonnull
126126
public HttpDestination getDestinationForDeploymentByScenario(
127127
@Nonnull final String resourceGroup, @Nonnull final String scenarioId)
128-
throws NoSuchElementException {
128+
throws DeploymentResolutionException {
129129
val deploymentId = deploymentResolver.getDeploymentIdByScenario(resourceGroup, scenarioId);
130130
return toInferenceDestination(resourceGroup, deploymentId);
131131
}
@@ -152,7 +152,7 @@ public ApiClient getApiClient() {
152152
return new ApiClient(rt).setBasePath(destination.asHttp().getUri().toString());
153153
}
154154

155-
private HttpDestination toInferenceDestination(
155+
HttpDestination toInferenceDestination(
156156
@Nonnull final String resourceGroup, @Nonnull final String deploymentId) {
157157
val destination = getBaseDestination();
158158
val path = buildDeploymentPath(deploymentId);
@@ -176,6 +176,6 @@ private static String buildDeploymentPath(@Nonnull final String deploymentId) {
176176
* @param resourceGroup the resource group of the deleted deployment, usually "default".
177177
*/
178178
public void reloadCachedDeployments(@Nonnull final String resourceGroup) {
179-
new DeploymentResolver(this).resetCache(resourceGroup);
179+
new DeploymentResolver(this).reloadDeployments(resourceGroup);
180180
}
181181
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package com.sap.ai.sdk.core;
2+
3+
import lombok.experimental.StandardException;
4+
5+
@StandardException
6+
public class DeploymentResolutionException extends RuntimeException {}

core/src/main/java/com/sap/ai/sdk/core/DeploymentResolver.java

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,8 @@
22

33
import com.sap.ai.sdk.core.client.DeploymentApi;
44
import com.sap.ai.sdk.core.client.model.AiDeployment;
5-
import com.sap.cloud.sdk.services.openapi.core.OpenApiRequestException;
65
import java.util.HashSet;
76
import java.util.Map;
8-
import java.util.NoSuchElementException;
97
import java.util.Objects;
108
import java.util.Optional;
119
import java.util.Set;
@@ -43,14 +41,15 @@ class DeploymentResolver {
4341
*
4442
* @param resourceGroup the resource group, usually "default".
4543
*/
46-
void resetCache(@Nonnull final String resourceGroup) {
44+
void reloadDeployments(@Nonnull final String resourceGroup) {
4745
cache.remove(resourceGroup);
4846
try {
49-
val deployments =
50-
new HashSet<>(new DeploymentApi(service).query(resourceGroup).getResources());
47+
val apiClient = new DeploymentApi(service);
48+
val deployments = new HashSet<>(apiClient.query(resourceGroup).getResources());
5149
cache.put(resourceGroup, deployments);
52-
} catch (final OpenApiRequestException e) {
53-
log.error("Failed to load deployments into cache", e);
50+
} catch (final RuntimeException e) {
51+
throw new DeploymentResolutionException(
52+
"Failed to load deployments for resource group " + resourceGroup, e);
5453
}
5554
}
5655

@@ -61,16 +60,16 @@ void resetCache(@Nonnull final String resourceGroup) {
6160
* @param resourceGroup the resource group, usually "default".
6261
* @param model the foundation model.
6362
* @return the deployment id.
64-
* @throws NoSuchElementException if no running deployment is found for the model.
63+
* @throws DeploymentResolutionException if no running deployment is found for the model.
6564
*/
6665
@Nonnull
6766
String getDeploymentIdByModel(@Nonnull final String resourceGroup, @Nonnull final AiModel model)
68-
throws NoSuchElementException {
67+
throws DeploymentResolutionException {
6968
final Predicate<AiDeployment> predicate = deployment -> isDeploymentOfModel(model, deployment);
7069
return resolveDeployment(resourceGroup, predicate)
7170
.orElseThrow(
7271
() ->
73-
new NoSuchElementException(
72+
new DeploymentResolutionException(
7473
"No running deployment found for model: " + model.name()));
7574
}
7675

@@ -81,18 +80,18 @@ String getDeploymentIdByModel(@Nonnull final String resourceGroup, @Nonnull fina
8180
* @param resourceGroup the resource group, usually "default".
8281
* @param scenarioId the scenario id, can be "orchestration".
8382
* @return the deployment id.
84-
* @throws NoSuchElementException if no running deployment is found for the scenario.
83+
* @throws DeploymentResolutionException if no running deployment is found for the scenario.
8584
*/
8685
@Nonnull
8786
String getDeploymentIdByScenario(
8887
@Nonnull final String resourceGroup, @Nonnull final String scenarioId)
89-
throws NoSuchElementException {
88+
throws DeploymentResolutionException {
9089
final Predicate<AiDeployment> predicate =
9190
deployment -> scenarioId.equals(deployment.getScenarioId());
9291
return resolveDeployment(resourceGroup, predicate)
9392
.orElseThrow(
9493
() ->
95-
new NoSuchElementException(
94+
new DeploymentResolutionException(
9695
"No running deployment found for scenario: " + scenarioId));
9796
}
9897

@@ -109,7 +108,7 @@ Optional<String> resolveDeployment(
109108
if (deployment.isPresent()) {
110109
return deployment;
111110
}
112-
resetCache(resourceGroup);
111+
reloadDeployments(resourceGroup);
113112
return getCachedDeployment(resourceGroup, predicate);
114113
}
115114
}

core/src/main/java/com/sap/ai/sdk/core/DestinationResolver.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@
2222
@Slf4j
2323
@AllArgsConstructor
2424
class DestinationResolver {
25-
static final String AI_CLIENT_TYPE_KEY = "URL.headers.AI-Client-Type";
26-
static final String AI_CLIENT_TYPE_VALUE = "AI SDK Java";
25+
private static final String AI_CLIENT_TYPE_KEY = "URL.headers.AI-Client-Type";
26+
private static final String AI_CLIENT_TYPE_VALUE = "AI SDK Java";
2727

2828
private static final String BASE_PATH = "/v2/";
2929

core/src/test/java/com/sap/ai/sdk/core/AiCoreServiceTest.java

Lines changed: 30 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -2,120 +2,79 @@
22

33
import static org.assertj.core.api.Assertions.assertThat;
44
import static org.assertj.core.api.Assertions.assertThatCode;
5-
import static org.mockito.Mockito.doReturn;
6-
import static org.mockito.Mockito.doThrow;
7-
import static org.mockito.Mockito.mock;
8-
import static org.mockito.Mockito.never;
9-
import static org.mockito.Mockito.times;
10-
import static org.mockito.Mockito.verify;
115

126
import com.sap.cloud.sdk.cloudplatform.connectivity.DefaultHttpDestination;
13-
import com.sap.cloud.sdk.cloudplatform.connectivity.Destination;
147
import com.sap.cloud.sdk.cloudplatform.connectivity.Header;
158
import com.sap.cloud.sdk.cloudplatform.connectivity.HttpDestination;
169
import com.sap.cloud.sdk.cloudplatform.connectivity.exception.DestinationAccessException;
17-
import com.sap.cloud.sdk.services.openapi.apiclient.ApiClient;
18-
import javax.annotation.Nonnull;
10+
import java.util.function.Supplier;
1911
import lombok.val;
2012
import org.junit.jupiter.api.BeforeEach;
2113
import org.junit.jupiter.api.Test;
2214

2315
class AiCoreServiceTest {
24-
private static final HttpDestination serviceBindingDestination =
25-
DefaultHttpDestination.builder("https://api.ai.com").build();
16+
private static final HttpDestination baseDestination =
17+
DefaultHttpDestination.builder("https://api.ai.com/v2/").build();
2618

27-
private DestinationResolver resolver;
19+
private Supplier<HttpDestination> destinationResolver;
2820
private AiCoreService service;
2921

3022
@BeforeEach
3123
void setUp() {
32-
resolver = mock(DestinationResolver.class);
33-
doReturn(serviceBindingDestination).when(resolver).getDestination();
24+
destinationResolver = () -> baseDestination;
3425

35-
service = new AiCoreService(resolver);
26+
service = new AiCoreService(destinationResolver);
3627
}
3728

3829
@Test
3930
void testLazyDestinationLoading() {
40-
doThrow(new DestinationAccessException()).when(resolver).getDestination();
31+
destinationResolver =
32+
() -> {
33+
throw new DestinationAccessException("Test");
34+
};
4135

42-
assertThatCode(() -> new AiCoreService(resolver))
36+
assertThatCode(() -> new AiCoreService(destinationResolver))
4337
.describedAs("This must not perform any destination loading upon initialization")
4438
.doesNotThrowAnyException();
45-
46-
verify(resolver, never()).getDestination();
4739
}
4840

4941
@Test
50-
void testBaseDestination() {
51-
assertThat(service.getBaseDestination()).isEqualTo(serviceBindingDestination);
42+
void testDefaultBaseDestination() {
43+
assertThat(service.getBaseDestination())
44+
.describedAs(
45+
"By default the destination obtained from the destination resolver should be used as-is")
46+
.isEqualTo(baseDestination);
5247
}
5348

5449
@Test
55-
void testGetDestination() {
56-
var destination = service.destination();
57-
assertThat(destination).isNotEqualTo(serviceBindingDestination);
58-
assertThat(destination.getUri()).hasHost("api.ai.com").hasPath("/v2/");
59-
assertThat(destination.getHeaders())
50+
void testCustomBaseDestination() {
51+
val customDestination =
52+
DefaultHttpDestination.builder("https://custom.ai.com/custom/base/path").build();
53+
val customService = service.withBaseDestination(customDestination);
54+
55+
assertThat(customService.getBaseDestination().getUri())
56+
.hasHost("custom.ai.com")
57+
.hasPath("/custom/base/path");
58+
assertThat(customService.getBaseDestination().getHeaders())
59+
.describedAs("The client type should be added to ensure its present in all requests")
6060
.containsExactly(new Header("AI-Client-Type", "AI SDK Java"));
6161
}
6262

6363
@Test
64-
void testDeploymentDestination() {
65-
var destination = service.forDeployment("123").withResourceGroup("foo").destination();
64+
void testToInferenceDestination() {
65+
val destination = service.toInferenceDestination("foo", "123");
6666

6767
assertThat(destination.getUri())
6868
.hasHost("api.ai.com")
6969
.hasPath("/v2/inference/deployments/123/");
70-
assertThat(destination.getHeaders())
71-
.containsExactly(
72-
new Header("AI-Client-Type", "AI SDK Java"), new Header("AI-Resource-Group", "foo"));
73-
74-
assertThat(service.destination())
75-
.describedAs("The service object should remain unchanged")
76-
.extracting(HttpDestination::getUri)
77-
.isEqualTo(serviceBindingDestination.getUri());
7870

79-
verify(resolver, times(2)).getDestination();
71+
assertThat(destination.getHeaders()).containsExactly(new Header("AI-Resource-Group", "foo"));
8072
}
8173

8274
@Test
8375
void testBuildApiClient() {
84-
var apiClient = service.client();
76+
var apiClient = service.getApiClient();
8577

8678
assertThat(apiClient.getBasePath()).hasToString("https://api.ai.com/v2/");
87-
88-
verify(resolver, times(1)).getDestination();
89-
}
90-
91-
@Test
92-
void testCustomization() {
93-
val customService =
94-
new AiCoreService() {
95-
@Nonnull
96-
@Override
97-
protected HttpDestination getBaseDestination() {
98-
return DefaultHttpDestination.builder("https://ai").build();
99-
}
100-
101-
@Nonnull
102-
@Override
103-
protected ApiClient buildApiClient(@Nonnull Destination destination) {
104-
return new ApiClient().setBasePath("https://fizz.buzz").setUserAgent("SAP");
105-
}
106-
};
107-
108-
val customServiceForDeployment =
109-
customService.forDeployment("deployment").withResourceGroup("group");
110-
111-
val client = customServiceForDeployment.client();
112-
assertThat(client.getBasePath()).isEqualTo("https://fizz.buzz");
113-
114-
val destination = customServiceForDeployment.destination().asHttp();
115-
assertThat(destination.getUri()).hasToString("https://ai/v2/inference/deployments/deployment/");
116-
117-
val resourceGroup = customService.resourceGroup;
118-
assertThat(resourceGroup).isEqualTo("group");
119-
assertThat(destination.getHeaders()).contains(new Header("AI-Resource-Group", "group"));
12079
}
12180
}

0 commit comments

Comments
 (0)