From 0d306135f5541176ee8abbc54e896f77d8530fd9 Mon Sep 17 00:00:00 2001 From: Jaro Hartmann Date: Thu, 25 Jan 2024 11:18:37 +0100 Subject: [PATCH] feat(irs-test):[#344] Create reusable wiremock config --- .../tractusx/irs/WireMockTestConfig.java | 17 +- .../tractusx/irs/bpdm/BpdmWiremockTest.java | 15 +- .../semanticshub/SemanticHubWiremockTest.java | 87 ++++-- .../{ => semantichub}/all-models-page-IT.json | 0 .../semantichub/batch-2.0.0-schema.json | 143 ++++++++++ .../client/SubmodelFacadeWiremockConfig.java | 265 ++++++++++++++++++ .../client/SubmodelFacadeWiremockTest.java | 120 ++------ ...igitalTwinRegistryServiceWiremockTest.java | 43 +-- .../DiscoveryServiceWiremockConfig.java | 93 ++++++ .../testing/wiremock}/DtrWiremockConfig.java | 114 ++------ .../irs/testing/wiremock/WireMockConfig.java | 7 + 11 files changed, 674 insertions(+), 230 deletions(-) rename irs-api/src/test/resources/__files/{ => semantichub}/all-models-page-IT.json (100%) create mode 100644 irs-api/src/test/resources/__files/semantichub/batch-2.0.0-schema.json create mode 100644 irs-edc-client/src/test/java/org/eclipse/tractusx/irs/edc/client/SubmodelFacadeWiremockConfig.java create mode 100644 irs-testing/src/main/java/org/eclipse/tractusx/irs/testing/wiremock/DiscoveryServiceWiremockConfig.java rename {irs-registry-client/src/test/java/org/eclipse/tractusx/irs/registryclient/decentral => irs-testing/src/main/java/org/eclipse/tractusx/irs/testing/wiremock}/DtrWiremockConfig.java (58%) diff --git a/irs-api/src/test/java/org/eclipse/tractusx/irs/WireMockTestConfig.java b/irs-api/src/test/java/org/eclipse/tractusx/irs/WireMockTestConfig.java index 4e7d14c48a..16e9e92c3c 100644 --- a/irs-api/src/test/java/org/eclipse/tractusx/irs/WireMockTestConfig.java +++ b/irs-api/src/test/java/org/eclipse/tractusx/irs/WireMockTestConfig.java @@ -26,11 +26,17 @@ import static org.eclipse.tractusx.irs.configuration.RestTemplateConfig.SEMHUB_REST_TEMPLATE; import static org.eclipse.tractusx.irs.testing.wiremock.WireMockConfig.restTemplateProxy; +import java.util.List; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.eclipse.edc.policy.model.PolicyRegistrationTypes; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.test.context.TestConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Primary; import org.springframework.context.annotation.Profile; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.web.client.RestTemplate; @TestConfiguration @@ -49,7 +55,16 @@ RestTemplate dtrRestTemplate() { @Profile("integrationtest") @Bean(EDC_REST_TEMPLATE) RestTemplate edcRestTemplate() { - return restTemplateProxy(PROXY_SERVER_HOST, HTTP_PORT); + final RestTemplate edcRestTemplate = restTemplateProxy(PROXY_SERVER_HOST, HTTP_PORT); + final List> messageConverters = edcRestTemplate.getMessageConverters(); + for (final HttpMessageConverter converter : messageConverters) { + if (converter instanceof final MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter) { + final ObjectMapper mappingJackson2HttpMessageConverterObjectMapper = mappingJackson2HttpMessageConverter.getObjectMapper(); + PolicyRegistrationTypes.TYPES.forEach( + mappingJackson2HttpMessageConverterObjectMapper::registerSubtypes); + } + } + return edcRestTemplate; } @Primary diff --git a/irs-api/src/test/java/org/eclipse/tractusx/irs/bpdm/BpdmWiremockTest.java b/irs-api/src/test/java/org/eclipse/tractusx/irs/bpdm/BpdmWiremockTest.java index 325c97cfd4..e9e107cd8d 100644 --- a/irs-api/src/test/java/org/eclipse/tractusx/irs/bpdm/BpdmWiremockTest.java +++ b/irs-api/src/test/java/org/eclipse/tractusx/irs/bpdm/BpdmWiremockTest.java @@ -23,6 +23,8 @@ import static com.github.tomakehurst.wiremock.client.WireMock.givenThat; import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.eclipse.tractusx.irs.testing.wiremock.WireMockConfig.responseWithStatus; import static org.eclipse.tractusx.irs.testing.wiremock.WireMockConfig.restTemplateProxy; import java.util.Optional; @@ -31,6 +33,7 @@ import com.github.tomakehurst.wiremock.junit5.WireMockTest; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.client.RestTemplate; @WireMockTest @@ -114,13 +117,11 @@ void shouldResolveManufacturerName() { @Test void shouldReturnEmptyOnNotFound() { // Arrange - givenThat(get(urlPathEqualTo("/legal-entities/BPNL00000000TEST")).willReturn( - aResponse().withStatus(404).withHeader("Content-Type", "application/json;charset=UTF-8"))); + givenThat(get(urlPathEqualTo("/legal-entities/BPNL00000000TEST")).willReturn(responseWithStatus(404))); - // Act - final Optional manufacturerName = bpdmFacade.findManufacturerName("BPNL00000000TEST"); - - // Assert - assertThat(manufacturerName).isEmpty(); + // Act & Assert + // TODO fix implementation to not throw HttpClientErrorException$NotFound + assertThatExceptionOfType(HttpClientErrorException.class).isThrownBy( + () -> bpdmFacade.findManufacturerName("BPNL00000000TEST")); } } diff --git a/irs-api/src/test/java/org/eclipse/tractusx/irs/semanticshub/SemanticHubWiremockTest.java b/irs-api/src/test/java/org/eclipse/tractusx/irs/semanticshub/SemanticHubWiremockTest.java index 1106c18891..321f4d6fef 100644 --- a/irs-api/src/test/java/org/eclipse/tractusx/irs/semanticshub/SemanticHubWiremockTest.java +++ b/irs-api/src/test/java/org/eclipse/tractusx/irs/semanticshub/SemanticHubWiremockTest.java @@ -23,15 +23,21 @@ ********************************************************************************/ package org.eclipse.tractusx.irs.semanticshub; -import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; -import static com.github.tomakehurst.wiremock.client.WireMock.configureFor; import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; +import static com.github.tomakehurst.wiremock.client.WireMock.exactly; import static com.github.tomakehurst.wiremock.client.WireMock.get; +import static com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor; import static com.github.tomakehurst.wiremock.client.WireMock.givenThat; +import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; +import static com.github.tomakehurst.wiremock.client.WireMock.urlPathMatching; +import static com.github.tomakehurst.wiremock.client.WireMock.verify; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.eclipse.tractusx.irs.testing.wiremock.WireMockConfig.responseWithStatus; import static org.eclipse.tractusx.irs.testing.wiremock.WireMockConfig.restTemplateProxy; +import com.github.tomakehurst.wiremock.client.MappingBuilder; import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo; import com.github.tomakehurst.wiremock.junit5.WireMockTest; import org.eclipse.tractusx.irs.configuration.SemanticsHubConfiguration; @@ -45,6 +51,15 @@ class SemanticHubWiremockTest { private static final String PROXY_SERVER_HOST = "127.0.0.1"; private SemanticsHubFacade semanticsHubFacade; + private static MappingBuilder getAllModels200() { + return get(urlPathEqualTo("/models")).withHost(equalTo("semantic.hub")) + .willReturn(responseWithStatus(200).withBodyFile("all-models-page.json")); + } + + private static void verifyGetAllModels(final int times) { + verify(exactly(times), getRequestedFor(urlPathEqualTo("/models"))); + } + @BeforeEach void configureSystemUnderTest(WireMockRuntimeInfo wireMockRuntimeInfo) { final RestTemplate restTemplate = restTemplateProxy(PROXY_SERVER_HOST, wireMockRuntimeInfo.getHttpPort()); @@ -60,35 +75,75 @@ void configureSystemUnderTest(WireMockRuntimeInfo wireMockRuntimeInfo) { @Test void shouldReturn1Page() throws SchemaNotFoundException { - givenThat(get(urlPathEqualTo("/models")).willReturn(aResponse().withStatus(200) - .withHeader("Content-Type", - "application/json;charset=UTF-8") - .withBodyFile("all-models-page.json"))); + givenThat(getAllModels200()); final AspectModels allAspectModels = semanticsHubFacade.getAllAspectModels(); assertThat(allAspectModels.models()).isNotEmpty(); assertThat(allAspectModels.models().get(0).name()).isEqualTo("SerialPartTypization"); + verifyGetAllModels(1); } @Test void shouldReturn2Pages() throws SchemaNotFoundException { - givenThat(get(urlPathEqualTo("/models")).withQueryParam("page", equalTo("0")) + givenThat(get(urlPathEqualTo("/models")).withHost(equalTo("semantic.hub")) + .withQueryParam("page", equalTo("0")) .withQueryParam("pageSize", equalTo("10")) - .willReturn(aResponse().withStatus(200) - .withHeader("Content-Type", - "application/json;charset=UTF-8") - .withBodyFile("all-models-page1.json"))); - givenThat(get(urlPathEqualTo("/models")).withQueryParam("page", equalTo("1")) + .willReturn( + responseWithStatus(200).withBodyFile("all-models-page1.json"))); + givenThat(get(urlPathEqualTo("/models")).withHost(equalTo("semantic.hub")) + .withQueryParam("page", equalTo("1")) .withQueryParam("pageSize", equalTo("10")) - .willReturn(aResponse().withStatus(200) - .withHeader("Content-Type", - "application/json;charset=UTF-8") - .withBodyFile("all-models-page2.json"))); + .willReturn( + responseWithStatus(200).withBodyFile("all-models-page2.json"))); final AspectModels allAspectModels = semanticsHubFacade.getAllAspectModels(); assertThat(allAspectModels.models()).hasSize(20); assertThat(allAspectModels.models().get(0).name()).isEqualTo("SerialPartTypization"); + verifyGetAllModels(2); + } + + @Test + void shouldReturnJsonSchema() throws SchemaNotFoundException { + // Arrange + stubFor(get(urlPathMatching("/models/urn:samm:io.catenax.batch:2.0.0%23Batch/json-schema")).withHost( + equalTo("semantic.hub")) + .willReturn( + responseWithStatus( + 200).withBodyFile( + "semantichub/batch-2.0.0-schema.json"))); + + // Act + final String modelJsonSchema = semanticsHubFacade.getModelJsonSchema("urn:samm:io.catenax.batch:2.0.0#Batch"); + + // Assert + assertThat(modelJsonSchema).contains("urn_samm_io.catenax.batch_2.0.0_CatenaXIdTrait") + .contains("A batch is a quantity of (semi-) finished products or (raw) material"); + verify(exactly(1), + getRequestedFor(urlPathMatching("/models/urn:samm:io.catenax.batch:2.0.0%23Batch/json-schema"))); + } + + @Test + void shouldThrowSchemaExceptionWhenSchemaNotFound() { + // Arrange + final String url = "/models/%s/json-schema".formatted( + "urn:bamm:io.catenax.single_level_bom_as_built:2.0.0%23SingleLevelBomAsBuilt"); + final String errorBody = """ + { + "timestamp": "2024-01-24T12:06:23.390+00:00", + "status": 500, + "error": "Internal Server Error", + "path": "/api/v1/models/urn:bamm:io.catenax.single_level_bom_as_built:2.0.0#SingleLevelBomAsBuilt/json-schema" + } + """; + System.out.println(url); + stubFor(get(urlPathEqualTo(url)).withHost(equalTo("semantic.hub")) + .willReturn(responseWithStatus(500).withBody(errorBody))); + + // Act & Assert + assertThatExceptionOfType(SchemaNotFoundException.class).isThrownBy(() -> semanticsHubFacade.getModelJsonSchema( + "urn:bamm:io.catenax.single_level_bom_as_built:2.0.0#SingleLevelBomAsBuilt")); + verify(exactly(1), getRequestedFor(urlPathEqualTo(url))); } } diff --git a/irs-api/src/test/resources/__files/all-models-page-IT.json b/irs-api/src/test/resources/__files/semantichub/all-models-page-IT.json similarity index 100% rename from irs-api/src/test/resources/__files/all-models-page-IT.json rename to irs-api/src/test/resources/__files/semantichub/all-models-page-IT.json diff --git a/irs-api/src/test/resources/__files/semantichub/batch-2.0.0-schema.json b/irs-api/src/test/resources/__files/semantichub/batch-2.0.0-schema.json new file mode 100644 index 0000000000..4ff694cd76 --- /dev/null +++ b/irs-api/src/test/resources/__files/semantichub/batch-2.0.0-schema.json @@ -0,0 +1,143 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema", + "description": "A batch is a quantity of (semi-) finished products or (raw) material product that have been produced under the same circumstances (e.g. same production location), as specified groups or amounts, within a certain time frame. Every batch can differ in the number or amount of products. Different batches can have varied specifications, e.g., different colors. A batch is identified via a Batch ID.", + "type": "object", + "components": { + "schemas": { + "urn_samm_io.catenax.batch_2.0.0_CatenaXIdTrait": { + "type": "string", + "description": "The provided regular expression ensures that the UUID is composed of five groups of characters separated by hyphens, in the form 8-4-4-4-12 for a total of 36 characters (32 hexadecimal characters and 4 hyphens), optionally prefixed by \"urn:uuid:\" to make it an IRI.", + "pattern": "(^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$)|(^urn:uuid:[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$)" + }, + "urn_samm_io.catenax.batch_2.0.0_KeyTrait": { + "type": "string", + "description": "Constraint that ensures that the predefined keys are used.", + "pattern": "^(manufacturerId|batchId)$" + }, + "urn_samm_io.catenax.batch_2.0.0_ValueCharacteristic": { + "type": "string", + "description": "The value of an identifier." + }, + "urn_samm_io.catenax.batch_2.0.0_KeyValueList": { + "description": "A list of key value pairs for local identifiers, which are composed of a key and a corresponding value.", + "type": "object", + "properties": { + "key": { + "description": "The key of a local identifier.", + "$ref": "#/components/schemas/urn_samm_io.catenax.batch_2.0.0_KeyTrait" + }, + "value": { + "description": "The value of an identifier.", + "$ref": "#/components/schemas/urn_samm_io.catenax.batch_2.0.0_ValueCharacteristic" + } + }, + "required": [ + "key", + "value" + ] + }, + "urn_samm_io.catenax.batch_2.0.0_LocalIdentifierCharacteristic": { + "description": "A batch may have multiple attributes, which uniquely identify that batch in a specific dataspace (e.g. the manufacturer`s dataspace)", + "type": "array", + "items": { + "$ref": "#/components/schemas/urn_samm_io.catenax.batch_2.0.0_KeyValueList" + }, + "uniqueItems": true + }, + "urn_samm_org.eclipse.esmf.samm_characteristic_2.1.0_Timestamp": { + "type": "string", + "pattern": "-?([1-9][0-9]{3,}|0[0-9]{3})-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])T(([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9](\\.[0-9]+)?|(24:00:00(\\.0+)?))(Z|(\\+|-)((0[0-9]|1[0-3]):[0-5][0-9]|14:00))?", + "description": "Describes a Property which contains the date and time with an optional timezone." + }, + "urn_samm_io.catenax.batch_2.0.0_ProductionCountryCodeTrait": { + "type": "string", + "description": "Regular Expression that ensures a three-letter code", + "pattern": "^[A-Z]{3}$" + }, + "urn_samm_io.catenax.batch_2.0.0_ManufacturingCharacteristic": { + "description": "Characteristic to describe manufacturing related data", + "type": "object", + "properties": { + "date": { + "description": "Timestamp of the manufacturing date as the final step in production process (e.g. final quality check, ready-for-shipment event)", + "$ref": "#/components/schemas/urn_samm_org.eclipse.esmf.samm_characteristic_2.1.0_Timestamp" + }, + "country": { + "description": "Country code where the part was manufactured", + "$ref": "#/components/schemas/urn_samm_io.catenax.batch_2.0.0_ProductionCountryCodeTrait" + } + }, + "required": [ + "date" + ] + }, + "urn_samm_io.catenax.batch_2.0.0_PartIdCharacteristic": { + "type": "string", + "description": "The part ID is a multi-character string, ususally assigned by an ERP system" + }, + "urn_samm_io.catenax.batch_2.0.0_PartNameCharacteristic": { + "type": "string", + "description": "Part Name in string format from the respective system in the value chain" + }, + "urn_samm_io.catenax.batch_2.0.0_ClassificationCharacteristic": { + "type": "string", + "description": "A part type must be placed into one of the following classes: 'component', 'product', 'software', 'assembly', 'tool', or 'raw material'.", + "enum": [ + "product", + "raw material", + "software", + "assembly", + "tool", + "component" + ] + }, + "urn_samm_io.catenax.batch_2.0.0_PartTypeInformationCharacteristic": { + "description": "The characteristics of the part type", + "type": "object", + "properties": { + "manufacturerPartId": { + "description": "Part ID as assigned by the manufacturer of the part. The Part ID identifies the part (as designed) in the manufacturer`s dataspace. The Part ID does not reference a specific instance of a part and thus should not be confused with the serial number or batch number.", + "$ref": "#/components/schemas/urn_samm_io.catenax.batch_2.0.0_PartIdCharacteristic" + }, + "nameAtManufacturer": { + "description": "Name of the part as assigned by the manufacturer", + "$ref": "#/components/schemas/urn_samm_io.catenax.batch_2.0.0_PartNameCharacteristic" + }, + "classification": { + "description": "The classification of the part type according to STEP standard definition", + "$ref": "#/components/schemas/urn_samm_io.catenax.batch_2.0.0_ClassificationCharacteristic" + } + }, + "required": [ + "manufacturerPartId", + "nameAtManufacturer", + "classification" + ] + } + } + }, + "properties": { + "catenaXId": { + "description": "The fully anonymous Catena-X ID of the batch, valid for the Catena-X dataspace.", + "$ref": "#/components/schemas/urn_samm_io.catenax.batch_2.0.0_CatenaXIdTrait" + }, + "localIdentifiers": { + "description": "A local identifier enables identification of a part in a specific dataspace, but is not unique in Catena-X dataspace. Multiple local identifiers may exist.", + "$ref": "#/components/schemas/urn_samm_io.catenax.batch_2.0.0_LocalIdentifierCharacteristic" + }, + "manufacturingInformation": { + "description": "Information from manufacturing process, such as manufacturing date and manufacturing country", + "$ref": "#/components/schemas/urn_samm_io.catenax.batch_2.0.0_ManufacturingCharacteristic" + }, + "partTypeInformation": { + "description": "The part type of which the batch has been instantiated of.", + "$ref": "#/components/schemas/urn_samm_io.catenax.batch_2.0.0_PartTypeInformationCharacteristic" + } + }, + "required": [ + "catenaXId", + "localIdentifiers", + "manufacturingInformation", + "partTypeInformation" + ] +} \ No newline at end of file diff --git a/irs-edc-client/src/test/java/org/eclipse/tractusx/irs/edc/client/SubmodelFacadeWiremockConfig.java b/irs-edc-client/src/test/java/org/eclipse/tractusx/irs/edc/client/SubmodelFacadeWiremockConfig.java new file mode 100644 index 0000000000..762ddeaef1 --- /dev/null +++ b/irs-edc-client/src/test/java/org/eclipse/tractusx/irs/edc/client/SubmodelFacadeWiremockConfig.java @@ -0,0 +1,265 @@ +/******************************************************************************** + * Copyright (c) 2021,2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +package org.eclipse.tractusx.irs.edc.client; + +import static com.github.tomakehurst.wiremock.client.WireMock.get; +import static com.github.tomakehurst.wiremock.client.WireMock.givenThat; +import static com.github.tomakehurst.wiremock.client.WireMock.post; +import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; +import static org.eclipse.tractusx.irs.testing.wiremock.WireMockConfig.responseWithStatus; + +import java.nio.charset.StandardCharsets; +import java.util.Base64; +import java.util.List; +import java.util.Map; + +import org.eclipse.edc.spi.types.domain.edr.EndpointDataReference; +import org.eclipse.tractusx.irs.data.StringMapper; +import org.eclipse.tractusx.irs.edc.client.configuration.JsonLdConfiguration; +import org.eclipse.tractusx.irs.edc.client.model.EDRAuthCode; +import org.jetbrains.annotations.NotNull; + +public class SubmodelFacadeWiremockConfig { + + public static final String PATH_CATALOG = "/catalog/request"; + public static final String PATH_NEGOTIATE = "/contractnegotiations"; + public static final String PATH_TRANSFER = "/transferprocesses"; + public static final String PATH_STATE = "/state"; + public static final String PATH_DATAPLANE_PUBLIC = "/api/public"; + public static final String DATAPLANE_HOST = "http://provider.dataplane"; + public static final String CONTEXT = """ + { + "dct": "https://purl.org/dc/terms/", + "tx": "https://w3id.org/tractusx/v0.0.1/ns/", + "edc": "https://w3id.org/edc/v0.0.1/ns/", + "dcat": "https://www.w3.org/ns/dcat/", + "odrl": "http://www.w3.org/ns/odrl/2/", + "dspace": "https://w3id.org/dspace/v0.8/" + }"""; + public static final String EDC_PROVIDER_DUMMY_URL = "https://edc.io/api/v1/dsp"; + public static final String IRS_INTERNAL_CALLBACK_URL = "https://irs.test/internal/endpoint-data-reference"; + + // TODO move to irs-testing + + public static void prepareNegotiation(final EndpointDataReferenceStorage storage) { + prepareNegotiation(storage, "1bbaec6e-c316-4e1e-8258-c07a648cc43c", "1b21e963-0bc5-422a-b30d-fd3511861d88", + "7681f966-36ea-4542-b5ea-0d0db81967de:5a7ab616-989f-46ae-bdf2-32027b9f6ee6-31b614f5-ec14-4ed2-a509-e7b7780083e7:a6144a2e-c1b1-4ec6-96e1-a221da134e4f", + "5a7ab616-989f-46ae-bdf2-32027b9f6ee6-31b614f5-ec14-4ed2-a509-e7b7780083e7"); + } + + public static void prepareNegotiation(final EndpointDataReferenceStorage storage, final String negotiationId, + final String transferProcessId, final String contractAgreementId, final String edcAssetId) { + givenThat(post(urlPathEqualTo(PATH_CATALOG)).willReturn( + responseWithStatus(200).withBody(getCatalogResponse(edcAssetId, "USE", "BPNL00000003CRHK")))); + System.out.println(getCatalogResponse(edcAssetId, "USE", "BPNL00000003CRHK")); + givenThat(post(urlPathEqualTo(PATH_NEGOTIATE)).willReturn( + responseWithStatus(200).withBody(startNegotiationResponse(negotiationId)))); + + final String negotiationState = "FINALIZED"; + givenThat(get(urlPathEqualTo(PATH_NEGOTIATE + "/" + negotiationId)).willReturn(responseWithStatus(200).withBody( + getNegotiationConfirmedResponse(negotiationId, negotiationState, contractAgreementId)))); + + givenThat(get(urlPathEqualTo(PATH_NEGOTIATE + "/" + negotiationId + PATH_STATE)).willReturn( + responseWithStatus(200).withBody(getNegotiationStateResponse(negotiationState)))); + + givenThat(post(urlPathEqualTo(PATH_TRANSFER)).willReturn( + responseWithStatus(200).withBody(startTransferProcessResponse(transferProcessId)) + + )); + final String transferProcessState = "COMPLETED"; + givenThat(get(urlPathEqualTo(PATH_TRANSFER + "/" + transferProcessId + PATH_STATE)).willReturn( + responseWithStatus(200).withBody(getTransferProcessStateResponse(transferProcessState)) + + )); + givenThat(get(urlPathEqualTo(PATH_TRANSFER + "/" + transferProcessId)).willReturn( + responseWithStatus(200).withBody( + getTransferConfirmedResponse(transferProcessId, transferProcessState, edcAssetId, + contractAgreementId)))); + + final EndpointDataReference ref = createEndpointDataReference(contractAgreementId); + storage.put(contractAgreementId, ref); + } + + private static String startTransferProcessResponse(final String transferProcessId) { + return startNegotiationResponse(transferProcessId); + } + + private static String startNegotiationResponse(final String negotiationId) { + return """ + { + "@type": "edc:IdResponseDto", + "@id": "%s", + "edc:createdAt": 1686830151573, + "@context": %s + } + """.formatted(negotiationId, CONTEXT); + } + + private static String getNegotiationStateResponse(final String negotiationState) { + return stateResponseTemplate("edc:NegotiationState", negotiationState); + } + + private static String getTransferProcessStateResponse(final String transferProcessState) { + return stateResponseTemplate("edc:TransferState", transferProcessState); + } + + private static String stateResponseTemplate(final String responseType, final String negotiationState) { + return """ + { + "@type": "%s", + "edc:state": "%s", + "@context": %s + } + """.formatted(responseType, negotiationState, CONTEXT); + } + + private static String getNegotiationConfirmedResponse(final String negotiationId, final String negotiationState, + final String contractAgreementId) { + return """ + { + "@type": "edc:ContractNegotiationDto", + "@id": "%s", + "edc:type": "CONSUMER", + "edc:protocol": "dataspace-protocol-http", + "edc:state": "%s", + "edc:counterPartyAddress": "%s", + "edc:callbackAddresses": [], + "edc:contractAgreementId": "%s", + "@context": %s + } + """.formatted(negotiationId, negotiationState, EDC_PROVIDER_DUMMY_URL, contractAgreementId, CONTEXT); + } + + private static String getTransferConfirmedResponse(final String transferProcessId, final String transferState, + final String edcAssetId, final String contractAgreementId) { + return """ + { + "@id": "%s", + "@type": "edc:TransferProcessDto", + "edc:state": "%s", + "edc:stateTimestamp": 1688024335567, + "edc:type": "CONSUMER", + "edc:callbackAddresses": [], + "edc:dataDestination": { + "edc:type": "HttpProxy" + }, + "edc:dataRequest": { + "@type": "edc:DataRequestDto", + "@id": "%s", + "edc:assetId": "%s", + "edc:contractId": "%s", + "edc:connectorId": "BPNL00000003CRHK" + }, + "edc:receiverHttpEndpoint": "%s", + "@context": %s + } + """.formatted(transferProcessId, transferState, transferProcessId, edcAssetId, contractAgreementId, + IRS_INTERNAL_CALLBACK_URL, CONTEXT); + } + + private static String getCatalogResponse(final String edcAssetId, final String permissionType, + final String edcProviderBpn) { + return """ + { + "@id": "78ff625c-0c05-4014-965c-bd3d0a6a0de0", + "@type": "dcat:Catalog", + "dcat:dataset": { + "@id": "58505404-4da1-427a-82aa-b79482bcd1f0", + "@type": "dcat:Dataset", + "odrl:hasPolicy": { + "@id": "7681f966-36ea-4542-b5ea-0d0db81967de:5a7ab616-989f-46ae-bdf2-32027b9f6ee6-31b614f5-ec14-4ed2-a509-e7b7780083e7:66131c58-32af-4df0-825d-77f7df6017c1", + "@type": "odrl:Set", + "odrl:permission": { + "odrl:target": "%s", + "odrl:action": { + "odrl:type": "%s" + }, + "odrl:constraint": %s + }, + "odrl:prohibition": [], + "odrl:obligation": [], + "odrl:target": "%s" + }, + "dcat:distribution": [ + { + "@type": "dcat:Distribution", + "dct:format": { + "@id": "HttpProxy" + }, + "dcat:accessService": "4ba1faa1-7f1a-4fb7-a41c-317f450e7443" + } + ], + "edc:description": "IRS EDC Test Asset", + "edc:id": "%s" + }, + "dcat:service": { + "@id": "4ba1faa1-7f1a-4fb7-a41c-317f450e7443", + "@type": "dcat:DataService", + "dct:terms": "connector", + "dct:endpointUrl": "%s" + }, + "edc:participantId": "%s", + "@context": %s + } + """.formatted(edcAssetId, permissionType, createConstraints(), edcAssetId, edcAssetId, + EDC_PROVIDER_DUMMY_URL, edcProviderBpn, CONTEXT); + } + + @NotNull + private static String createConstraints() { + final List atomitConstraints = List.of(createAtomicConstraint("Membership", "active"), + createAtomicConstraint("FrameworkAgreement.traceability", "active")); + return """ + { + "odrl:and": [ + %s + ] + }""".formatted(String.join(",\n", atomitConstraints)); + } + + private static String createAtomicConstraint(final String leftOperand, final String rightOperand) { + return """ + { + "odrl:leftOperand": "%s", + "odrl:operator": { + "@id": "odrl:eq" + }, + "odrl:rightOperand": "%s" + }""".formatted(leftOperand, rightOperand); + } + + public static EndpointDataReference createEndpointDataReference(final String contractAgreementId) { + final EDRAuthCode edrAuthCode = EDRAuthCode.builder() + .cid(contractAgreementId) + .dad("test") + .exp(9999999999L) + .build(); + final String b64EncodedAuthCode = Base64.getUrlEncoder() + .encodeToString(StringMapper.mapToString(edrAuthCode) + .getBytes(StandardCharsets.UTF_8)); + final String jwtToken = "eyJhbGciOiJSUzI1NiJ9." + b64EncodedAuthCode + ".test"; + return EndpointDataReference.Builder.newInstance() + .authKey("testkey") + .authCode(jwtToken) + .properties( + Map.of(JsonLdConfiguration.NAMESPACE_EDC_CID, contractAgreementId)) + .endpoint(DATAPLANE_HOST + PATH_DATAPLANE_PUBLIC) + .build(); + } +} \ No newline at end of file diff --git a/irs-edc-client/src/test/java/org/eclipse/tractusx/irs/edc/client/SubmodelFacadeWiremockTest.java b/irs-edc-client/src/test/java/org/eclipse/tractusx/irs/edc/client/SubmodelFacadeWiremockTest.java index 7471135414..43ef624320 100644 --- a/irs-edc-client/src/test/java/org/eclipse/tractusx/irs/edc/client/SubmodelFacadeWiremockTest.java +++ b/irs-edc-client/src/test/java/org/eclipse/tractusx/irs/edc/client/SubmodelFacadeWiremockTest.java @@ -23,17 +23,13 @@ ********************************************************************************/ package org.eclipse.tractusx.irs.edc.client; -import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; -import static com.github.tomakehurst.wiremock.client.WireMock.configureFor; import static com.github.tomakehurst.wiremock.client.WireMock.get; import static com.github.tomakehurst.wiremock.client.WireMock.givenThat; -import static com.github.tomakehurst.wiremock.client.WireMock.post; import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; -import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.eclipse.tractusx.irs.edc.client.configuration.JsonLdConfiguration.NAMESPACE_EDC_CID; import static org.eclipse.tractusx.irs.edc.client.testutil.TestMother.createEdcTransformer; +import static org.eclipse.tractusx.irs.testing.wiremock.WireMockConfig.responseWithStatus; import static org.eclipse.tractusx.irs.testing.wiremock.WireMockConfig.restTemplateProxy; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -43,19 +39,16 @@ import java.time.OffsetDateTime; import java.util.ArrayList; import java.util.List; -import java.util.Map; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import com.fasterxml.jackson.databind.ObjectMapper; -import com.github.tomakehurst.wiremock.WireMockServer; import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo; import com.github.tomakehurst.wiremock.junit5.WireMockTest; import io.github.resilience4j.retry.RetryRegistry; import org.assertj.core.api.ThrowableAssert; import org.eclipse.edc.policy.model.PolicyRegistrationTypes; -import org.eclipse.edc.spi.types.domain.edr.EndpointDataReference; import org.eclipse.tractusx.irs.edc.client.cache.endpointdatareference.EndpointDataReferenceCacheService; import org.eclipse.tractusx.irs.edc.client.exceptions.EdcClientException; import org.eclipse.tractusx.irs.edc.client.exceptions.UsagePolicyException; @@ -68,10 +61,8 @@ import org.eclipse.tractusx.irs.edc.client.policy.Permission; import org.eclipse.tractusx.irs.edc.client.policy.PolicyCheckerService; import org.eclipse.tractusx.irs.edc.client.policy.PolicyType; -import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.web.client.RestClientException; @@ -119,7 +110,8 @@ void configureSystemUnderTest(WireMockRuntimeInfo wireMockRuntimeInfo) { final EdcDataPlaneClient dataPlaneClient = new EdcDataPlaneClient(restTemplate); final EDCCatalogFacade catalogFacade = new EDCCatalogFacade(controlPlaneClient, config); - final EndpointDataReferenceCacheService endpointDataReferenceCacheService = new EndpointDataReferenceCacheService(new EndpointDataReferenceStorage(Duration.ofMinutes(1))); + final EndpointDataReferenceCacheService endpointDataReferenceCacheService = new EndpointDataReferenceCacheService( + new EndpointDataReferenceStorage(Duration.ofMinutes(1))); acceptedPoliciesProvider = mock(AcceptedPoliciesProvider.class); when(acceptedPoliciesProvider.getAcceptedPolicies()).thenReturn(List.of(new AcceptedPolicy(policy("IRS Policy", @@ -141,12 +133,9 @@ void configureSystemUnderTest(WireMockRuntimeInfo wireMockRuntimeInfo) { void shouldReturnAssemblyPartRelationshipAsString() throws EdcClientException, ExecutionException, InterruptedException { // Arrange - prepareNegotiation(); - givenThat(get(urlPathEqualTo(submodelDataplanePath)).willReturn(aResponse().withStatus(200) - .withHeader("Content-Type", - "application/json;charset=UTF-8") - .withBodyFile( - "singleLevelBomAsBuilt.json"))); + SubmodelFacadeWiremockConfig.prepareNegotiation(storage); + givenThat(get(urlPathEqualTo(submodelDataplanePath)).willReturn( + responseWithStatus(200).withBodyFile("singleLevelBomAsBuilt.json"))); // Act final String submodel = edcSubmodelClient.getSubmodelRawPayload(connectorEndpoint, submodelDataplaneUrl, @@ -156,69 +145,13 @@ void shouldReturnAssemblyPartRelationshipAsString() assertThat(submodel).contains("\"catenaXId\": \"urn:uuid:fe99da3d-b0de-4e80-81da-882aebcca978\""); } - private void prepareNegotiation() { - final var contentType = "application/json;charset=UTF-8"; - final var pathCatalog = "/catalog/request"; - final var pathNegotiate = "/contractnegotiations"; - final var pathTransfer = "/transferprocesses"; - givenThat(post(urlPathEqualTo(pathCatalog)).willReturn(aResponse().withStatus(200) - .withHeader("Content-Type", contentType) - .withBodyFile("edc/responseCatalog.json"))); - - givenThat(post(urlPathEqualTo(pathNegotiate)).willReturn(aResponse().withStatus(200) - .withHeader("Content-Type", contentType) - .withBodyFile( - "edc/responseStartNegotiation.json"))); - - final var negotiationId = "1bbaec6e-c316-4e1e-8258-c07a648cc43c"; - givenThat(get(urlPathEqualTo(pathNegotiate + "/" + negotiationId)).willReturn(aResponse().withStatus(200) - .withHeader( - "Content-Type", - contentType) - .withBodyFile( - "edc/responseGetNegotiationConfirmed.json"))); - givenThat(get(urlPathEqualTo(pathNegotiate + "/" + negotiationId + "/state")).willReturn( - aResponse().withStatus(200) - .withHeader("Content-Type", contentType) - .withBodyFile("edc/responseGetNegotiationState.json"))); - - givenThat(post(urlPathEqualTo(pathTransfer)).willReturn(aResponse().withStatus(200) - .withHeader("Content-Type", contentType) - .withBodyFile( - "edc/responseStartTransferprocess.json"))); - final var transferProcessId = "1b21e963-0bc5-422a-b30d-fd3511861d88"; - givenThat(get(urlPathEqualTo(pathTransfer + "/" + transferProcessId + "/state")).willReturn( - aResponse().withStatus(200) - .withHeader("Content-Type", contentType) - .withBodyFile("edc/responseGetTransferState.json"))); - givenThat(get(urlPathEqualTo(pathTransfer + "/" + transferProcessId)).willReturn(aResponse().withStatus(200) - .withHeader( - "Content-Type", - contentType) - .withBodyFile( - "edc/responseGetTransferConfirmed.json"))); - - final var contractAgreementId = "7681f966-36ea-4542-b5ea-0d0db81967de:5a7ab616-989f-46ae-bdf2-32027b9f6ee6-31b614f5-ec14-4ed2-a509-e7b7780083e7:a6144a2e-c1b1-4ec6-96e1-a221da134e4f"; - final EndpointDataReference ref = EndpointDataReference.Builder.newInstance() - .authKey("testkey") - .authCode("testcode") - .properties(Map.of(NAMESPACE_EDC_CID, - contractAgreementId)) - .endpoint("http://provider.dataplane/api/public") - .build(); - storage.put(contractAgreementId, ref); - } - @Test void shouldReturnMaterialForRecyclingAsString() throws EdcClientException, ExecutionException, InterruptedException { // Arrange - prepareNegotiation(); - givenThat(get(urlPathEqualTo(submodelDataplanePath)).willReturn(aResponse().withStatus(200) - .withHeader("Content-Type", - "application/json;charset=UTF-8") - .withBodyFile( - "materialForRecycling.json"))); + SubmodelFacadeWiremockConfig.prepareNegotiation(storage); + givenThat(get(urlPathEqualTo(submodelDataplanePath)).willReturn( + responseWithStatus(200).withBodyFile("materialForRecycling.json"))); // Act final String submodel = edcSubmodelClient.getSubmodelRawPayload(connectorEndpoint, submodelDataplaneUrl, @@ -232,11 +165,8 @@ void shouldReturnMaterialForRecyclingAsString() void shouldReturnObjectAsStringWhenResponseNotJSON() throws EdcClientException, ExecutionException, InterruptedException { // Arrange - prepareNegotiation(); - givenThat(get(urlPathEqualTo(submodelDataplanePath)).willReturn(aResponse().withStatus(200) - .withHeader("Content-Type", - "application/json;charset=UTF-8") - .withBody("test"))); + SubmodelFacadeWiremockConfig.prepareNegotiation(storage); + givenThat(get(urlPathEqualTo(submodelDataplanePath)).willReturn(responseWithStatus(200).withBody("test"))); // Act final String submodel = edcSubmodelClient.getSubmodelRawPayload(connectorEndpoint, submodelDataplaneUrl, @@ -259,27 +189,22 @@ void shouldThrowExceptionWhenPoliciesAreNotAccepted() { when(acceptedPoliciesProvider.getAcceptedPolicies()).thenReturn(List.of(acceptedPolicy)); - prepareNegotiation(); - givenThat(get(urlPathEqualTo(submodelDataplanePath)).willReturn(aResponse().withStatus(200) - .withHeader("Content-Type", - "application/json;charset=UTF-8") - .withBody("test"))); + SubmodelFacadeWiremockConfig.prepareNegotiation(storage); + givenThat(get(urlPathEqualTo(submodelDataplanePath)).willReturn(responseWithStatus(200).withBody("test"))); // Act & Assert final String errorMessage = "Consumption of asset '58505404-4da1-427a-82aa-b79482bcd1f0' is not permitted as the required catalog offer policies do not comply with defined IRS policies."; assertThatExceptionOfType(UsagePolicyException.class).isThrownBy( - () -> edcSubmodelClient.getSubmodelRawPayload(connectorEndpoint, submodelDataplaneUrl, assetId) - .get()).withMessageEndingWith(errorMessage); + () -> edcSubmodelClient.getSubmodelRawPayload(connectorEndpoint, submodelDataplaneUrl, assetId).get()) + .withMessageEndingWith(errorMessage); } @Test void shouldThrowExceptionWhenResponse_400() { // Arrange - prepareNegotiation(); - givenThat(get(urlPathEqualTo(submodelDataplanePath)).willReturn(aResponse().withStatus(400) - .withHeader("Content-Type", - "application/json;charset=UTF-8") - .withBody("{ error: '400'}"))); + SubmodelFacadeWiremockConfig.prepareNegotiation(storage); + givenThat(get(urlPathEqualTo(submodelDataplanePath)).willReturn( + responseWithStatus(400).withBody("{ error: '400'}"))); // Act final ThrowableAssert.ThrowingCallable throwingCallable = () -> edcSubmodelClient.getSubmodelRawPayload( @@ -293,11 +218,9 @@ void shouldThrowExceptionWhenResponse_400() { @Test void shouldThrowExceptionWhenResponse_500() { // Arrange - prepareNegotiation(); - givenThat(get(urlPathEqualTo(submodelDataplanePath)).willReturn(aResponse().withStatus(500) - .withHeader("Content-Type", - "application/json;charset=UTF-8") - .withBody("{ error: '500'}"))); + SubmodelFacadeWiremockConfig.prepareNegotiation(storage); + givenThat(get(urlPathEqualTo(submodelDataplanePath)).willReturn( + responseWithStatus(500).withBody("{ error: '500'}"))); // Act final ThrowableAssert.ThrowingCallable throwingCallable = () -> edcSubmodelClient.getSubmodelRawPayload( @@ -308,7 +231,6 @@ void shouldThrowExceptionWhenResponse_500() { .withCauseInstanceOf(RestClientException.class); } - private org.eclipse.tractusx.irs.edc.client.policy.Policy policy(String policyId, List permissions) { return new org.eclipse.tractusx.irs.edc.client.policy.Policy(policyId, OffsetDateTime.now(), OffsetDateTime.now().plusYears(1), permissions); diff --git a/irs-registry-client/src/test/java/org/eclipse/tractusx/irs/registryclient/decentral/DecentralDigitalTwinRegistryServiceWiremockTest.java b/irs-registry-client/src/test/java/org/eclipse/tractusx/irs/registryclient/decentral/DecentralDigitalTwinRegistryServiceWiremockTest.java index e00704f8c8..1072ea7e6d 100644 --- a/irs-registry-client/src/test/java/org/eclipse/tractusx/irs/registryclient/decentral/DecentralDigitalTwinRegistryServiceWiremockTest.java +++ b/irs-registry-client/src/test/java/org/eclipse/tractusx/irs/registryclient/decentral/DecentralDigitalTwinRegistryServiceWiremockTest.java @@ -27,24 +27,24 @@ import static com.github.tomakehurst.wiremock.client.WireMock.verify; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.eclipse.tractusx.irs.registryclient.decentral.DtrWiremockConfig.DATAPLANE_URL; -import static org.eclipse.tractusx.irs.registryclient.decentral.DtrWiremockConfig.DISCOVERY_FINDER_PATH; -import static org.eclipse.tractusx.irs.registryclient.decentral.DtrWiremockConfig.DISCOVERY_FINDER_URL; -import static org.eclipse.tractusx.irs.registryclient.decentral.DtrWiremockConfig.EDC_DISCOVERY_PATH; -import static org.eclipse.tractusx.irs.registryclient.decentral.DtrWiremockConfig.LOOKUP_SHELLS_PATH; -import static org.eclipse.tractusx.irs.registryclient.decentral.DtrWiremockConfig.LOOKUP_SHELLS_TEMPLATE; -import static org.eclipse.tractusx.irs.registryclient.decentral.DtrWiremockConfig.SHELL_DESCRIPTORS_PATH; -import static org.eclipse.tractusx.irs.registryclient.decentral.DtrWiremockConfig.SHELL_DESCRIPTORS_TEMPLATE; -import static org.eclipse.tractusx.irs.registryclient.decentral.DtrWiremockConfig.TEST_BPN; -import static org.eclipse.tractusx.irs.registryclient.decentral.DtrWiremockConfig.getLookupShells200; -import static org.eclipse.tractusx.irs.registryclient.decentral.DtrWiremockConfig.getLookupShells200Empty; -import static org.eclipse.tractusx.irs.registryclient.decentral.DtrWiremockConfig.getLookupShells404; -import static org.eclipse.tractusx.irs.registryclient.decentral.DtrWiremockConfig.getShellDescriptor200; -import static org.eclipse.tractusx.irs.registryclient.decentral.DtrWiremockConfig.getShellDescriptor404; -import static org.eclipse.tractusx.irs.registryclient.decentral.DtrWiremockConfig.postDiscoveryFinder200; -import static org.eclipse.tractusx.irs.registryclient.decentral.DtrWiremockConfig.postDiscoveryFinder404; -import static org.eclipse.tractusx.irs.registryclient.decentral.DtrWiremockConfig.postEdcDiscovery200; -import static org.eclipse.tractusx.irs.registryclient.decentral.DtrWiremockConfig.postEdcDiscovery404; +import static org.eclipse.tractusx.irs.testing.wiremock.DiscoveryServiceWiremockConfig.DISCOVERY_FINDER_PATH; +import static org.eclipse.tractusx.irs.testing.wiremock.DiscoveryServiceWiremockConfig.DISCOVERY_FINDER_URL; +import static org.eclipse.tractusx.irs.testing.wiremock.DiscoveryServiceWiremockConfig.EDC_DISCOVERY_PATH; +import static org.eclipse.tractusx.irs.testing.wiremock.DiscoveryServiceWiremockConfig.TEST_BPN; +import static org.eclipse.tractusx.irs.testing.wiremock.DiscoveryServiceWiremockConfig.postDiscoveryFinder200; +import static org.eclipse.tractusx.irs.testing.wiremock.DiscoveryServiceWiremockConfig.postDiscoveryFinder404; +import static org.eclipse.tractusx.irs.testing.wiremock.DiscoveryServiceWiremockConfig.postEdcDiscovery200; +import static org.eclipse.tractusx.irs.testing.wiremock.DiscoveryServiceWiremockConfig.postEdcDiscovery404; +import static org.eclipse.tractusx.irs.testing.wiremock.DtrWiremockConfig.DATAPLANE_URL; +import static org.eclipse.tractusx.irs.testing.wiremock.DtrWiremockConfig.LOOKUP_SHELLS_PATH; +import static org.eclipse.tractusx.irs.testing.wiremock.DtrWiremockConfig.LOOKUP_SHELLS_TEMPLATE; +import static org.eclipse.tractusx.irs.testing.wiremock.DtrWiremockConfig.SHELL_DESCRIPTORS_PATH; +import static org.eclipse.tractusx.irs.testing.wiremock.DtrWiremockConfig.SHELL_DESCRIPTORS_TEMPLATE; +import static org.eclipse.tractusx.irs.testing.wiremock.DtrWiremockConfig.getLookupShells200; +import static org.eclipse.tractusx.irs.testing.wiremock.DtrWiremockConfig.getLookupShells200Empty; +import static org.eclipse.tractusx.irs.testing.wiremock.DtrWiremockConfig.getLookupShells404; +import static org.eclipse.tractusx.irs.testing.wiremock.DtrWiremockConfig.getShellDescriptor200; +import static org.eclipse.tractusx.irs.testing.wiremock.DtrWiremockConfig.getShellDescriptor404; import static org.eclipse.tractusx.irs.testing.wiremock.WireMockConfig.restTemplateProxy; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; @@ -121,6 +121,7 @@ void shouldThrowHttpClientExceptionInCaseOfDiscoveryError() { final List testId = List.of(new DigitalTwinRegistryKey("testId", TEST_BPN)); // Act & Assert + // TODO fix implementation to not throw HttpClientErrorException$NotFound assertThatThrownBy(() -> decentralDigitalTwinRegistryService.fetchShells(testId)).isInstanceOf( HttpClientErrorException.class); verify(exactly(1), postRequestedFor(urlPathEqualTo(DISCOVERY_FINDER_PATH))); @@ -134,6 +135,7 @@ void shouldThrowHttpClientExceptionInCaseOfEdcDiscoveryError() { final List testId = List.of(new DigitalTwinRegistryKey("testId", TEST_BPN)); // Act & Assert + // TODO fix implementation to not throw HttpClientErrorException$NotFound assertThatThrownBy(() -> decentralDigitalTwinRegistryService.fetchShells(testId)).isInstanceOf( HttpClientErrorException.class); verify(exactly(1), postRequestedFor(urlPathEqualTo(DISCOVERY_FINDER_PATH))); @@ -149,6 +151,7 @@ void shouldThrowHttpClientExceptionInCaseOfLookupShellsError() { final List testId = List.of(new DigitalTwinRegistryKey("testId", TEST_BPN)); // Act & Assert + // TODO fix implementation to not throw HttpClientErrorException$NotFound assertThatThrownBy(() -> decentralDigitalTwinRegistryService.fetchShells(testId)).isInstanceOf( HttpClientErrorException.class); verify(exactly(1), postRequestedFor(urlPathEqualTo(DISCOVERY_FINDER_PATH))); @@ -166,6 +169,7 @@ void shouldThrowHttpClientExceptionInCaseOfShellDescriptorsError() { final List testId = List.of(new DigitalTwinRegistryKey("testId", TEST_BPN)); // Act & Assert + // TODO fix implementation to not throw HttpClientErrorException$NotFound assertThatThrownBy(() -> decentralDigitalTwinRegistryService.fetchShells(testId)).isInstanceOf( HttpClientErrorException.class); verify(exactly(1), postRequestedFor(urlPathEqualTo(DISCOVERY_FINDER_PATH))); @@ -184,11 +188,12 @@ void shouldThrowExceptionOnEmptyShells() { final List testId = List.of(new DigitalTwinRegistryKey("testId", TEST_BPN)); // Act & Assert + // TODO fix implementation to not throw HttpClientErrorException$NotFound assertThatThrownBy(() -> decentralDigitalTwinRegistryService.fetchShells(testId)).isInstanceOf( HttpClientErrorException.class); verify(exactly(1), postRequestedFor(urlPathEqualTo(DISCOVERY_FINDER_PATH))); verify(exactly(1), postRequestedFor(urlPathEqualTo(EDC_DISCOVERY_PATH))); verify(exactly(1), getRequestedFor(urlPathEqualTo(LOOKUP_SHELLS_PATH))); - verify(exactly(1), getRequestedFor(urlPathEqualTo(LOOKUP_SHELLS_PATH))); + verify(exactly(1), getRequestedFor(urlPathMatching(SHELL_DESCRIPTORS_PATH + ".*"))); } } diff --git a/irs-testing/src/main/java/org/eclipse/tractusx/irs/testing/wiremock/DiscoveryServiceWiremockConfig.java b/irs-testing/src/main/java/org/eclipse/tractusx/irs/testing/wiremock/DiscoveryServiceWiremockConfig.java new file mode 100644 index 0000000000..6130c1501d --- /dev/null +++ b/irs-testing/src/main/java/org/eclipse/tractusx/irs/testing/wiremock/DiscoveryServiceWiremockConfig.java @@ -0,0 +1,93 @@ +/******************************************************************************** + * Copyright (c) 2021,2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +package org.eclipse.tractusx.irs.testing.wiremock; + +import static com.github.tomakehurst.wiremock.client.WireMock.post; +import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; +import static org.eclipse.tractusx.irs.testing.wiremock.WireMockConfig.*; + +import java.util.List; + +import com.github.tomakehurst.wiremock.client.MappingBuilder; + +public class DiscoveryServiceWiremockConfig { + public static final String CONTROLPLANE_PUBLIC_URL = "https://controlplane.test"; + public static final String EDC_DISCOVERY_PATH = "/edcDiscovery"; + public static final String TEST_BPN = "BPNL00000000TEST"; + public static final String DISCOVERY_FINDER_PATH = "/discoveryFinder"; + public static final String DISCOVERY_HOST = "http://discovery.finder"; + public static final String EDC_DISCOVERY_URL = DISCOVERY_HOST + EDC_DISCOVERY_PATH; + public static final String DISCOVERY_FINDER_URL = DISCOVERY_HOST + DiscoveryServiceWiremockConfig.DISCOVERY_FINDER_PATH; + + public static MappingBuilder postEdcDiscovery200() { + return post(urlPathEqualTo(EDC_DISCOVERY_PATH)).willReturn( + responseWithStatus(200).withBody(edcDiscoveryResponse(TEST_BPN, List.of(CONTROLPLANE_PUBLIC_URL)))); + } + + public static String edcDiscoveryResponse(final String bpn, final List connectorEndpoints) { + return """ + [ + { + "bpn": "%s", + "connectorEndpoint": [ + %s + ] + } + ] + """.formatted(bpn, String.join(",\n", connectorEndpoints.stream().map(s -> "\"" + s + "\"").toList())); + } + + public static MappingBuilder postDiscoveryFinder200() { + return post(urlPathEqualTo(DISCOVERY_FINDER_PATH)).willReturn( + responseWithStatus(200).withBody(discoveryFinderResponse(EDC_DISCOVERY_URL))); + } + + public static String discoveryFinderResponse(final String discoveryFinderUrl) { + return """ + { + "endpoints": [ + { + "type": "bpn", + "description": "Service to discover EDC to a particular BPN", + "endpointAddress": "%s", + "documentation": "http://.../swagger/index.html", + "resourceId": "316417cd-0fb5-4daf-8dfa-8f68125923f1" + } + ] + } + """.formatted(discoveryFinderUrl); + } + + public static String discoveryFinderEmtpyResponse() { + return """ + { + "endpoints": [ + ] + } + """; + } + + public static MappingBuilder postDiscoveryFinder404() { + return post(urlPathEqualTo(DISCOVERY_FINDER_PATH)).willReturn(responseWithStatus(404)); + } + + public static MappingBuilder postEdcDiscovery404() { + return post(urlPathEqualTo(EDC_DISCOVERY_PATH)).willReturn(responseWithStatus(404)); + } +} diff --git a/irs-registry-client/src/test/java/org/eclipse/tractusx/irs/registryclient/decentral/DtrWiremockConfig.java b/irs-testing/src/main/java/org/eclipse/tractusx/irs/testing/wiremock/DtrWiremockConfig.java similarity index 58% rename from irs-registry-client/src/test/java/org/eclipse/tractusx/irs/registryclient/decentral/DtrWiremockConfig.java rename to irs-testing/src/main/java/org/eclipse/tractusx/irs/testing/wiremock/DtrWiremockConfig.java index 00f1719e82..4f29f1fbd1 100644 --- a/irs-registry-client/src/test/java/org/eclipse/tractusx/irs/registryclient/decentral/DtrWiremockConfig.java +++ b/irs-testing/src/main/java/org/eclipse/tractusx/irs/testing/wiremock/DtrWiremockConfig.java @@ -16,60 +16,54 @@ * * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -package org.eclipse.tractusx.irs.registryclient.decentral; +package org.eclipse.tractusx.irs.testing.wiremock; -import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; 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.urlPathEqualTo; import static com.github.tomakehurst.wiremock.client.WireMock.urlPathMatching; +import static org.eclipse.tractusx.irs.testing.wiremock.WireMockConfig.responseWithStatus; import java.util.List; import com.github.tomakehurst.wiremock.client.MappingBuilder; -import com.github.tomakehurst.wiremock.client.ResponseDefinitionBuilder; public class DtrWiremockConfig { + public static final String DATAPLANE_URL = "http://dataplane.test"; + public static final String DATAPLANE_PUBLIC_URL = DATAPLANE_URL + "/api/public/data/"; public static final String SHELL_DESCRIPTORS_PATH = "/shell-descriptors/"; public static final String SHELL_DESCRIPTORS_TEMPLATE = SHELL_DESCRIPTORS_PATH + "{aasIdentifier}"; public static final String LOOKUP_SHELLS_PATH = "/lookup/shells"; public static final String LOOKUP_SHELLS_TEMPLATE = LOOKUP_SHELLS_PATH + "?assetIds={assetIds}"; - public static final String DISCOVERY_FINDER_PATH = "/discoveryFinder"; - public static final String EDC_DISCOVERY_PATH = "/edcDiscovery"; - public static final String TEST_BPN = "BPNL00000000TEST"; - public static final String DISCOVERY_HOST = "http://discovery.finder"; - public static final String DATAPLANE_URL = "http://dataplane.test"; - public static final String DISCOVERY_FINDER_URL = DISCOVERY_HOST + DISCOVERY_FINDER_PATH; - static ResponseDefinitionBuilder responseWithStatus(final int statusCode) { - return aResponse().withStatus(statusCode).withHeader("Content-Type", "application/json;charset=UTF-8"); + private DtrWiremockConfig() { } - static MappingBuilder getShellDescriptor200() { - final String materialForRecycling = submodel("https://dataplane.test/api/public/data/", - "urn:uuid:19b0338f-6d03-4198-b3b8-5c43f8958d60", "https://controlplane.test", "MaterialForRecycling", - "urn:uuid:cf06d5d5-e3f8-4bd4-bfcf-81815310701f", + public static MappingBuilder getShellDescriptor200() { + final String materialForRecycling = submodelDescriptor(DATAPLANE_PUBLIC_URL, + "urn:uuid:19b0338f-6d03-4198-b3b8-5c43f8958d60", DiscoveryServiceWiremockConfig.CONTROLPLANE_PUBLIC_URL, + "MaterialForRecycling", "urn:uuid:cf06d5d5-e3f8-4bd4-bfcf-81815310701f", "urn:bamm:io.catenax.material_for_recycling:1.1.0#MaterialForRecycling"); - final String batch = submodel("https://dataplane.test/api/public/data/", - "urn:uuid:234edd2f-0223-47c7-9fe4-3984ab14c4f9", "https://controlplane.test", "Batch", + final String batch = submodelDescriptor(DATAPLANE_PUBLIC_URL, "urn:uuid:234edd2f-0223-47c7-9fe4-3984ab14c4f9", + DiscoveryServiceWiremockConfig.CONTROLPLANE_PUBLIC_URL, "Batch", "urn:uuid:f53db6ef-7a58-4326-9169-0ae198b85dbf", "urn:samm:io.catenax.batch:2.0.0#Batch"); - final String singleLevelUsageAsBuilt = submodel("https://dataplane.test/api/public/data/", - "urn:uuid:f8196d6a-1664-4531-bdee-f15dbb1daf26", "https://controlplane.test", "SingleLevelUsageAsBuilt", - "urn:uuid:e2899f43-eca8-4aec-b877-4a69691f0487", + final String singleLevelUsageAsBuilt = submodelDescriptor(DATAPLANE_PUBLIC_URL, + "urn:uuid:f8196d6a-1664-4531-bdee-f15dbb1daf26", DiscoveryServiceWiremockConfig.CONTROLPLANE_PUBLIC_URL, + "SingleLevelUsageAsBuilt", "urn:uuid:e2899f43-eca8-4aec-b877-4a69691f0487", "urn:bamm:io.catenax.single_level_usage_as_built:2.0.0#SingleLevelUsageAsBuilt"); final List submodelDescriptors = List.of(materialForRecycling, batch, singleLevelUsageAsBuilt); final List specificAssetIds = List.of(specificAssetId("manufacturerId", "BPNL00000003B0Q0"), specificAssetId("batchId", "BID12345678")); return get(urlPathMatching(SHELL_DESCRIPTORS_PATH + ".*")).willReturn(responseWithStatus(200).withBody( - assetAdministrationShell(submodelDescriptors, "urn:uuid:7e4541ea-bb0f-464c-8cb3-021abccbfaf5", + assetAdministrationShellResponse(submodelDescriptors, "urn:uuid:7e4541ea-bb0f-464c-8cb3-021abccbfaf5", "EngineeringPlastics", "urn:uuid:9ce43b21-75e3-4cea-b13e-9a34f4f6822a", specificAssetIds))); } - public static String assetAdministrationShell(final List submodelDescriptors, final String globalAssetId, - final String idShort, final String shellId, final List specificAssetIds) { + public static String assetAdministrationShellResponse(final List submodelDescriptors, + final String globalAssetId, final String idShort, final String shellId, + final List specificAssetIds) { return """ { "description": [], @@ -107,7 +101,7 @@ public static String specificAssetId(final String key, final String value) { """.formatted(key, value); } - public static String submodel(final String dataplaneUrl, final String assetId, final String dspEndpoint, + public static String submodelDescriptor(final String dataplaneUrl, final String assetId, final String dspEndpoint, final String idShort, final String submodelDescriptorId, final String semanticId) { final String href = dataplaneUrl + submodelDescriptorId; return """ @@ -152,73 +146,17 @@ public static String submodel(final String dataplaneUrl, final String assetId, f """.formatted(href, assetId, dspEndpoint, idShort, submodelDescriptorId, semanticId); } - static MappingBuilder postEdcDiscovery200() { - return post(urlPathEqualTo(EDC_DISCOVERY_PATH)).willReturn( - responseWithStatus(200).withBody(edcDiscovery("BPNL00000000TEST", List.of("http://edc.test/edc")))); - } - - public static String edcDiscovery(final String bpn, final List connectorEndpoints) { - return """ - [ - { - "bpn": "%s", - "connectorEndpoint": [ - %s - ] - } - ] - """.formatted(bpn, String.join(",\n", connectorEndpoints.stream().map(s -> "\"" + s + "\"").toList())); - } - - static MappingBuilder postDiscoveryFinder200() { - return post(urlPathEqualTo(DISCOVERY_FINDER_PATH)).willReturn( - responseWithStatus(200).withBody(discoveryFinder("http://discovery.finder/edcDiscovery"))); - } - - public static String discoveryFinder(final String discoveryFinderUrl) { - return """ - { - "endpoints": [ - { - "type": "bpn", - "description": "Service to discover EDC to a particular BPN", - "endpointAddress": "%s", - "documentation": "http://.../swagger/index.html", - "resourceId": "316417cd-0fb5-4daf-8dfa-8f68125923f1" - } - ] - } - """.formatted(discoveryFinderUrl); - } - - private static String discoveryFinderEmtpy() { - return """ - { - "endpoints": [ - ] - } - """; - } - - static MappingBuilder postDiscoveryFinder404() { - return post(urlPathEqualTo(DISCOVERY_FINDER_PATH)).willReturn(responseWithStatus(404)); - } - - static MappingBuilder postEdcDiscovery404() { - return post(urlPathEqualTo(EDC_DISCOVERY_PATH)).willReturn(responseWithStatus(404)); - } - - static MappingBuilder getLookupShells200() { + public static MappingBuilder getLookupShells200() { return get(urlPathEqualTo(LOOKUP_SHELLS_PATH)).willReturn(responseWithStatus(200).withBody( - lookupShells(List.of("urn:uuid:21f7ebea-fa8a-410c-a656-bd9082e67dcf")))); + lookupShellsResponse(List.of("urn:uuid:21f7ebea-fa8a-410c-a656-bd9082e67dcf")))); } - static MappingBuilder getLookupShells200Empty() { + public static MappingBuilder getLookupShells200Empty() { return get(urlPathMatching(LOOKUP_SHELLS_PATH + ".*")).willReturn( - responseWithStatus(200).withBody(lookupShells(List.of()))); + responseWithStatus(200).withBody(lookupShellsResponse(List.of()))); } - public static String lookupShells(final List shellIds) { + public static String lookupShellsResponse(final List shellIds) { return """ { "paging_metadata": {}, @@ -229,11 +167,11 @@ public static String lookupShells(final List shellIds) { """.formatted(String.join(",\n", shellIds.stream().map(s -> "\"" + s + "\"").toList())); } - static MappingBuilder getLookupShells404() { + public static MappingBuilder getLookupShells404() { return get(urlPathEqualTo(LOOKUP_SHELLS_PATH)).willReturn(responseWithStatus(404)); } - static MappingBuilder getShellDescriptor404() { + public static MappingBuilder getShellDescriptor404() { return get(urlPathMatching(SHELL_DESCRIPTORS_PATH + ".*")).willReturn(responseWithStatus(404)); } } \ No newline at end of file diff --git a/irs-testing/src/main/java/org/eclipse/tractusx/irs/testing/wiremock/WireMockConfig.java b/irs-testing/src/main/java/org/eclipse/tractusx/irs/testing/wiremock/WireMockConfig.java index 9ff8b9562c..21598577df 100644 --- a/irs-testing/src/main/java/org/eclipse/tractusx/irs/testing/wiremock/WireMockConfig.java +++ b/irs-testing/src/main/java/org/eclipse/tractusx/irs/testing/wiremock/WireMockConfig.java @@ -18,9 +18,12 @@ ********************************************************************************/ package org.eclipse.tractusx.irs.testing.wiremock; +import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; + import java.net.InetSocketAddress; import java.net.Proxy; +import com.github.tomakehurst.wiremock.client.ResponseDefinitionBuilder; import org.springframework.http.client.SimpleClientHttpRequestFactory; import org.springframework.web.client.RestTemplate; @@ -44,4 +47,8 @@ public static RestTemplate restTemplateProxy(final String proxyServerHost, final requestFactory.setProxy(proxy); return new RestTemplate(requestFactory); } + + public static ResponseDefinitionBuilder responseWithStatus(final int statusCode) { + return aResponse().withStatus(statusCode).withHeader("Content-Type", "application/json;charset=UTF-8"); + } }