From ea3a65adf975a890011700d894fd31090fa006a9 Mon Sep 17 00:00:00 2001 From: Roshin Rajan Panackal Date: Thu, 22 Aug 2024 15:28:17 +0200 Subject: [PATCH 01/11] Minor line change --- e2e-test-app/src/main/java/com/sap/ai/sdk/app/Application.java | 1 + 1 file changed, 1 insertion(+) diff --git a/e2e-test-app/src/main/java/com/sap/ai/sdk/app/Application.java b/e2e-test-app/src/main/java/com/sap/ai/sdk/app/Application.java index 5378b970d..8a0bed18a 100644 --- a/e2e-test-app/src/main/java/com/sap/ai/sdk/app/Application.java +++ b/e2e-test-app/src/main/java/com/sap/ai/sdk/app/Application.java @@ -15,6 +15,7 @@ class Application { * @param args Command line arguments. */ public static void main(final String[] args) { + SpringApplication.run(Application.class, args); } } From 7deb1a439ed8d6213d7c30c8cc665823c69fb55b Mon Sep 17 00:00:00 2001 From: Roshin Rajan Panackal Date: Mon, 2 Sep 2024 11:14:29 +0200 Subject: [PATCH 02/11] Add unit tests for GET API endpoints - Remove unneccesary isNotNull assertions - Rename test methods --- .../ai/sdk/core/client/ArtifactUnitTest.java | 60 ++++++++++--- .../core/client/ConfigurationUnitTest.java | 9 +- .../sdk/core/client/DeploymentUnitTest.java | 89 ++++++++++++++++--- .../ai/sdk/core/client/ExecutionUnitTest.java | 17 ++-- .../ai/sdk/core/client/ScenarioUnitTest.java | 46 +++++++++- 5 files changed, 185 insertions(+), 36 deletions(-) diff --git a/core/src/test/java/com/sap/ai/sdk/core/client/ArtifactUnitTest.java b/core/src/test/java/com/sap/ai/sdk/core/client/ArtifactUnitTest.java index e0a9f477d..c8b327346 100644 --- a/core/src/test/java/com/sap/ai/sdk/core/client/ArtifactUnitTest.java +++ b/core/src/test/java/com/sap/ai/sdk/core/client/ArtifactUnitTest.java @@ -21,7 +21,7 @@ */ public class ArtifactUnitTest extends WireMockTestServer { @Test - void testGetArtifact() { + void getArtifacts() { wireMockServer.stubFor( get(urlPathEqualTo("/lm/artifacts")) .withHeader("AI-Resource-Group", equalTo("default")) @@ -48,8 +48,9 @@ void testGetArtifact() { } """))); - final AiArtifactList artifactList = new ArtifactApi(getClient(destination)).artifactQuery("default"); - assertThat(artifactList).isNotNull(); + final AiArtifactList artifactList = + new ArtifactApi(getClient(destination)).artifactQuery("default"); + assertThat(artifactList.getCount()).isEqualTo(1); assertThat(artifactList.getResources().size()).isEqualTo(1); AiArtifact artifact = artifactList.getResources().get(0); @@ -64,7 +65,7 @@ void testGetArtifact() { } @Test - void testPostArtifact() { + void postArtifact() { wireMockServer.stubFor( post(urlPathEqualTo("/lm/artifacts")) .withHeader("AI-Resource-Group", equalTo("default")) @@ -82,17 +83,54 @@ void testPostArtifact() { """))); AiArtifactPostData artifactPostData = - AiArtifactPostData.create() - .name("default") - .kind(AiArtifactPostData.KindEnum.DATASET) - .url("ai://default/spam/data") - .scenarioId("foundation-models") - .description("dataset for aicore training"); + AiArtifactPostData.create() + .name("default") + .kind(AiArtifactPostData.KindEnum.DATASET) + .url("ai://default/spam/data") + .scenarioId("foundation-models") + .description("dataset for aicore training"); final AiArtifactCreationResponse artifact = new ArtifactApi(getClient(destination)).artifactCreate("default", artifactPostData); - assertThat(artifact).isNotNull(); + assertThat(artifact.getId()).isEqualTo("1a84bb38-4a84-4d12-a5aa-300ae7d33fb4"); assertThat(artifact.getMessage()).isEqualTo("AiArtifact acknowledged"); assertThat(artifact.getUrl()).isEqualTo("ai://default/spam/data"); } + + @Test + void getArtifactById() { + wireMockServer.stubFor( + get(urlPathEqualTo("/lm/artifacts/777dea85-e9b1-4a7b-9bea-14769b977633")) + .withHeader("AI-Resource-Group", equalTo("default")) + .willReturn( + aResponse() + .withHeader("content-type", "application/json") + .withBody( + """ + { + "createdAt": "2024-08-23T09:13:21Z", + "description": "", + "id": "777dea85-e9b1-4a7b-9bea-14769b977633", + "kind": "other", + "modifiedAt": "2024-08-23T09:13:21Z", + "name": "test", + "scenarioId": "orchestration", + "url": "https://file-examples.com/wp-content/storage/2017/10/file-sample_150kB.pdf" + } + """))); + + AiArtifact artifact = + new ArtifactApi(getClient(destination)) + .artifactGet("default", "777dea85-e9b1-4a7b-9bea-14769b977633"); + + assertThat(artifact.getCreatedAt()).isEqualTo("2024-08-23T09:13:21Z"); + assertThat(artifact.getDescription()).isEqualTo(""); + assertThat(artifact.getId()).isEqualTo("777dea85-e9b1-4a7b-9bea-14769b977633"); + assertThat(artifact.getKind()).isEqualTo(AiArtifact.KindEnum.OTHER); + assertThat(artifact.getModifiedAt()).isEqualTo("2024-08-23T09:13:21Z"); + assertThat(artifact.getName()).isEqualTo("test"); + assertThat(artifact.getScenarioId()).isEqualTo("orchestration"); + assertThat(artifact.getUrl()) + .isEqualTo("https://file-examples.com/wp-content/storage/2017/10/file-sample_150kB.pdf"); + } } diff --git a/core/src/test/java/com/sap/ai/sdk/core/client/ConfigurationUnitTest.java b/core/src/test/java/com/sap/ai/sdk/core/client/ConfigurationUnitTest.java index d74beaf55..d6aedf726 100644 --- a/core/src/test/java/com/sap/ai/sdk/core/client/ConfigurationUnitTest.java +++ b/core/src/test/java/com/sap/ai/sdk/core/client/ConfigurationUnitTest.java @@ -13,7 +13,6 @@ import com.sap.ai.sdk.core.client.model.AiConfigurationBaseData; import com.sap.ai.sdk.core.client.model.AiConfigurationCreationResponse; import com.sap.ai.sdk.core.client.model.AiConfigurationList; -import java.util.List; import org.apache.hc.core5.http.HttpStatus; import org.junit.jupiter.api.Test; @@ -23,7 +22,7 @@ */ public class ConfigurationUnitTest extends WireMockTestServer { @Test - void testGetConfigurations() { + void getConfigurations() { wireMockServer.stubFor( get(urlPathEqualTo("/lm/configurations")) .withHeader("AI-Resource-Group", equalTo("default")) @@ -60,7 +59,7 @@ void testGetConfigurations() { final AiConfigurationList configurationList = new ConfigurationApi(getClient(destination)).configurationQuery("default"); - assertThat(configurationList).isNotNull(); + assertThat(configurationList.getCount()).isEqualTo(1); assertThat(configurationList.getResources().size()).isEqualTo(1); AiConfiguration configuration = configurationList.getResources().get(0); @@ -76,7 +75,7 @@ void testGetConfigurations() { } @Test - void testPostConfiguration() { + void postConfiguration() { wireMockServer.stubFor( post(urlPathEqualTo("/lm/configurations")) .withHeader("AI-Resource-Group", equalTo("default")) @@ -103,7 +102,7 @@ void testPostConfiguration() { .artifactId("744b0136-ed4b-49b1-bd10-08c236ed5ce7")); final AiConfigurationCreationResponse configuration = new ConfigurationApi(getClient(destination)).configurationCreate("default", configurationBaseData); - assertThat(configuration).isNotNull(); + assertThat(configuration.getId()).isEqualTo("f88e7581-ade7-45c6-94e9-807889b523ec"); assertThat(configuration.getMessage()).isEqualTo("Configuration created"); } diff --git a/core/src/test/java/com/sap/ai/sdk/core/client/DeploymentUnitTest.java b/core/src/test/java/com/sap/ai/sdk/core/client/DeploymentUnitTest.java index 3d9308386..4a0d2530b 100644 --- a/core/src/test/java/com/sap/ai/sdk/core/client/DeploymentUnitTest.java +++ b/core/src/test/java/com/sap/ai/sdk/core/client/DeploymentUnitTest.java @@ -19,6 +19,7 @@ import com.sap.ai.sdk.core.client.model.AiDeploymentList; import com.sap.ai.sdk.core.client.model.AiDeploymentModificationRequest; import com.sap.ai.sdk.core.client.model.AiDeploymentModificationResponse; +import com.sap.ai.sdk.core.client.model.AiDeploymentResponseWithDetails; import com.sap.ai.sdk.core.client.model.AiDeploymentStatus; import com.sap.ai.sdk.core.client.model.AiDeploymentTargetStatus; import com.sap.ai.sdk.core.client.model.AiExecutionStatus; @@ -31,7 +32,7 @@ */ public class DeploymentUnitTest extends WireMockTestServer { @Test - void testGetDeployments() { + void getDeployments() { wireMockServer.stubFor( get(urlPathEqualTo("/lm/deployments")) .withHeader("AI-Resource-Group", equalTo("default")) @@ -78,7 +79,7 @@ void testGetDeployments() { final AiDeploymentList deploymentList = new DeploymentApi(getClient(destination)).deploymentQuery("default"); - assertThat(deploymentList).isNotNull(); + assertThat(deploymentList.getCount()).isEqualTo(1); assertThat(deploymentList.getResources().size()).isEqualTo(1); AiDeployment deployment = deploymentList.getResources().get(0); @@ -100,7 +101,7 @@ void testGetDeployments() { } @Test - void testPostAiDeployment() { + void postAiDeployment() { wireMockServer.stubFor( post(urlPathEqualTo("/lm/deployments")) .withHeader("AI-Resource-Group", equalTo("default")) @@ -119,18 +120,21 @@ void testPostAiDeployment() { """))); AiDeploymentCreationRequest deploymentCreationRequest = - AiDeploymentCreationRequest.create().configurationId("7652a231-ba9b-4fcc-b473-2c355cb21b61"); + AiDeploymentCreationRequest.create() + .configurationId("7652a231-ba9b-4fcc-b473-2c355cb21b61"); final AiDeploymentCreationResponse deployment = - new DeploymentApi(getClient(destination)).deploymentCreate("default", deploymentCreationRequest); - assertThat(deployment).isNotNull(); + new DeploymentApi(getClient(destination)) + .deploymentCreate("default", deploymentCreationRequest); assertThat(deployment.getDeploymentUrl()).isEqualTo(""); assertThat(deployment.getId()).isEqualTo("d5b764fe55b3e87c"); assertThat(deployment.getMessage()).isEqualTo("AiDeployment scheduled."); assertThat(deployment.getStatus()).isEqualTo(AiExecutionStatus.UNKNOWN); + + // TODO: Verify that the request body is correct } @Test - void testPatchAiDeployment() { + void patchAiDeployment() { wireMockServer.stubFor( patch(urlPathEqualTo("/lm/deployments/d19b998f347341aa")) .willReturn( @@ -144,13 +148,12 @@ void testPatchAiDeployment() { "message": "AiDeployment modification scheduled" } """))); - + AiDeploymentModificationRequest configModification = AiDeploymentModificationRequest.create().targetStatus(AiDeploymentTargetStatus.STOPPED); AiDeploymentModificationResponse deployment = new DeploymentApi(getClient(destination)) .deploymentModify("default", "d19b998f347341aa", configModification); - assertThat(deployment).isNotNull(); assertThat(deployment.getId()).isEqualTo("d5b764fe55b3e87c"); assertThat(deployment.getMessage()).isEqualTo("AiDeployment modification scheduled"); @@ -168,7 +171,7 @@ void testPatchAiDeployment() { } @Test - void testDeleteAiDeployment() { + void deleteAiDeployment() { wireMockServer.stubFor( delete(urlPathEqualTo("/lm/deployments/d5b764fe55b3e87c")) .withHeader("AI-Resource-Group", equalTo("default")) @@ -187,10 +190,74 @@ void testDeleteAiDeployment() { final AiDeploymentDeletionResponse deployment = new DeploymentApi(getClient(destination)).deploymentDelete("default", "d5b764fe55b3e87c"); - assertThat(deployment).isNotNull(); assertThat(deployment.getId()).isEqualTo("d5b764fe55b3e87c"); // targetStatus is not in the generated client, but we can still get it from the // cloudSdkCustomFields assertThat(deployment.getCustomField("targetStatus")).isEqualTo("DELETED"); } + + @Test + void getDeploymentById() { + wireMockServer.stubFor( + get(urlPathEqualTo("/lm/deployments/db1d64d9f06be467")) + .withHeader("AI-Resource-Group", equalTo("default")) + .willReturn( + aResponse() + .withStatus(HttpStatus.SC_OK) + .withHeader("content-type", "application/json") + .withBody( + """ + { + "configurationId": "dd80625e-ad86-426a-b1a7-1494c083428f", + "configurationName": "orchestration", + "createdAt": "2024-08-05T16:17:29Z", + "deploymentUrl": "https://api.ai.intprod-eu12.eu-central-1.aws.ml.hana.ondemand.com/v2/inference/deployments/db1d64d9f06be467", + "details": { + "resources": { + "backend_details": {} + }, + "scaling": { + "backend_details": {} + } + }, + "id": "db1d64d9f06be467", + "lastOperation": "CREATE", + "latestRunningConfigurationId": "dd80625e-ad86-426a-b1a7-1494c083428f", + "modifiedAt": "2024-08-26T12:43:18Z", + "scenarioId": "orchestration", + "startTime": "2024-08-05T16:18:41Z", + "status": "RUNNING", + "submissionTime": "2024-08-05T16:17:40Z", + "targetStatus": "RUNNING" + } + """))); + final AiDeploymentResponseWithDetails deployment = + new DeploymentApi(getClient(destination)).deploymentGet("default", "db1d64d9f06be467"); + + assertThat(deployment.getConfigurationId()).isEqualTo("dd80625e-ad86-426a-b1a7-1494c083428f"); + assertThat(deployment.getConfigurationName()).isEqualTo("orchestration"); + assertThat(deployment.getCreatedAt()).isEqualTo("2024-08-05T16:17:29Z"); + assertThat(deployment.getDeploymentUrl()) + .isEqualTo( + "https://api.ai.intprod-eu12.eu-central-1.aws.ml.hana.ondemand.com/v2/inference/deployments/db1d64d9f06be467"); + assertThat(deployment.getId()).isEqualTo("db1d64d9f06be467"); + assertThat(deployment.getLastOperation()).isEqualTo(AiDeploymentResponseWithDetails.LastOperationEnum.CREATE); + assertThat(deployment.getLatestRunningConfigurationId()) + .isEqualTo("dd80625e-ad86-426a-b1a7-1494c083428f"); + assertThat(deployment.getModifiedAt()).isEqualTo("2024-08-26T12:43:18Z"); + assertThat(deployment.getStartTime()).isEqualTo("2024-08-05T16:18:41Z"); + assertThat(deployment.getStatus()).isEqualTo(AiDeploymentStatus.RUNNING); + assertThat(deployment.getSubmissionTime()).isEqualTo("2024-08-05T16:17:40Z"); + assertThat(deployment.getTargetStatus()).isEqualTo(AiDeploymentResponseWithDetails.TargetStatusEnum.RUNNING); + } + + + @Test + void getLogsForDeploymentByStart() { + /* + * TODO: Deployment logs missing for /lm/deployments/{{deploymentid}}/logs. + * https://github.wdf.sap.corp/AI/ml-api-facade/issues/1235#top + * */ + } } + diff --git a/core/src/test/java/com/sap/ai/sdk/core/client/ExecutionUnitTest.java b/core/src/test/java/com/sap/ai/sdk/core/client/ExecutionUnitTest.java index f4deb9e8e..3f3451514 100644 --- a/core/src/test/java/com/sap/ai/sdk/core/client/ExecutionUnitTest.java +++ b/core/src/test/java/com/sap/ai/sdk/core/client/ExecutionUnitTest.java @@ -23,7 +23,7 @@ */ public class ExecutionUnitTest extends WireMockTestServer { @Test - void testGetExecutions() { + void getExecutions() { wireMockServer.stubFor( get(urlPathEqualTo("/lm/executions")) .withHeader("AI-Resource-Group", equalTo("default")) @@ -67,8 +67,8 @@ void testGetExecutions() { } """))); - final AiExecutionList executionList = new ExecutionApi(getClient(destination)).executionQuery("default"); - assertThat(executionList).isNotNull(); + final AiExecutionList executionList = + new ExecutionApi(getClient(destination)).executionQuery("default"); assertThat(executionList.getCount()).isEqualTo(1); assertThat(executionList.getResources().size()).isEqualTo(1); AiExecution execution = executionList.getResources().get(0); @@ -89,7 +89,7 @@ void testGetExecutions() { } @Test - void testPostExecution() { + void postExecution() { wireMockServer.stubFor( post(urlPathEqualTo("/lm/executions")) .withHeader("AI-Resource-Group", equalTo("default")) @@ -109,10 +109,15 @@ void testPostExecution() { AiEnactmentCreationRequest enactmentCreationRequest = AiEnactmentCreationRequest.create().configurationId("e0a9eb2e-9ea1-43bf-aff5-7660db166676"); final AiExecutionCreationResponse execution = - new ExecutionApi(getClient(destination)).executionCreate("default", enactmentCreationRequest); - assertThat(execution).isNotNull(); + new ExecutionApi(getClient(destination)) + .executionCreate("default", enactmentCreationRequest); + assertThat(execution.getId()).isEqualTo("eab289226fe981da"); assertThat(execution.getMessage()).isEqualTo("AiExecution acknowledged"); assertThat(execution.getCustomField("url")).isEqualTo("ai://default/eab289226fe981da"); } + + void getExecutionById() { + // TODO: No executions available in AI Core Test Instance -> Create them. + } } diff --git a/core/src/test/java/com/sap/ai/sdk/core/client/ScenarioUnitTest.java b/core/src/test/java/com/sap/ai/sdk/core/client/ScenarioUnitTest.java index 2e14b1a7f..b96efb3d2 100644 --- a/core/src/test/java/com/sap/ai/sdk/core/client/ScenarioUnitTest.java +++ b/core/src/test/java/com/sap/ai/sdk/core/client/ScenarioUnitTest.java @@ -9,6 +9,8 @@ import com.sap.ai.sdk.core.client.model.AiScenario; import com.sap.ai.sdk.core.client.model.AiScenarioList; +import com.sap.ai.sdk.core.client.model.AiVersion; +import com.sap.ai.sdk.core.client.model.AiVersionList; import org.apache.hc.core5.http.HttpStatus; import org.junit.jupiter.api.Test; @@ -18,7 +20,7 @@ */ public class ScenarioUnitTest extends WireMockTestServer { @Test - void testGetScenarios() { + void getScenarios() { wireMockServer.stubFor( get(urlPathEqualTo("/lm/scenarios")) .withHeader("AI-Resource-Group", equalTo("default")) @@ -49,8 +51,8 @@ void testGetScenarios() { """))); final AiScenarioList scenarioList = - new ScenarioApi(getClient(destination)).scenarioQuery("default"); - assertThat(scenarioList).isNotNull(); + new ScenarioApi(getClient(destination)).scenarioQuery("default"); + assertThat(scenarioList.getCount()).isEqualTo(1); assertThat(scenarioList.getResources().size()).isEqualTo(1); AiScenario scenario = scenarioList.getResources().get(0); @@ -60,4 +62,42 @@ void testGetScenarios() { assertThat(scenario.getLabels().get(0).getValue()).isEqualTo("true"); assertThat(scenario.getModifiedAt()).isEqualTo("2024-05-08T08:41:23+00:00"); } + + @Test + void getScenarioVersions() { + wireMockServer.stubFor( + get(urlPathEqualTo("/lm/scenarios/foundation-models/versions")) + .withHeader("AI-Resource-Group", equalTo("default")) + .willReturn( + aResponse() + .withHeader("content-type", "application/json") + .withStatus(HttpStatus.SC_OK) + .withBody( + """ + { + "count": 1, + "resources": [ + { + "createdAt": "2024-05-08T08:41:23+00:00", + "id": "0.0.1", + "modifiedAt": "2024-05-08T08:41:23+00:00", + "scenarioId": "foundation-models" + } + ] + } + """))); + + AiVersionList versionList = + new ScenarioApi(getClient(destination)) + .scenarioQueryVersions("default", "foundation-models"); + assertThat(versionList.getCount()).isEqualTo(1); + assertThat(versionList.getResources().size()).isEqualTo(1); + + AiVersion version = versionList.getResources().get(0); + assertThat(version.getCreatedAt()).isEqualTo("2024-05-08T08:41:23+00:00"); + assertThat(version.getId()).isEqualTo("0.0.1"); + assertThat(version.getModifiedAt()).isEqualTo("2024-05-08T08:41:23+00:00"); + assertThat(version.getScenarioId()).isEqualTo("foundation-models"); + } } + From 2d4ac0e930de0ee648b07c13aad5b90333f76f93 Mon Sep 17 00:00:00 2001 From: Roshin Rajan Panackal Date: Mon, 2 Sep 2024 14:53:28 +0200 Subject: [PATCH 03/11] Updated Formatting --- .../ai/sdk/core/client/ConfigurationUnitTest.java | 9 +++++---- .../sap/ai/sdk/core/client/DeploymentUnitTest.java | 12 +++++++----- .../sap/ai/sdk/core/client/ExecutionUnitTest.java | 6 ++++-- .../com/sap/ai/sdk/core/client/ScenarioUnitTest.java | 7 +++---- 4 files changed, 19 insertions(+), 15 deletions(-) diff --git a/core/src/test/java/com/sap/ai/sdk/core/client/ConfigurationUnitTest.java b/core/src/test/java/com/sap/ai/sdk/core/client/ConfigurationUnitTest.java index 5d3ab5fb0..f669f3708 100644 --- a/core/src/test/java/com/sap/ai/sdk/core/client/ConfigurationUnitTest.java +++ b/core/src/test/java/com/sap/ai/sdk/core/client/ConfigurationUnitTest.java @@ -97,11 +97,12 @@ void postConfiguration() { .executableId("aicore-nvidia") .scenarioId("foundation-models") .addInputArtifactBindingsItem( - AiArtifactArgumentBinding.create() - .key("spam-data") - .artifactId("744b0136-ed4b-49b1-bd10-08c236ed5ce7")); + AiArtifactArgumentBinding.create() + .key("spam-data") + .artifactId("744b0136-ed4b-49b1-bd10-08c236ed5ce7")); final AiConfigurationCreationResponse configuration = - new ConfigurationApi(getClient(destination)).configurationCreate("default", configurationBaseData); + new ConfigurationApi(getClient(destination)) + .configurationCreate("default", configurationBaseData); assertThat(configuration).isNotNull(); assertThat(configuration.getId()).isEqualTo("f88e7581-ade7-45c6-94e9-807889b523ec"); assertThat(configuration.getMessage()).isEqualTo("Configuration created"); diff --git a/core/src/test/java/com/sap/ai/sdk/core/client/DeploymentUnitTest.java b/core/src/test/java/com/sap/ai/sdk/core/client/DeploymentUnitTest.java index 4f424fd66..90f8f0e0f 100644 --- a/core/src/test/java/com/sap/ai/sdk/core/client/DeploymentUnitTest.java +++ b/core/src/test/java/com/sap/ai/sdk/core/client/DeploymentUnitTest.java @@ -123,13 +123,14 @@ void postAiDeployment() { AiDeploymentCreationRequest.create() .configurationId("7652a231-ba9b-4fcc-b473-2c355cb21b61"); final AiDeploymentCreationResponse deployment = - new DeploymentApi(getClient(destination)).deploymentCreate("default", deploymentCreationRequest); + new DeploymentApi(getClient(destination)) + .deploymentCreate("default", deploymentCreationRequest); assertThat(deployment).isNotNull(); assertThat(deployment.getDeploymentUrl()).isEqualTo(""); assertThat(deployment.getId()).isEqualTo("d5b764fe55b3e87c"); assertThat(deployment.getMessage()).isEqualTo("AiDeployment scheduled."); assertThat(deployment.getStatus()).isEqualTo(AiExecutionStatus.UNKNOWN); - + // TODO: Verify that the request body is correct } @@ -243,17 +244,18 @@ void getDeploymentById() { .isEqualTo( "https://api.ai.intprod-eu12.eu-central-1.aws.ml.hana.ondemand.com/v2/inference/deployments/db1d64d9f06be467"); assertThat(deployment.getId()).isEqualTo("db1d64d9f06be467"); - assertThat(deployment.getLastOperation()).isEqualTo(AiDeploymentResponseWithDetails.LastOperationEnum.CREATE); + assertThat(deployment.getLastOperation()) + .isEqualTo(AiDeploymentResponseWithDetails.LastOperationEnum.CREATE); assertThat(deployment.getLatestRunningConfigurationId()) .isEqualTo("dd80625e-ad86-426a-b1a7-1494c083428f"); assertThat(deployment.getModifiedAt()).isEqualTo("2024-08-26T12:43:18Z"); assertThat(deployment.getStartTime()).isEqualTo("2024-08-05T16:18:41Z"); assertThat(deployment.getStatus()).isEqualTo(AiDeploymentStatus.RUNNING); assertThat(deployment.getSubmissionTime()).isEqualTo("2024-08-05T16:17:40Z"); - assertThat(deployment.getTargetStatus()).isEqualTo(AiDeploymentResponseWithDetails.TargetStatusEnum.RUNNING); + assertThat(deployment.getTargetStatus()) + .isEqualTo(AiDeploymentResponseWithDetails.TargetStatusEnum.RUNNING); } - @Test void getLogsForDeploymentByStart() { /* diff --git a/core/src/test/java/com/sap/ai/sdk/core/client/ExecutionUnitTest.java b/core/src/test/java/com/sap/ai/sdk/core/client/ExecutionUnitTest.java index 3fa5fe5f7..0b7c82434 100644 --- a/core/src/test/java/com/sap/ai/sdk/core/client/ExecutionUnitTest.java +++ b/core/src/test/java/com/sap/ai/sdk/core/client/ExecutionUnitTest.java @@ -67,7 +67,8 @@ void getExecutions() { } """))); - final AiExecutionList executionList = new ExecutionApi(getClient(destination)).executionQuery("default"); + final AiExecutionList executionList = + new ExecutionApi(getClient(destination)).executionQuery("default"); assertThat(executionList).isNotNull(); assertThat(executionList.getCount()).isEqualTo(1); assertThat(executionList.getResources().size()).isEqualTo(1); @@ -109,7 +110,8 @@ void postExecution() { AiEnactmentCreationRequest enactmentCreationRequest = AiEnactmentCreationRequest.create().configurationId("e0a9eb2e-9ea1-43bf-aff5-7660db166676"); final AiExecutionCreationResponse execution = - new ExecutionApi(getClient(destination)).executionCreate("default", enactmentCreationRequest); + new ExecutionApi(getClient(destination)) + .executionCreate("default", enactmentCreationRequest); assertThat(execution).isNotNull(); assertThat(execution.getId()).isEqualTo("eab289226fe981da"); assertThat(execution.getMessage()).isEqualTo("AiExecution acknowledged"); diff --git a/core/src/test/java/com/sap/ai/sdk/core/client/ScenarioUnitTest.java b/core/src/test/java/com/sap/ai/sdk/core/client/ScenarioUnitTest.java index ea460c5f8..162cf33e9 100644 --- a/core/src/test/java/com/sap/ai/sdk/core/client/ScenarioUnitTest.java +++ b/core/src/test/java/com/sap/ai/sdk/core/client/ScenarioUnitTest.java @@ -51,7 +51,7 @@ void getScenarios() { """))); final AiScenarioList scenarioList = - new ScenarioApi(getClient(destination)).scenarioQuery("default"); + new ScenarioApi(getClient(destination)).scenarioQuery("default"); assertThat(scenarioList).isNotNull(); assertThat(scenarioList.getCount()).isEqualTo(1); assertThat(scenarioList.getResources().size()).isEqualTo(1); @@ -62,7 +62,7 @@ void getScenarios() { assertThat(scenario.getLabels().get(0).getValue()).isEqualTo("true"); assertThat(scenario.getModifiedAt()).isEqualTo("2024-05-08T08:41:23+00:00"); } - + @Test void getScenarioVersions() { wireMockServer.stubFor( @@ -92,7 +92,7 @@ void getScenarioVersions() { .scenarioQueryVersions("default", "foundation-models"); assertThat(versionList.getCount()).isEqualTo(1); assertThat(versionList.getResources().size()).isEqualTo(1); - + AiVersion version = versionList.getResources().get(0); assertThat(version.getCreatedAt()).isEqualTo("2024-05-08T08:41:23+00:00"); assertThat(version.getId()).isEqualTo("0.0.1"); @@ -100,4 +100,3 @@ void getScenarioVersions() { assertThat(version.getScenarioId()).isEqualTo("foundation-models"); } } - From 9129e8de59c80b1751a8b5ee0866774be9d8087f Mon Sep 17 00:00:00 2001 From: Roshin Rajan Panackal Date: Mon, 2 Sep 2024 15:32:36 +0200 Subject: [PATCH 04/11] Remove TODOs and Add verify on post deployment unit test --- .../sdk/core/client/DeploymentUnitTest.java | 20 ++++++++++--------- .../ai/sdk/core/client/ExecutionUnitTest.java | 5 ++--- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/core/src/test/java/com/sap/ai/sdk/core/client/DeploymentUnitTest.java b/core/src/test/java/com/sap/ai/sdk/core/client/DeploymentUnitTest.java index 90f8f0e0f..d6760776f 100644 --- a/core/src/test/java/com/sap/ai/sdk/core/client/DeploymentUnitTest.java +++ b/core/src/test/java/com/sap/ai/sdk/core/client/DeploymentUnitTest.java @@ -8,6 +8,7 @@ import static com.github.tomakehurst.wiremock.client.WireMock.patch; import static com.github.tomakehurst.wiremock.client.WireMock.patchRequestedFor; import static com.github.tomakehurst.wiremock.client.WireMock.post; +import static com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor; import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; import static com.sap.ai.sdk.core.Core.getClient; import static org.assertj.core.api.Assertions.assertThat; @@ -131,7 +132,16 @@ void postAiDeployment() { assertThat(deployment.getMessage()).isEqualTo("AiDeployment scheduled."); assertThat(deployment.getStatus()).isEqualTo(AiExecutionStatus.UNKNOWN); - // TODO: Verify that the request body is correct + wireMockServer.verify( + postRequestedFor(urlPathEqualTo("/lm/deployments")) + .withHeader("AI-Resource-Group", equalTo("default")) + .withRequestBody( + equalToJson( + """ + { + "configurationId" : "7652a231-ba9b-4fcc-b473-2c355cb21b61" + } + """))); } @Test @@ -255,12 +265,4 @@ void getDeploymentById() { assertThat(deployment.getTargetStatus()) .isEqualTo(AiDeploymentResponseWithDetails.TargetStatusEnum.RUNNING); } - - @Test - void getLogsForDeploymentByStart() { - /* - * TODO: Deployment logs missing for /lm/deployments/{{deploymentid}}/logs. - * https://github.wdf.sap.corp/AI/ml-api-facade/issues/1235#top - * */ - } } diff --git a/core/src/test/java/com/sap/ai/sdk/core/client/ExecutionUnitTest.java b/core/src/test/java/com/sap/ai/sdk/core/client/ExecutionUnitTest.java index 0b7c82434..9c3a9fb56 100644 --- a/core/src/test/java/com/sap/ai/sdk/core/client/ExecutionUnitTest.java +++ b/core/src/test/java/com/sap/ai/sdk/core/client/ExecutionUnitTest.java @@ -118,7 +118,6 @@ void postExecution() { assertThat(execution.getCustomField("url")).isEqualTo("ai://default/eab289226fe981da"); } - void getExecutionById() { - // TODO: No executions available in AI Core Test Instance -> Create them. - } + @Test + void getExecutionById() {} } From decd7f82ec149e268e5acd718b6c105575a79bdd Mon Sep 17 00:00:00 2001 From: Roshin Rajan Panackal Date: Mon, 2 Sep 2024 17:24:33 +0200 Subject: [PATCH 05/11] Consistent null assertions and Add missing assertions --- .../sap/ai/sdk/core/client/ArtifactUnitTest.java | 3 +++ .../ai/sdk/core/client/ConfigurationUnitTest.java | 1 + .../sap/ai/sdk/core/client/DeploymentUnitTest.java | 14 ++++++++++++++ .../sap/ai/sdk/core/client/ExecutionUnitTest.java | 3 --- .../sap/ai/sdk/core/client/ScenarioUnitTest.java | 1 + 5 files changed, 19 insertions(+), 3 deletions(-) diff --git a/core/src/test/java/com/sap/ai/sdk/core/client/ArtifactUnitTest.java b/core/src/test/java/com/sap/ai/sdk/core/client/ArtifactUnitTest.java index c8b327346..6b59e253d 100644 --- a/core/src/test/java/com/sap/ai/sdk/core/client/ArtifactUnitTest.java +++ b/core/src/test/java/com/sap/ai/sdk/core/client/ArtifactUnitTest.java @@ -51,6 +51,7 @@ void getArtifacts() { final AiArtifactList artifactList = new ArtifactApi(getClient(destination)).artifactQuery("default"); + assertThat(artifactList).isNotNull(); assertThat(artifactList.getCount()).isEqualTo(1); assertThat(artifactList.getResources().size()).isEqualTo(1); AiArtifact artifact = artifactList.getResources().get(0); @@ -92,6 +93,7 @@ void postArtifact() { final AiArtifactCreationResponse artifact = new ArtifactApi(getClient(destination)).artifactCreate("default", artifactPostData); + assertThat(artifact).isNotNull(); assertThat(artifact.getId()).isEqualTo("1a84bb38-4a84-4d12-a5aa-300ae7d33fb4"); assertThat(artifact.getMessage()).isEqualTo("AiArtifact acknowledged"); assertThat(artifact.getUrl()).isEqualTo("ai://default/spam/data"); @@ -123,6 +125,7 @@ void getArtifactById() { new ArtifactApi(getClient(destination)) .artifactGet("default", "777dea85-e9b1-4a7b-9bea-14769b977633"); + assertThat(artifact).isNotNull(); assertThat(artifact.getCreatedAt()).isEqualTo("2024-08-23T09:13:21Z"); assertThat(artifact.getDescription()).isEqualTo(""); assertThat(artifact.getId()).isEqualTo("777dea85-e9b1-4a7b-9bea-14769b977633"); diff --git a/core/src/test/java/com/sap/ai/sdk/core/client/ConfigurationUnitTest.java b/core/src/test/java/com/sap/ai/sdk/core/client/ConfigurationUnitTest.java index f669f3708..10cef9dcb 100644 --- a/core/src/test/java/com/sap/ai/sdk/core/client/ConfigurationUnitTest.java +++ b/core/src/test/java/com/sap/ai/sdk/core/client/ConfigurationUnitTest.java @@ -66,6 +66,7 @@ void getConfigurations() { assertThat(configuration.getCreatedAt()).isEqualTo("2024-04-17T15:19:45Z"); assertThat(configuration.getExecutableId()).isEqualTo("azure-openai"); assertThat(configuration.getId()).isEqualTo("7652a231-ba9b-4fcc-b473-2c355cb21b61"); + assertThat(configuration.getInputArtifactBindings().size()).isEqualTo(0); assertThat(configuration.getName()).isEqualTo("gpt-4-32k"); assertThat(configuration.getParameterBindings().get(0).getKey()).isEqualTo("modelName"); assertThat(configuration.getParameterBindings().get(0).getValue()).isEqualTo("gpt-4-32k"); diff --git a/core/src/test/java/com/sap/ai/sdk/core/client/DeploymentUnitTest.java b/core/src/test/java/com/sap/ai/sdk/core/client/DeploymentUnitTest.java index d6760776f..8a73d9999 100644 --- a/core/src/test/java/com/sap/ai/sdk/core/client/DeploymentUnitTest.java +++ b/core/src/test/java/com/sap/ai/sdk/core/client/DeploymentUnitTest.java @@ -247,12 +247,26 @@ void getDeploymentById() { final AiDeploymentResponseWithDetails deployment = new DeploymentApi(getClient(destination)).deploymentGet("default", "db1d64d9f06be467"); + assertThat(deployment).isNotNull(); assertThat(deployment.getConfigurationId()).isEqualTo("dd80625e-ad86-426a-b1a7-1494c083428f"); assertThat(deployment.getConfigurationName()).isEqualTo("orchestration"); assertThat(deployment.getCreatedAt()).isEqualTo("2024-08-05T16:17:29Z"); assertThat(deployment.getDeploymentUrl()) .isEqualTo( "https://api.ai.intprod-eu12.eu-central-1.aws.ml.hana.ondemand.com/v2/inference/deployments/db1d64d9f06be467"); + + // backend_details key not identified by deserializer and dumped into customFields + assertThat( + deployment + .getDetails() + .getResources() + .getCustomFieldNames() + .contains("backend_details")) + .isTrue(); + assertThat( + deployment.getDetails().getScaling().getCustomFieldNames().contains("backend_details")) + .isTrue(); + assertThat(deployment.getId()).isEqualTo("db1d64d9f06be467"); assertThat(deployment.getLastOperation()) .isEqualTo(AiDeploymentResponseWithDetails.LastOperationEnum.CREATE); diff --git a/core/src/test/java/com/sap/ai/sdk/core/client/ExecutionUnitTest.java b/core/src/test/java/com/sap/ai/sdk/core/client/ExecutionUnitTest.java index 9c3a9fb56..40db22cda 100644 --- a/core/src/test/java/com/sap/ai/sdk/core/client/ExecutionUnitTest.java +++ b/core/src/test/java/com/sap/ai/sdk/core/client/ExecutionUnitTest.java @@ -117,7 +117,4 @@ void postExecution() { assertThat(execution.getMessage()).isEqualTo("AiExecution acknowledged"); assertThat(execution.getCustomField("url")).isEqualTo("ai://default/eab289226fe981da"); } - - @Test - void getExecutionById() {} } diff --git a/core/src/test/java/com/sap/ai/sdk/core/client/ScenarioUnitTest.java b/core/src/test/java/com/sap/ai/sdk/core/client/ScenarioUnitTest.java index 162cf33e9..c2d955ba5 100644 --- a/core/src/test/java/com/sap/ai/sdk/core/client/ScenarioUnitTest.java +++ b/core/src/test/java/com/sap/ai/sdk/core/client/ScenarioUnitTest.java @@ -90,6 +90,7 @@ void getScenarioVersions() { AiVersionList versionList = new ScenarioApi(getClient(destination)) .scenarioQueryVersions("default", "foundation-models"); + assertThat(versionList).isNotNull(); assertThat(versionList.getCount()).isEqualTo(1); assertThat(versionList.getResources().size()).isEqualTo(1); From 6643c425525309f0f08823741842fb73479e1b22 Mon Sep 17 00:00:00 2001 From: Charles Dubois <103174266+CharlesDuboisSAP@users.noreply.github.com> Date: Wed, 11 Sep 2024 11:22:11 +0200 Subject: [PATCH 06/11] Update e2e-test-app/src/main/java/com/sap/ai/sdk/app/Application.java --- e2e-test-app/src/main/java/com/sap/ai/sdk/app/Application.java | 1 - 1 file changed, 1 deletion(-) diff --git a/e2e-test-app/src/main/java/com/sap/ai/sdk/app/Application.java b/e2e-test-app/src/main/java/com/sap/ai/sdk/app/Application.java index 8a0bed18a..5378b970d 100644 --- a/e2e-test-app/src/main/java/com/sap/ai/sdk/app/Application.java +++ b/e2e-test-app/src/main/java/com/sap/ai/sdk/app/Application.java @@ -15,7 +15,6 @@ class Application { * @param args Command line arguments. */ public static void main(final String[] args) { - SpringApplication.run(Application.class, args); } } From 63fc6a0c43dd32d0dcaf002c410fd66fea6bf284 Mon Sep 17 00:00:00 2001 From: Roshin Rajan Panackal Date: Wed, 11 Sep 2024 13:56:16 +0200 Subject: [PATCH 07/11] Add unit test for methods that modify state --- .../ai/sdk/core/client/ArtifactUnitTest.java | 29 ++- .../core/client/ConfigurationUnitTest.java | 37 +++- .../sdk/core/client/DeploymentUnitTest.java | 80 ++++++-- .../ai/sdk/core/client/ExecutionUnitTest.java | 191 +++++++++++++++++- .../ai/sdk/core/client/ScenarioUnitTest.java | 11 +- .../sdk/core/client/WireMockTestServer.java | 7 + 6 files changed, 319 insertions(+), 36 deletions(-) diff --git a/core/src/test/java/com/sap/ai/sdk/core/client/ArtifactUnitTest.java b/core/src/test/java/com/sap/ai/sdk/core/client/ArtifactUnitTest.java index 6b59e253d..d0e8d569b 100644 --- a/core/src/test/java/com/sap/ai/sdk/core/client/ArtifactUnitTest.java +++ b/core/src/test/java/com/sap/ai/sdk/core/client/ArtifactUnitTest.java @@ -2,8 +2,10 @@ import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; +import static com.github.tomakehurst.wiremock.client.WireMock.equalToJson; import static com.github.tomakehurst.wiremock.client.WireMock.get; import static com.github.tomakehurst.wiremock.client.WireMock.post; +import static com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor; import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; import static com.sap.ai.sdk.core.Core.getClient; import static org.assertj.core.api.Assertions.assertThat; @@ -54,7 +56,9 @@ void getArtifacts() { assertThat(artifactList).isNotNull(); assertThat(artifactList.getCount()).isEqualTo(1); assertThat(artifactList.getResources().size()).isEqualTo(1); - AiArtifact artifact = artifactList.getResources().get(0); + + final AiArtifact artifact = artifactList.getResources().get(0); + assertThat(artifact.getCreatedAt()).isEqualTo("2024-05-22T07:40:30Z"); assertThat(artifact.getDescription()).isEqualTo("dataset for aicore training"); assertThat(artifact.getId()).isEqualTo("744b0136-ed4b-49b1-bd10-08c236ed5ce7"); @@ -72,7 +76,7 @@ void postArtifact() { .withHeader("AI-Resource-Group", equalTo("default")) .willReturn( aResponse() - .withStatus(HttpStatus.SC_OK) + .withStatus(HttpStatus.SC_CREATED) .withHeader("content-type", "application/json") .withBody( """ @@ -83,12 +87,13 @@ void postArtifact() { } """))); - AiArtifactPostData artifactPostData = + final AiArtifactPostData artifactPostData = AiArtifactPostData.create() .name("default") .kind(AiArtifactPostData.KindEnum.DATASET) .url("ai://default/spam/data") .scenarioId("foundation-models") + .scenarioId("foundation-models") .description("dataset for aicore training"); final AiArtifactCreationResponse artifact = new ArtifactApi(getClient(destination)).artifactCreate("default", artifactPostData); @@ -97,6 +102,22 @@ void postArtifact() { assertThat(artifact.getId()).isEqualTo("1a84bb38-4a84-4d12-a5aa-300ae7d33fb4"); assertThat(artifact.getMessage()).isEqualTo("AiArtifact acknowledged"); assertThat(artifact.getUrl()).isEqualTo("ai://default/spam/data"); + + wireMockServer.verify( + postRequestedFor(urlPathEqualTo("/lm/artifacts")) + .withHeader("AI-Resource-Group", equalTo("default")) + .withRequestBody( + equalToJson( + """ + { + "name": "default", + "kind": "dataset", + "url": "ai://default/spam/data", + "scenarioId": "foundation-models", + "description": "dataset for aicore training", + "labels": [] + } + """))); } @Test @@ -121,7 +142,7 @@ void getArtifactById() { } """))); - AiArtifact artifact = + final AiArtifact artifact = new ArtifactApi(getClient(destination)) .artifactGet("default", "777dea85-e9b1-4a7b-9bea-14769b977633"); diff --git a/core/src/test/java/com/sap/ai/sdk/core/client/ConfigurationUnitTest.java b/core/src/test/java/com/sap/ai/sdk/core/client/ConfigurationUnitTest.java index 10cef9dcb..4bf54abb8 100644 --- a/core/src/test/java/com/sap/ai/sdk/core/client/ConfigurationUnitTest.java +++ b/core/src/test/java/com/sap/ai/sdk/core/client/ConfigurationUnitTest.java @@ -2,8 +2,10 @@ import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; +import static com.github.tomakehurst.wiremock.client.WireMock.equalToJson; import static com.github.tomakehurst.wiremock.client.WireMock.get; import static com.github.tomakehurst.wiremock.client.WireMock.post; +import static com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor; import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; import static com.sap.ai.sdk.core.Core.getClient; import static org.assertj.core.api.Assertions.assertThat; @@ -59,6 +61,7 @@ void getConfigurations() { final AiConfigurationList configurationList = new ConfigurationApi(getClient(destination)).configurationQuery("default"); + assertThat(configurationList).isNotNull(); assertThat(configurationList.getCount()).isEqualTo(1); assertThat(configurationList.getResources().size()).isEqualTo(1); @@ -82,7 +85,7 @@ void postConfiguration() { .withHeader("AI-Resource-Group", equalTo("default")) .willReturn( aResponse() - .withStatus(HttpStatus.SC_OK) + .withStatus(HttpStatus.SC_CREATED) .withHeader("content-type", "application/json") .withBody( """ @@ -92,20 +95,42 @@ void postConfiguration() { } """))); - AiConfigurationBaseData configurationBaseData = + final AiArtifactArgumentBinding inputArtifactBindingsItem = + AiArtifactArgumentBinding.create() + .key("spam-data") + .artifactId("744b0136-ed4b-49b1-bd10-08c236ed5ce7"); + final AiConfigurationBaseData configurationBaseData = AiConfigurationBaseData.create() .name("i538344_exec_config") .executableId("aicore-nvidia") .scenarioId("foundation-models") - .addInputArtifactBindingsItem( - AiArtifactArgumentBinding.create() - .key("spam-data") - .artifactId("744b0136-ed4b-49b1-bd10-08c236ed5ce7")); + .addInputArtifactBindingsItem(inputArtifactBindingsItem); final AiConfigurationCreationResponse configuration = new ConfigurationApi(getClient(destination)) .configurationCreate("default", configurationBaseData); + assertThat(configuration).isNotNull(); assertThat(configuration.getId()).isEqualTo("f88e7581-ade7-45c6-94e9-807889b523ec"); assertThat(configuration.getMessage()).isEqualTo("Configuration created"); + + wireMockServer.verify( + postRequestedFor(urlPathEqualTo("/lm/configurations")) + .withHeader("AI-Resource-Group", equalTo("default")) + .withRequestBody( + equalToJson( + """ + { + "name": "i538344_exec_config", + "executableId": "aicore-nvidia", + "scenarioId": "foundation-models", + "parameterBindings":[], + "inputArtifactBindings": [ + { + "key": "spam-data", + "artifactId": "744b0136-ed4b-49b1-bd10-08c236ed5ce7" + } + ] + } + """))); } } diff --git a/core/src/test/java/com/sap/ai/sdk/core/client/DeploymentUnitTest.java b/core/src/test/java/com/sap/ai/sdk/core/client/DeploymentUnitTest.java index 8a73d9999..71682c3f2 100644 --- a/core/src/test/java/com/sap/ai/sdk/core/client/DeploymentUnitTest.java +++ b/core/src/test/java/com/sap/ai/sdk/core/client/DeploymentUnitTest.java @@ -24,6 +24,7 @@ import com.sap.ai.sdk.core.client.model.AiDeploymentStatus; import com.sap.ai.sdk.core.client.model.AiDeploymentTargetStatus; import com.sap.ai.sdk.core.client.model.AiExecutionStatus; +import java.util.Map; import org.apache.hc.core5.http.HttpStatus; import org.junit.jupiter.api.Test; @@ -80,16 +81,26 @@ void getDeployments() { final AiDeploymentList deploymentList = new DeploymentApi(getClient(destination)).deploymentQuery("default"); + assertThat(deploymentList).isNotNull(); assertThat(deploymentList.getCount()).isEqualTo(1); assertThat(deploymentList.getResources().size()).isEqualTo(1); - AiDeployment deployment = deploymentList.getResources().get(0); + + final AiDeployment deployment = deploymentList.getResources().get(0); + assertThat(deployment.getConfigurationId()).isEqualTo("7652a231-ba9b-4fcc-b473-2c355cb21b61"); assertThat(deployment.getConfigurationName()).isEqualTo("gpt-4-32k"); assertThat(deployment.getCreatedAt()).isEqualTo("2024-04-17T15:19:53Z"); assertThat(deployment.getDeploymentUrl()) .isEqualTo( "https://api.ai.intprod-eu12.eu-central-1.aws.ml.hana.ondemand.com/v2/inference/deployments/d19b998f347341aa"); + // Response contains key "backend_details" while spec (mistakenly) defines key "backendDetails". + final var expected = Map.of("model", Map.of("name", "gpt-4-32k", "version", "latest")); + assertThat(deployment.getDetails().getResources().getCustomField("backend_details")) + .isEqualTo(expected); + assertThat( + deployment.getDetails().getScaling().getCustomFieldNames().contains("backend_details")) + .isTrue(); assertThat(deployment.getId()).isEqualTo("d19b998f347341aa"); assertThat(deployment.getLastOperation()).isEqualTo(AiDeployment.LastOperationEnum.CREATE); assertThat(deployment.getLatestRunningConfigurationId()) @@ -102,7 +113,7 @@ void getDeployments() { } @Test - void postAiDeployment() { + void postDeployment() { wireMockServer.stubFor( post(urlPathEqualTo("/lm/deployments")) .withHeader("AI-Resource-Group", equalTo("default")) @@ -120,12 +131,13 @@ void postAiDeployment() { } """))); - AiDeploymentCreationRequest deploymentCreationRequest = + final AiDeploymentCreationRequest deploymentCreationRequest = AiDeploymentCreationRequest.create() .configurationId("7652a231-ba9b-4fcc-b473-2c355cb21b61"); final AiDeploymentCreationResponse deployment = new DeploymentApi(getClient(destination)) .deploymentCreate("default", deploymentCreationRequest); + assertThat(deployment).isNotNull(); assertThat(deployment.getDeploymentUrl()).isEqualTo(""); assertThat(deployment.getId()).isEqualTo("d5b764fe55b3e87c"); @@ -145,12 +157,12 @@ void postAiDeployment() { } @Test - void patchAiDeployment() { + void patchDeploymentStatus() { wireMockServer.stubFor( patch(urlPathEqualTo("/lm/deployments/d19b998f347341aa")) .willReturn( aResponse() - .withStatus(HttpStatus.SC_OK) + .withStatus(HttpStatus.SC_ACCEPTED) .withHeader("content-type", "application/json") .withBody( """ @@ -160,11 +172,12 @@ void patchAiDeployment() { } """))); - AiDeploymentModificationRequest configModification = + final AiDeploymentModificationRequest configModification = AiDeploymentModificationRequest.create().targetStatus(AiDeploymentTargetStatus.STOPPED); - AiDeploymentModificationResponse deployment = + final AiDeploymentModificationResponse deployment = new DeploymentApi(getClient(destination)) .deploymentModify("default", "d19b998f347341aa", configModification); + assertThat(deployment).isNotNull(); assertThat(deployment.getId()).isEqualTo("d5b764fe55b3e87c"); assertThat(deployment.getMessage()).isEqualTo("AiDeployment modification scheduled"); @@ -183,13 +196,13 @@ void patchAiDeployment() { } @Test - void deleteAiDeployment() { + void deleteDeployment() { wireMockServer.stubFor( delete(urlPathEqualTo("/lm/deployments/d5b764fe55b3e87c")) .withHeader("AI-Resource-Group", equalTo("default")) .willReturn( aResponse() - .withStatus(HttpStatus.SC_OK) + .withStatus(HttpStatus.SC_ACCEPTED) .withHeader("content-type", "application/json") .withBody( """ @@ -202,10 +215,10 @@ void deleteAiDeployment() { final AiDeploymentDeletionResponse deployment = new DeploymentApi(getClient(destination)).deploymentDelete("default", "d5b764fe55b3e87c"); + assertThat(deployment).isNotNull(); assertThat(deployment.getId()).isEqualTo("d5b764fe55b3e87c"); - // targetStatus is not in the generated client, but we can still get it from the - // cloudSdkCustomFields + // targetStatus is not in the generated client assertThat(deployment.getCustomField("targetStatus")).isEqualTo("DELETED"); } @@ -244,6 +257,7 @@ void getDeploymentById() { "targetStatus": "RUNNING" } """))); + final AiDeploymentResponseWithDetails deployment = new DeploymentApi(getClient(destination)).deploymentGet("default", "db1d64d9f06be467"); @@ -254,8 +268,7 @@ void getDeploymentById() { assertThat(deployment.getDeploymentUrl()) .isEqualTo( "https://api.ai.intprod-eu12.eu-central-1.aws.ml.hana.ondemand.com/v2/inference/deployments/db1d64d9f06be467"); - - // backend_details key not identified by deserializer and dumped into customFields + // Response contains key "backend_details" while spec (mistakenly) defines key "backendDetails". assertThat( deployment .getDetails() @@ -266,7 +279,6 @@ void getDeploymentById() { assertThat( deployment.getDetails().getScaling().getCustomFieldNames().contains("backend_details")) .isTrue(); - assertThat(deployment.getId()).isEqualTo("db1d64d9f06be467"); assertThat(deployment.getLastOperation()) .isEqualTo(AiDeploymentResponseWithDetails.LastOperationEnum.CREATE); @@ -279,4 +291,44 @@ void getDeploymentById() { assertThat(deployment.getTargetStatus()) .isEqualTo(AiDeploymentResponseWithDetails.TargetStatusEnum.RUNNING); } + + @Test + void patchDeploymentConfiguration() { + wireMockServer.stubFor( + patch(urlPathEqualTo("/lm/deployments/d03050a2ab7055cc")) + .willReturn( + aResponse() + .withStatus(HttpStatus.SC_ACCEPTED) + .withHeader("content-type", "application/json") + .withBody( + """ + { + "id": "d03050a2ab7055cc", + "message": "Deployment modification scheduled" + } + """))); + + final AiDeploymentModificationRequest configModification = + AiDeploymentModificationRequest.create() + .configurationId("6ff6cb80-87db-45f0-b718-4e1d96e66332"); + final AiDeploymentModificationResponse deployment = + new DeploymentApi(getClient(destination)) + .deploymentModify("default", "d03050a2ab7055cc", configModification); + + assertThat(deployment).isNotNull(); + assertThat(deployment.getId()).isEqualTo("d03050a2ab7055cc"); + assertThat(deployment.getMessage()).isEqualTo("Deployment modification scheduled"); + + // verify that null fields are absent from the sent request + wireMockServer.verify( + patchRequestedFor(urlPathEqualTo("/lm/deployments/d03050a2ab7055cc")) + .withHeader("AI-Resource-Group", equalTo("default")) + .withRequestBody( + equalToJson( + """ + { + "configurationId": "6ff6cb80-87db-45f0-b718-4e1d96e66332" + } + """))); + } } diff --git a/core/src/test/java/com/sap/ai/sdk/core/client/ExecutionUnitTest.java b/core/src/test/java/com/sap/ai/sdk/core/client/ExecutionUnitTest.java index 40db22cda..6fdc89226 100644 --- a/core/src/test/java/com/sap/ai/sdk/core/client/ExecutionUnitTest.java +++ b/core/src/test/java/com/sap/ai/sdk/core/client/ExecutionUnitTest.java @@ -1,9 +1,14 @@ package com.sap.ai.sdk.core.client; import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.delete; import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; +import static com.github.tomakehurst.wiremock.client.WireMock.equalToJson; import static com.github.tomakehurst.wiremock.client.WireMock.get; +import static com.github.tomakehurst.wiremock.client.WireMock.patch; +import static com.github.tomakehurst.wiremock.client.WireMock.patchRequestedFor; import static com.github.tomakehurst.wiremock.client.WireMock.post; +import static com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor; import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; import static com.sap.ai.sdk.core.Core.getClient; import static org.assertj.core.api.Assertions.assertThat; @@ -12,7 +17,11 @@ import com.sap.ai.sdk.core.client.model.AiEnactmentCreationRequest; import com.sap.ai.sdk.core.client.model.AiExecution; import com.sap.ai.sdk.core.client.model.AiExecutionCreationResponse; +import com.sap.ai.sdk.core.client.model.AiExecutionDeletionResponse; import com.sap.ai.sdk.core.client.model.AiExecutionList; +import com.sap.ai.sdk.core.client.model.AiExecutionModificationRequest; +import com.sap.ai.sdk.core.client.model.AiExecutionModificationResponse; +import com.sap.ai.sdk.core.client.model.AiExecutionResponseWithDetails; import com.sap.ai.sdk.core.client.model.AiExecutionStatus; import org.apache.hc.core5.http.HttpStatus; import org.junit.jupiter.api.Test; @@ -69,10 +78,13 @@ void getExecutions() { final AiExecutionList executionList = new ExecutionApi(getClient(destination)).executionQuery("default"); + assertThat(executionList).isNotNull(); assertThat(executionList.getCount()).isEqualTo(1); assertThat(executionList.getResources().size()).isEqualTo(1); - AiExecution execution = executionList.getResources().get(0); + + final AiExecution execution = executionList.getResources().get(0); + assertThat(execution.getCompletionTime()).isEqualTo("2023-08-05T14:10:16Z"); assertThat(execution.getConfigurationId()).isEqualTo("e0a9eb2e-9ea1-43bf-aff5-7660db166676"); assertThat(execution.getConfigurationName()).isEqualTo("spam-detection-execution-config-0"); @@ -80,13 +92,25 @@ void getExecutions() { // executableId is not in the generated client, but we can still get it from the // cloudSdkCustomFields assertThat(execution.getCustomField("executableId")).isEqualTo("wt-spam-detection-i343697"); - assertThat(execution.getOutputArtifacts().get(0).getId()) - .isEqualTo("be0d728f-1cb2-4ff4-97ad-45c54ac592f6"); - assertThat(execution.getOutputArtifacts().get(0).getKind()) - .isEqualTo(AiArtifact.KindEnum.MODEL); - assertThat(execution.getOutputArtifacts().get(0).getUrl()) - .isEqualTo("ai://default/eab289226fe981da/classifier-model-output"); + assertThat(execution.getScenarioId()).isEqualTo("scenario-spam-detection-i343697"); + assertThat(execution.getStartTime()).isEqualTo("2023-08-05T14:09:21Z"); assertThat(execution.getStatus()).isEqualTo(AiExecutionStatus.COMPLETED); + assertThat(execution.getSubmissionTime()).isEqualTo("2023-08-05T14:09:21Z"); + assertThat(execution.getTargetStatus()).isEqualTo(AiExecution.TargetStatusEnum.COMPLETED); + assertThat(execution.getOutputArtifacts().size()).isEqualTo(1); + + final AiArtifact aiArtifact = execution.getOutputArtifacts().get(0); + + assertThat(aiArtifact.getCreatedAt()).isEqualTo("2023-08-05T14:10:05Z"); + assertThat(aiArtifact.getDescription()).isEqualTo(""); + assertThat(aiArtifact.getExecutionId()).isEqualTo("eab289226fe981da"); + assertThat(aiArtifact.getId()).isEqualTo("be0d728f-1cb2-4ff4-97ad-45c54ac592f6"); + assertThat(aiArtifact.getKind()).isEqualTo(AiArtifact.KindEnum.MODEL); + assertThat(aiArtifact.getModifiedAt()).isEqualTo("2023-08-05T14:10:05Z"); + assertThat(aiArtifact.getName()).isEqualTo("classifier-model-output"); + assertThat(aiArtifact.getScenarioId()).isEqualTo("scenario-spam-detection-i343697"); + assertThat(aiArtifact.getUrl()) + .isEqualTo("ai://default/eab289226fe981da/classifier-model-output"); } @Test @@ -96,7 +120,7 @@ void postExecution() { .withHeader("AI-Resource-Group", equalTo("default")) .willReturn( aResponse() - .withStatus(HttpStatus.SC_OK) + .withStatus(HttpStatus.SC_CREATED) .withHeader("content-type", "application/json") .withBody( """ @@ -107,14 +131,163 @@ void postExecution() { } """))); - AiEnactmentCreationRequest enactmentCreationRequest = + final AiEnactmentCreationRequest enactmentCreationRequest = AiEnactmentCreationRequest.create().configurationId("e0a9eb2e-9ea1-43bf-aff5-7660db166676"); final AiExecutionCreationResponse execution = new ExecutionApi(getClient(destination)) .executionCreate("default", enactmentCreationRequest); + assertThat(execution).isNotNull(); assertThat(execution.getId()).isEqualTo("eab289226fe981da"); assertThat(execution.getMessage()).isEqualTo("AiExecution acknowledged"); assertThat(execution.getCustomField("url")).isEqualTo("ai://default/eab289226fe981da"); + + wireMockServer.verify( + postRequestedFor(urlPathEqualTo("/lm/executions")) + .withHeader("AI-Resource-Group", equalTo("default")) + .withRequestBody( + equalToJson( + """ + { + "configurationId": "e0a9eb2e-9ea1-43bf-aff5-7660db166676" + } + """))); + } + + @Test + void getExecutionById() { + wireMockServer.stubFor( + get(urlPathEqualTo("/lm/executions/e529e8bd58740bc9")) + .withHeader("AI-Resource-Group", equalTo("default")) + .willReturn( + aResponse() + .withStatus(HttpStatus.SC_OK) + .withHeader("content-type", "application/json") + .withBody( + """ + { + "completionTime": "2024-09-09T19:10:58Z", + "configurationId": "218b651d-113d-4da0-be15-b63a644a92fb", + "configurationName": "i749902_exec_conf", + "createdAt": "2024-09-09T19:09:57Z", + "executableId": "wt-spam-detection-i749902", + "id": "e529e8bd58740bc9", + "modifiedAt": "2024-09-09T19:11:17Z", + "outputArtifacts": [ + { + "createdAt": "2024-09-09T19:10:48Z", + "description": "", + "executionId": "e529e8bd58740bc9", + "id": "c4792df8-da67-44fe-ad99-b5ea74bbb248", + "kind": "model", + "modifiedAt": "2024-09-09T19:10:48Z", + "name": "classifier-model-output", + "scenarioId": "scenario-spam-detection-i749902", + "url": "ai://default/e529e8bd58740bc9/classifier-model-output" + } + ], + "scenarioId": "scenario-spam-detection-i749902", + "startTime": "2024-09-09T19:10:11Z", + "status": "COMPLETED", + "submissionTime": "2024-09-09T19:10:11Z", + "targetStatus": "COMPLETED" + } + """))); + + final AiExecutionResponseWithDetails execution = + new ExecutionApi(getClient(destination)).executionGet("default", "e529e8bd58740bc9"); + + assertThat(execution).isNotNull(); + assertThat(execution.getCompletionTime()).isEqualTo("2024-09-09T19:10:58Z"); + assertThat(execution.getConfigurationId()).isEqualTo("218b651d-113d-4da0-be15-b63a644a92fb"); + assertThat(execution.getConfigurationName()).isEqualTo("i749902_exec_conf"); + assertThat(execution.getCreatedAt()).isEqualTo("2024-09-09T19:09:57Z"); + // executableId is not in the generated client. + assertThat(execution.getCustomField("executableId")).isEqualTo("wt-spam-detection-i749902"); + assertThat(execution.getId()).isEqualTo("e529e8bd58740bc9"); + assertThat(execution.getModifiedAt()).isEqualTo("2024-09-09T19:11:17Z"); + assertThat(execution.getScenarioId()).isEqualTo("scenario-spam-detection-i749902"); + assertThat(execution.getStartTime()).isEqualTo("2024-09-09T19:10:11Z"); + assertThat(execution.getStatus()).isEqualTo(AiExecutionStatus.COMPLETED); + assertThat(execution.getSubmissionTime()).isEqualTo("2024-09-09T19:10:11Z"); + assertThat(execution.getTargetStatus()) + .isEqualTo(AiExecutionResponseWithDetails.TargetStatusEnum.COMPLETED); + assertThat(execution.getOutputArtifacts().size()).isEqualTo(1); + + final AiArtifact aiArtifact = execution.getOutputArtifacts().get(0); + + assertThat(aiArtifact.getCreatedAt()).isEqualTo("2024-09-09T19:10:48Z"); + assertThat(aiArtifact.getDescription()).isEqualTo(""); + assertThat(aiArtifact.getExecutionId()).isEqualTo("e529e8bd58740bc9"); + assertThat(aiArtifact.getId()).isEqualTo("c4792df8-da67-44fe-ad99-b5ea74bbb248"); + assertThat(aiArtifact.getKind()).isEqualTo(AiArtifact.KindEnum.MODEL); + assertThat(aiArtifact.getModifiedAt()).isEqualTo("2024-09-09T19:10:48Z"); + assertThat(aiArtifact.getName()).isEqualTo("classifier-model-output"); + assertThat(aiArtifact.getScenarioId()).isEqualTo("scenario-spam-detection-i749902"); + assertThat(aiArtifact.getUrl()) + .isEqualTo("ai://default/e529e8bd58740bc9/classifier-model-output"); + } + + @Test + void deleteExecution() { + wireMockServer.stubFor( + delete(urlPathEqualTo("/lm/executions/e529e8bd58740bc9")) + .withHeader("AI-Resource-Group", equalTo("default")) + .willReturn( + aResponse() + .withStatus(HttpStatus.SC_ACCEPTED) + .withHeader("content-type", "application/json") + .withBody( + """ + { + "id": "e529e8bd58740bc9", + "message": "Deletion scheduled", + "targetStatus": "DELETED" + } + """))); + + final AiExecutionDeletionResponse execution = + new ExecutionApi(getClient(destination)).executionDelete("default", "e529e8bd58740bc9"); + + assertThat(execution).isNotNull(); + assertThat(execution.getId()).isEqualTo("e529e8bd58740bc9"); + assertThat(execution.getMessage()).isEqualTo("Deletion scheduled"); + // targetStatus is not in the generated client. + assertThat(execution.getCustomField("targetStatus")).isEqualTo("DELETED"); + } + + @Test + void patchExecution() { + wireMockServer.stubFor( + patch(urlPathEqualTo("/lm/executions/eec3c6ea18bac6da")) + .withHeader("AI-Resource-Group", equalTo("default")) + .willReturn( + aResponse() + .withStatus(HttpStatus.SC_ACCEPTED) + .withHeader("content-type", "application/json") + .withBody( + """ + { + "id": "eec3c6ea18bac6da", + "message": "Execution modification scheduled" + } + """))); + + final AiExecutionModificationRequest aiExecutionModificationRequest = + AiExecutionModificationRequest.create() + .targetStatus(AiExecutionModificationRequest.TargetStatusEnum.STOPPED); + final AiExecutionModificationResponse aiExecutionModificationResponse = + new ExecutionApi(getClient(destination)) + .executionModify("default", "eec3c6ea18bac6da", aiExecutionModificationRequest); + + assertThat(aiExecutionModificationResponse).isNotNull(); + assertThat(aiExecutionModificationResponse.getId()).isEqualTo("eec3c6ea18bac6da"); + assertThat(aiExecutionModificationResponse.getMessage()) + .isEqualTo("Execution modification scheduled"); + + wireMockServer.verify( + patchRequestedFor(urlPathEqualTo("/lm/executions/eec3c6ea18bac6da")) + .withHeader("AI-Resource-Group", equalTo("default")) + .withRequestBody(equalToJson("{\"targetStatus\":\"STOPPED\"}"))); } } diff --git a/core/src/test/java/com/sap/ai/sdk/core/client/ScenarioUnitTest.java b/core/src/test/java/com/sap/ai/sdk/core/client/ScenarioUnitTest.java index c2d955ba5..ab367c713 100644 --- a/core/src/test/java/com/sap/ai/sdk/core/client/ScenarioUnitTest.java +++ b/core/src/test/java/com/sap/ai/sdk/core/client/ScenarioUnitTest.java @@ -52,10 +52,13 @@ void getScenarios() { final AiScenarioList scenarioList = new ScenarioApi(getClient(destination)).scenarioQuery("default"); + assertThat(scenarioList).isNotNull(); assertThat(scenarioList.getCount()).isEqualTo(1); assertThat(scenarioList.getResources().size()).isEqualTo(1); - AiScenario scenario = scenarioList.getResources().get(0); + + final AiScenario scenario = scenarioList.getResources().get(0); + assertThat(scenario.getId()).isEqualTo("foundation-models"); assertThat(scenario.getName()).isEqualTo("foundation-models"); assertThat(scenario.getLabels().get(0).getKey()).isEqualTo("scenarios.ai.sap.com/llm"); @@ -87,14 +90,16 @@ void getScenarioVersions() { } """))); - AiVersionList versionList = + final AiVersionList versionList = new ScenarioApi(getClient(destination)) .scenarioQueryVersions("default", "foundation-models"); + assertThat(versionList).isNotNull(); assertThat(versionList.getCount()).isEqualTo(1); assertThat(versionList.getResources().size()).isEqualTo(1); - AiVersion version = versionList.getResources().get(0); + final AiVersion version = versionList.getResources().get(0); + assertThat(version.getCreatedAt()).isEqualTo("2024-05-08T08:41:23+00:00"); assertThat(version.getId()).isEqualTo("0.0.1"); assertThat(version.getModifiedAt()).isEqualTo("2024-05-08T08:41:23+00:00"); diff --git a/core/src/test/java/com/sap/ai/sdk/core/client/WireMockTestServer.java b/core/src/test/java/com/sap/ai/sdk/core/client/WireMockTestServer.java index 5091331b0..816e1f024 100644 --- a/core/src/test/java/com/sap/ai/sdk/core/client/WireMockTestServer.java +++ b/core/src/test/java/com/sap/ai/sdk/core/client/WireMockTestServer.java @@ -6,6 +6,7 @@ import com.github.tomakehurst.wiremock.core.WireMockConfiguration; import com.sap.cloud.sdk.cloudplatform.connectivity.DefaultHttpDestination; import com.sap.cloud.sdk.cloudplatform.connectivity.Destination; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; /** Test server for all unit tests. */ @@ -22,4 +23,10 @@ static void setup() { wireMockServer.start(); destination = DefaultHttpDestination.builder(wireMockServer.baseUrl()).build(); } + + // Reset WireMock before each test to ensure clean state + @AfterEach + void reset() { + wireMockServer.resetAll(); + } } From 46298acb79d272d9f3d916c7db2a9232f3814b83 Mon Sep 17 00:00:00 2001 From: Roshin Rajan Panackal Date: Mon, 16 Sep 2024 14:41:46 +0200 Subject: [PATCH 08/11] Added unit tests for execution/deployment counts --- .../sdk/core/client/DeploymentUnitTest.java | 21 +++++++++++++++++++ .../ai/sdk/core/client/ExecutionUnitTest.java | 19 +++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/core/src/test/java/com/sap/ai/sdk/core/client/DeploymentUnitTest.java b/core/src/test/java/com/sap/ai/sdk/core/client/DeploymentUnitTest.java index 87b5db9cd..6bb4c608c 100644 --- a/core/src/test/java/com/sap/ai/sdk/core/client/DeploymentUnitTest.java +++ b/core/src/test/java/com/sap/ai/sdk/core/client/DeploymentUnitTest.java @@ -331,4 +331,25 @@ void patchDeploymentConfiguration() { } """))); } + + @Test + void getDeploymentCount() { + wireMockServer.stubFor( + get(urlPathEqualTo("/lm/deployments/$count")) + .withHeader("AI-Resource-Group", equalTo("default")) + .willReturn( + aResponse() + .withStatus(HttpStatus.SC_OK) + .withHeader("content-type", "application/json") + .withBody( + """ + 1 + """))); + + final int count = new DeploymentApi(getClient(destination)).deploymentCount("default"); + + assertThat(count).isEqualTo(1); + } + + } diff --git a/core/src/test/java/com/sap/ai/sdk/core/client/ExecutionUnitTest.java b/core/src/test/java/com/sap/ai/sdk/core/client/ExecutionUnitTest.java index 6fdc89226..8560c5340 100644 --- a/core/src/test/java/com/sap/ai/sdk/core/client/ExecutionUnitTest.java +++ b/core/src/test/java/com/sap/ai/sdk/core/client/ExecutionUnitTest.java @@ -290,4 +290,23 @@ void patchExecution() { .withHeader("AI-Resource-Group", equalTo("default")) .withRequestBody(equalToJson("{\"targetStatus\":\"STOPPED\"}"))); } + + @Test + void getExecutionCount() { + wireMockServer.stubFor( + get(urlPathEqualTo("/lm/executions/$count")) + .withHeader("AI-Resource-Group", equalTo("default")) + .willReturn( + aResponse() + .withStatus(HttpStatus.SC_OK) + .withHeader("content-type", "application/json") + .withBody( + """ + 1 + """))); + + final int count = new ExecutionApi(getClient(destination)).executionCount("default"); + + assertThat(count).isEqualTo(1); + } } From a0149e680f8e16a3a4f479dd6e30ab96c8565dc5 Mon Sep 17 00:00:00 2001 From: SAP Cloud SDK Bot Date: Mon, 16 Sep 2024 12:45:45 +0000 Subject: [PATCH 09/11] Formatting --- .../sap/ai/sdk/core/client/DeploymentUnitTest.java | 11 ++++------- .../com/sap/ai/sdk/core/client/ExecutionUnitTest.java | 9 ++++----- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/core/src/test/java/com/sap/ai/sdk/core/client/DeploymentUnitTest.java b/core/src/test/java/com/sap/ai/sdk/core/client/DeploymentUnitTest.java index 6bb4c608c..f95a4e88a 100644 --- a/core/src/test/java/com/sap/ai/sdk/core/client/DeploymentUnitTest.java +++ b/core/src/test/java/com/sap/ai/sdk/core/client/DeploymentUnitTest.java @@ -331,7 +331,7 @@ void patchDeploymentConfiguration() { } """))); } - + @Test void getDeploymentCount() { wireMockServer.stubFor( @@ -341,15 +341,12 @@ void getDeploymentCount() { aResponse() .withStatus(HttpStatus.SC_OK) .withHeader("content-type", "application/json") - .withBody( - """ + .withBody(""" 1 """))); - + final int count = new DeploymentApi(getClient(destination)).deploymentCount("default"); - + assertThat(count).isEqualTo(1); } - - } diff --git a/core/src/test/java/com/sap/ai/sdk/core/client/ExecutionUnitTest.java b/core/src/test/java/com/sap/ai/sdk/core/client/ExecutionUnitTest.java index 8560c5340..59b76bec9 100644 --- a/core/src/test/java/com/sap/ai/sdk/core/client/ExecutionUnitTest.java +++ b/core/src/test/java/com/sap/ai/sdk/core/client/ExecutionUnitTest.java @@ -290,7 +290,7 @@ void patchExecution() { .withHeader("AI-Resource-Group", equalTo("default")) .withRequestBody(equalToJson("{\"targetStatus\":\"STOPPED\"}"))); } - + @Test void getExecutionCount() { wireMockServer.stubFor( @@ -300,13 +300,12 @@ void getExecutionCount() { aResponse() .withStatus(HttpStatus.SC_OK) .withHeader("content-type", "application/json") - .withBody( - """ + .withBody(""" 1 """))); - + final int count = new ExecutionApi(getClient(destination)).executionCount("default"); - + assertThat(count).isEqualTo(1); } } From a8099f70d914a19fc174623e79af2e0a7c7c2758 Mon Sep 17 00:00:00 2001 From: Roshin Rajan Panackal Date: Wed, 18 Sep 2024 14:36:26 +0200 Subject: [PATCH 10/11] Added missing unit tests except for logs --- .../ai/sdk/core/client/ArtifactUnitTest.java | 18 ++++ .../core/client/ConfigurationUnitTest.java | 77 +++++++++++++++ .../sdk/core/client/DeploymentUnitTest.java | 11 +-- .../ai/sdk/core/client/ExecutionUnitTest.java | 9 +- .../ai/sdk/core/client/ScenarioUnitTest.java | 96 +++++++++++++++++++ 5 files changed, 199 insertions(+), 12 deletions(-) diff --git a/core/src/test/java/com/sap/ai/sdk/core/client/ArtifactUnitTest.java b/core/src/test/java/com/sap/ai/sdk/core/client/ArtifactUnitTest.java index d0e8d569b..144e105db 100644 --- a/core/src/test/java/com/sap/ai/sdk/core/client/ArtifactUnitTest.java +++ b/core/src/test/java/com/sap/ai/sdk/core/client/ArtifactUnitTest.java @@ -157,4 +157,22 @@ void getArtifactById() { assertThat(artifact.getUrl()) .isEqualTo("https://file-examples.com/wp-content/storage/2017/10/file-sample_150kB.pdf"); } + + @Test + void getArtifactCount() { + wireMockServer.stubFor( + get(urlPathEqualTo("/lm/artifacts/$count")) + .withHeader("AI-Resource-Group", equalTo("default")) + .willReturn( + aResponse() + .withStatus(HttpStatus.SC_OK) + .withHeader("content-type", "application/json") + .withBody(""" + 4 + """))); + + final int count = new ArtifactApi(getClient(destination)).artifactCount("default"); + + assertThat(count).isEqualTo(4); + } } diff --git a/core/src/test/java/com/sap/ai/sdk/core/client/ConfigurationUnitTest.java b/core/src/test/java/com/sap/ai/sdk/core/client/ConfigurationUnitTest.java index 4bf54abb8..67a7ab073 100644 --- a/core/src/test/java/com/sap/ai/sdk/core/client/ConfigurationUnitTest.java +++ b/core/src/test/java/com/sap/ai/sdk/core/client/ConfigurationUnitTest.java @@ -15,6 +15,7 @@ import com.sap.ai.sdk.core.client.model.AiConfigurationBaseData; import com.sap.ai.sdk.core.client.model.AiConfigurationCreationResponse; import com.sap.ai.sdk.core.client.model.AiConfigurationList; +import com.sap.ai.sdk.core.client.model.AiParameterArgumentBinding; import org.apache.hc.core5.http.HttpStatus; import org.junit.jupiter.api.Test; @@ -133,4 +134,80 @@ void postConfiguration() { } """))); } + + @Test + void getConfigurationCount() { + wireMockServer.stubFor( + get(urlPathEqualTo("/lm/configurations/$count")) + .withHeader("AI-Resource-Group", equalTo("default")) + .willReturn( + aResponse() + .withStatus(HttpStatus.SC_OK) + .withHeader("content-type", "application/json") + .withBody(""" + 3 + """))); + + final int configurationCount = + new ConfigurationApi(getClient(destination)).configurationCount("default"); + + assertThat(configurationCount).isEqualTo(3); + } + + @Test + void getConfigurationById() { + wireMockServer.stubFor( + get(urlPathEqualTo("/lm/configurations/6ff6cb80-87db-45f0-b718-4e1d96e66332")) + .withHeader("AI-Resource-Group", equalTo("default")) + .willReturn( + aResponse() + .withStatus(HttpStatus.SC_OK) + .withHeader("content-type", "application/json") + .withBody( + """ + { + "createdAt": "2024-09-11T09:14:31Z", + "executableId": "st-spam-detection-i749902", + "id": "6ff6cb80-87db-45f0-b718-4e1d96e66332", + "inputArtifactBindings": [ + { + "artifactId": "c4792df8-da67-44fe-ad99-b5ea74bbb248", + "key": "modeluri" + } + ], + "name": "i749902_depl_conf", + "parameterBindings": [ + { + "key": "minreplicas", + "value": "1" + } + ], + "scenarioId": "scenario-spam-detection-i749902" + } + """))); + + final AiConfiguration configuration = + new ConfigurationApi(getClient(destination)) + .configurationGet("default", "6ff6cb80-87db-45f0-b718-4e1d96e66332"); + + assertThat(configuration).isNotNull(); + assertThat(configuration.getCreatedAt()).isEqualTo("2024-09-11T09:14:31Z"); + assertThat(configuration.getExecutableId()).isEqualTo("st-spam-detection-i749902"); + assertThat(configuration.getId()).isEqualTo("6ff6cb80-87db-45f0-b718-4e1d96e66332"); + assertThat(configuration.getInputArtifactBindings().size()).isEqualTo(1); + assertThat(configuration.getName()).isEqualTo("i749902_depl_conf"); + assertThat(configuration.getParameterBindings().size()).isEqualTo(1); + assertThat(configuration.getScenarioId()).isEqualTo("scenario-spam-detection-i749902"); + + AiArtifactArgumentBinding aiArtifactArgumentBinding = + configuration.getInputArtifactBindings().get(0); + assertThat(aiArtifactArgumentBinding.getArtifactId()) + .isEqualTo("c4792df8-da67-44fe-ad99-b5ea74bbb248"); + assertThat(aiArtifactArgumentBinding.getKey()).isEqualTo("modeluri"); + + AiParameterArgumentBinding aiParameterArgumentBinding = + configuration.getParameterBindings().get(0); + assertThat(aiParameterArgumentBinding.getKey()).isEqualTo("minreplicas"); + assertThat(aiParameterArgumentBinding.getValue()).isEqualTo("1"); + } } diff --git a/core/src/test/java/com/sap/ai/sdk/core/client/DeploymentUnitTest.java b/core/src/test/java/com/sap/ai/sdk/core/client/DeploymentUnitTest.java index 6bb4c608c..f95a4e88a 100644 --- a/core/src/test/java/com/sap/ai/sdk/core/client/DeploymentUnitTest.java +++ b/core/src/test/java/com/sap/ai/sdk/core/client/DeploymentUnitTest.java @@ -331,7 +331,7 @@ void patchDeploymentConfiguration() { } """))); } - + @Test void getDeploymentCount() { wireMockServer.stubFor( @@ -341,15 +341,12 @@ void getDeploymentCount() { aResponse() .withStatus(HttpStatus.SC_OK) .withHeader("content-type", "application/json") - .withBody( - """ + .withBody(""" 1 """))); - + final int count = new DeploymentApi(getClient(destination)).deploymentCount("default"); - + assertThat(count).isEqualTo(1); } - - } diff --git a/core/src/test/java/com/sap/ai/sdk/core/client/ExecutionUnitTest.java b/core/src/test/java/com/sap/ai/sdk/core/client/ExecutionUnitTest.java index 8560c5340..59b76bec9 100644 --- a/core/src/test/java/com/sap/ai/sdk/core/client/ExecutionUnitTest.java +++ b/core/src/test/java/com/sap/ai/sdk/core/client/ExecutionUnitTest.java @@ -290,7 +290,7 @@ void patchExecution() { .withHeader("AI-Resource-Group", equalTo("default")) .withRequestBody(equalToJson("{\"targetStatus\":\"STOPPED\"}"))); } - + @Test void getExecutionCount() { wireMockServer.stubFor( @@ -300,13 +300,12 @@ void getExecutionCount() { aResponse() .withStatus(HttpStatus.SC_OK) .withHeader("content-type", "application/json") - .withBody( - """ + .withBody(""" 1 """))); - + final int count = new ExecutionApi(getClient(destination)).executionCount("default"); - + assertThat(count).isEqualTo(1); } } diff --git a/core/src/test/java/com/sap/ai/sdk/core/client/ScenarioUnitTest.java b/core/src/test/java/com/sap/ai/sdk/core/client/ScenarioUnitTest.java index ab367c713..f7d973812 100644 --- a/core/src/test/java/com/sap/ai/sdk/core/client/ScenarioUnitTest.java +++ b/core/src/test/java/com/sap/ai/sdk/core/client/ScenarioUnitTest.java @@ -7,7 +7,11 @@ import static com.sap.ai.sdk.core.Core.getClient; import static org.assertj.core.api.Assertions.assertThat; +import com.sap.ai.sdk.core.client.model.AiModelBaseData; +import com.sap.ai.sdk.core.client.model.AiModelList; +import com.sap.ai.sdk.core.client.model.AiModelVersion; import com.sap.ai.sdk.core.client.model.AiScenario; +import com.sap.ai.sdk.core.client.model.AiScenarioLabel; import com.sap.ai.sdk.core.client.model.AiScenarioList; import com.sap.ai.sdk.core.client.model.AiVersion; import com.sap.ai.sdk.core.client.model.AiVersionList; @@ -105,4 +109,96 @@ void getScenarioVersions() { assertThat(version.getModifiedAt()).isEqualTo("2024-05-08T08:41:23+00:00"); assertThat(version.getScenarioId()).isEqualTo("foundation-models"); } + + @Test + void getScenarioById() { + wireMockServer.stubFor( + get(urlPathEqualTo("/lm/scenarios/foundation-models")) + .withHeader("AI-Resource-Group", equalTo("default")) + .willReturn( + aResponse() + .withHeader("content-type", "application/json") + .withStatus(HttpStatus.SC_OK) + .withBody( + """ + { + "createdAt": "2023-11-03T14:02:46+00:00", + "description": "AI Core Global Scenario for LLM Access", + "id": "foundation-models", + "labels": [ + { + "key": "scenarios.ai.sap.com/llm", + "value": "true" + } + ], + "modifiedAt": "2024-05-24T08:36:29+00:00", + "name": "foundation-models" + } + """))); + + final AiScenario scenario = + new ScenarioApi(getClient(destination)).scenarioGet("default", "foundation-models"); + + assertThat(scenario).isNotNull(); + assertThat(scenario.getCreatedAt()).isEqualTo("2023-11-03T14:02:46+00:00"); + assertThat(scenario.getDescription()).isEqualTo("AI Core Global Scenario for LLM Access"); + assertThat(scenario.getId()).isEqualTo("foundation-models"); + assertThat(scenario.getName()).isEqualTo("foundation-models"); + assertThat(scenario.getModifiedAt()).isEqualTo("2024-05-24T08:36:29+00:00"); + + AiScenarioLabel aiScenarioLabel = scenario.getLabels().get(0); + assertThat(aiScenarioLabel.getKey()).isEqualTo("scenarios.ai.sap.com/llm"); + assertThat(aiScenarioLabel.getValue()).isEqualTo("true"); + } + + @Test + void getScenarioModels() { + wireMockServer.stubFor( + get(urlPathEqualTo("/lm/scenarios/foundation-models/models")) + .withHeader("AI-Resource-Group", equalTo("default")) + .willReturn( + aResponse() + .withHeader("content-type", "application/json") + .withStatus(HttpStatus.SC_OK) + .withBody( + """ + { + "count": 1, + "resources": [ + { + "description": "Mistral mixtral-8x7b-instruct-v01 model", + "executableId": "aicore-opensource", + "model": "mistralai--mixtral-8x7b-instruct-v01", + "versions": [ + { + "deprecated": false, + "isLatest": true, + "name": "202407", + "retirementDate": "" + } + ] + } + ] + } + """))); + + final AiModelList scenarioList = + new ScenarioApi(getClient(destination)).modelsGet("foundation-models", "default"); + + assertThat(scenarioList).isNotNull(); + assertThat(scenarioList.getCount()).isEqualTo(1); + assertThat(scenarioList.getResources().size()).isEqualTo(1); + + final AiModelBaseData scenario = scenarioList.getResources().get(0); + assertThat(scenario.getDescription()).isEqualTo("Mistral mixtral-8x7b-instruct-v01 model"); + assertThat(scenario.getExecutableId()).isEqualTo("aicore-opensource"); + assertThat(scenario.getModel()).isEqualTo("mistralai--mixtral-8x7b-instruct-v01"); + + AiModelVersion aiModelVersion = scenario.getVersions().get(0); + assertThat(aiModelVersion.getName()).isEqualTo("202407"); + assertThat(aiModelVersion.isIsLatest()).isEqualTo(true); + // deprecated and retirementDate properties are not defined in spec. + assertThat(aiModelVersion.getCustomField("deprecated")).isEqualTo(false); + assertThat(aiModelVersion.getCustomField("retirementDate")).isEqualTo(""); + } } From ba6cf39c9e3aebbcdafb31357f2efa092938963e Mon Sep 17 00:00:00 2001 From: Roshin Rajan Panackal Date: Wed, 18 Sep 2024 16:22:14 +0200 Subject: [PATCH 11/11] Use executableId getter in unit test --- .../java/com/sap/ai/sdk/core/client/ExecutionUnitTest.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/core/src/test/java/com/sap/ai/sdk/core/client/ExecutionUnitTest.java b/core/src/test/java/com/sap/ai/sdk/core/client/ExecutionUnitTest.java index 1cb01f141..68c67bcf6 100644 --- a/core/src/test/java/com/sap/ai/sdk/core/client/ExecutionUnitTest.java +++ b/core/src/test/java/com/sap/ai/sdk/core/client/ExecutionUnitTest.java @@ -200,8 +200,7 @@ void getExecutionById() { assertThat(execution.getConfigurationId()).isEqualTo("218b651d-113d-4da0-be15-b63a644a92fb"); assertThat(execution.getConfigurationName()).isEqualTo("i749902_exec_conf"); assertThat(execution.getCreatedAt()).isEqualTo("2024-09-09T19:09:57Z"); - // executableId is not in the generated client. - assertThat(execution.getCustomField("executableId")).isEqualTo("wt-spam-detection-i749902"); + assertThat(execution.getExecutableId()).isEqualTo("wt-spam-detection-i749902"); assertThat(execution.getId()).isEqualTo("e529e8bd58740bc9"); assertThat(execution.getModifiedAt()).isEqualTo("2024-09-09T19:11:17Z"); assertThat(execution.getScenarioId()).isEqualTo("scenario-spam-detection-i749902");