From d1fd503620a36f1d0272f0928730d2ed07a8bc73 Mon Sep 17 00:00:00 2001 From: Jaro Hartmann Date: Fri, 26 Jan 2024 14:49:21 +0100 Subject: [PATCH] feat(irs-api):[#344] Add Irs WireMock Test simulating one level --- .../eclipse/tractusx/irs/IrsWireMockIT.java | 189 ++++++++++++++++-- .../tractusx/irs/WireMockTestConfig.java | 28 +++ .../tractusx/irs/bpdm/BpdmWireMockConfig.java | 86 ++++++++ .../tractusx/irs/bpdm/BpdmWiremockTest.java | 60 +----- .../SemanticHubWireMockConfig.java | 52 +++++ .../semanticshub/SemanticHubWiremockTest.java | 11 +- .../__files/integrationtesting/batch-1.json | 22 ++ .../singleLevelBomAsBuilt-1.json | 16 ++ .../singleLevelBomAsBuilt-2.0.0-schema.json | 102 ++++++++++ .../client/SubmodelFacadeWiremockTest.java | 87 +++++--- .../DiscoveryServiceWiremockConfig.java | 28 +-- .../testing/wiremock/DtrWiremockConfig.java | 34 +++- .../SubmodelFacadeWiremockConfig.java | 99 ++++----- 13 files changed, 620 insertions(+), 194 deletions(-) create mode 100644 irs-api/src/test/java/org/eclipse/tractusx/irs/bpdm/BpdmWireMockConfig.java create mode 100644 irs-api/src/test/java/org/eclipse/tractusx/irs/semanticshub/SemanticHubWireMockConfig.java create mode 100644 irs-api/src/test/resources/__files/integrationtesting/batch-1.json create mode 100644 irs-api/src/test/resources/__files/integrationtesting/singleLevelBomAsBuilt-1.json create mode 100644 irs-api/src/test/resources/__files/semantichub/singleLevelBomAsBuilt-2.0.0-schema.json rename {irs-edc-client/src/test/java/org/eclipse/tractusx/irs/edc/client => irs-testing/src/main/java/org/eclipse/tractusx/irs/testing/wiremock}/SubmodelFacadeWiremockConfig.java (67%) diff --git a/irs-api/src/test/java/org/eclipse/tractusx/irs/IrsWireMockIT.java b/irs-api/src/test/java/org/eclipse/tractusx/irs/IrsWireMockIT.java index d7970bdee4..7de5396ebe 100644 --- a/irs-api/src/test/java/org/eclipse/tractusx/irs/IrsWireMockIT.java +++ b/irs-api/src/test/java/org/eclipse/tractusx/irs/IrsWireMockIT.java @@ -23,24 +23,62 @@ ********************************************************************************/ package org.eclipse.tractusx.irs; -import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.containing; 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.postRequestedFor; +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.eclipse.tractusx.irs.WireMockTestConfig.createEndpointDataReference; +import static org.eclipse.tractusx.irs.bpdm.BpdmWireMockConfig.bpdmResponse; +import static org.eclipse.tractusx.irs.semanticshub.SemanticHubWireMockConfig.batchSchemaResponse200; +import static org.eclipse.tractusx.irs.semanticshub.SemanticHubWireMockConfig.singleLevelBomAsBuiltSchemaResponse200; +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.postEdcDiscovery200; +import static org.eclipse.tractusx.irs.testing.wiremock.DtrWiremockConfig.DATAPLANE_PUBLIC_PATH; +import static org.eclipse.tractusx.irs.testing.wiremock.DtrWiremockConfig.LOOKUP_SHELLS_TEMPLATE; +import static org.eclipse.tractusx.irs.testing.wiremock.DtrWiremockConfig.PUBLIC_LOOKUP_SHELLS_PATH; +import static org.eclipse.tractusx.irs.testing.wiremock.DtrWiremockConfig.PUBLIC_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.getShellDescriptor200; +import static org.eclipse.tractusx.irs.testing.wiremock.DtrWiremockConfig.lookupShellsResponse; +import static org.eclipse.tractusx.irs.testing.wiremock.SubmodelFacadeWiremockConfig.PATH_CATALOG; +import static org.eclipse.tractusx.irs.testing.wiremock.SubmodelFacadeWiremockConfig.PATH_NEGOTIATE; +import static org.eclipse.tractusx.irs.testing.wiremock.SubmodelFacadeWiremockConfig.PATH_STATE; +import static org.eclipse.tractusx.irs.testing.wiremock.SubmodelFacadeWiremockConfig.PATH_TRANSFER; +import static org.eclipse.tractusx.irs.testing.wiremock.WireMockConfig.responseWithStatus; +import java.nio.charset.StandardCharsets; +import java.time.Duration; import java.util.List; import com.github.tomakehurst.wiremock.junit5.WireMockTest; +import org.apache.commons.codec.binary.Base64; +import org.awaitility.Awaitility; +import org.eclipse.tractusx.irs.bpdm.BpdmWireMockConfig; import org.eclipse.tractusx.irs.component.JobHandle; +import org.eclipse.tractusx.irs.component.Jobs; import org.eclipse.tractusx.irs.component.PartChainIdentificationKey; import org.eclipse.tractusx.irs.component.RegisterJob; import org.eclipse.tractusx.irs.component.enums.Direction; +import org.eclipse.tractusx.irs.component.enums.JobState; +import org.eclipse.tractusx.irs.edc.client.EndpointDataReferenceStorage; import org.eclipse.tractusx.irs.semanticshub.AspectModels; +import org.eclipse.tractusx.irs.semanticshub.SemanticHubWireMockConfig; import org.eclipse.tractusx.irs.services.IrsItemGraphQueryService; import org.eclipse.tractusx.irs.services.SemanticHubService; import org.eclipse.tractusx.irs.services.validation.SchemaNotFoundException; import org.eclipse.tractusx.irs.testing.containers.MinioContainer; +import org.eclipse.tractusx.irs.testing.wiremock.SubmodelFacadeWiremockConfig; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -57,34 +95,29 @@ @WireMockTest(httpPort = 8085) @Testcontainers -@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = WireMockTestConfig.class - /*, properties = "spring.main.allow-bean-definition-overriding=true"*/) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = WireMockTestConfig.class) @ContextConfiguration(initializers = IrsWireMockIT.MinioConfigInitializer.class) -//@Import({ WireMockTestConfig.class }) @ActiveProfiles("integrationtest") class IrsWireMockIT { - public static final String BPDM_TEST = "http://bpdm.test/legal-entities/{partnerId}?idType={idType}"; - public static final String DISCOVERY_TEST = "http://discovery.test"; public static final String SEMANTIC_HUB_URL = "http://semantic.hub/models"; - public static final String SEMANTIC_HUB_SCHEMA_URL = "http://semantic.hub/models/{urn}/json-schema"; + public static final String EDC_URL = "http://edc.test"; private static final String ACCESS_KEY = "accessKey"; private static final String SECRET_KEY = "secretKey"; private static final MinioContainer minioContainer = new MinioContainer( new MinioContainer.CredentialsProvider(ACCESS_KEY, SECRET_KEY)).withReuse(true); - public static final String EDC_URL = "http://edc.test:8081/management"; @Autowired private IrsItemGraphQueryService irsService; @Autowired private SemanticHubService semanticHubService; + @Autowired + private EndpointDataReferenceStorage endpointDataReferenceStorage; @BeforeAll static void startContainer() { minioContainer.start(); - givenThat(get(urlPathEqualTo("/models")).willReturn(aResponse().withStatus(200) - .withHeader("Content-Type", - "application/json;charset=UTF-8") - .withBodyFile("all-models-page-IT.json"))); + givenThat(get(urlPathEqualTo("/models")).willReturn( + responseWithStatus(200).withBodyFile("semantichub/all-models-page-IT.json"))); } @AfterAll @@ -94,11 +127,21 @@ static void stopContainer() { @DynamicPropertySource static void configureProperties(DynamicPropertyRegistry registry) { - registry.add("bpdm.bpnEndpoint", () -> BPDM_TEST); - registry.add("digitalTwinRegistry.discoveryFinderUrl", () -> DISCOVERY_TEST); + registry.add("bpdm.bpnEndpoint", () -> BpdmWireMockConfig.BPDM_TEST); + registry.add("digitalTwinRegistry.discoveryFinderUrl", () -> DISCOVERY_FINDER_URL); + registry.add("digitalTwinRegistry.shellDescriptorTemplate", () -> SHELL_DESCRIPTORS_TEMPLATE); + registry.add("digitalTwinRegistry.lookupShellsTemplate", () -> LOOKUP_SHELLS_TEMPLATE); + registry.add("digitalTwinRegistry.type", () -> "decentral"); registry.add("semanticshub.url", () -> SEMANTIC_HUB_URL); - registry.add("semanticshub.modelJsonSchemaEndpoint", () -> SEMANTIC_HUB_SCHEMA_URL); + registry.add("semanticshub.modelJsonSchemaEndpoint", () -> SemanticHubWireMockConfig.SEMANTIC_HUB_SCHEMA_URL); registry.add("irs-edc-client.controlplane.endpoint.data", () -> EDC_URL); + registry.add("irs-edc-client.controlplane.endpoint.catalog", () -> PATH_CATALOG); + registry.add("irs-edc-client.controlplane.endpoint.contract-negotiation", () -> PATH_NEGOTIATE); + registry.add("irs-edc-client.controlplane.endpoint.transfer-process", () -> PATH_TRANSFER); + registry.add("irs-edc-client.controlplane.endpoint.state-suffix", () -> PATH_STATE); + registry.add("irs-edc-client.controlplane.api-key.header", () -> "X-Api-Key"); + registry.add("irs-edc-client.controlplane.api-key.secret", () -> "test"); + registry.add("resilience4j.retry.configs.default.waitDuration", () -> "1s"); } @Test @@ -113,13 +156,25 @@ void shouldStartApplicationAndCollectSemanticModels() throws SchemaNotFoundExcep @Test void shouldStartJob() { // Arrange + final String startId = "globalAssetId"; + + successfulSemanticHubRequests(); + successfulDiscovery(); + successfulRegistryNegotiation(); + successfulDtrRequest(startId); + successfulAssetNegotiation(); + + successfulDataRequests(); + + successfulBpdmRequests(); + final RegisterJob request = RegisterJob.builder() .key(PartChainIdentificationKey.builder() - .bpn("BPNTEST") - .globalAssetId("globalAssetId") + .bpn(TEST_BPN) + .globalAssetId(startId) .build()) - .depth(2) - .aspects(List.of("SerialPart", "Batch", "SingleLevelBomAsBuilt")) + .depth(1) + .aspects(List.of("Batch", "SingleLevelBomAsBuilt")) .collectAspects(true) .lookupBPNs(true) .direction(Direction.DOWNWARD) @@ -127,9 +182,103 @@ void shouldStartJob() { // Act final JobHandle jobHandle = irsService.registerItemJob(request); + assertThat(jobHandle.getId()).isNotNull(); + Awaitility.await() + .timeout(Duration.ofSeconds(35)) + .pollInterval(Duration.ofSeconds(1)) + .until(() -> irsService.getJobForJobId(jobHandle.getId(), false) + .getJob() + .getState() + .equals(JobState.COMPLETED)); + + Jobs jobForJobId = irsService.getJobForJobId(jobHandle.getId(), true); // Assert - assertThat(jobHandle.getId()).isNotNull(); + verifyDiscoveryCalls(1); + verifyNegotiationCalls(3); + + assertThat(jobForJobId.getJob().getState()).isEqualTo(JobState.COMPLETED); + assertThat(jobForJobId.getShells()).hasSize(2); + assertThat(jobForJobId.getRelationships()).hasSize(1); + assertThat(jobForJobId.getTombstones()).isEmpty(); + } + + private void successfulRegistryNegotiation() { + final String negotiationId = "1bbaec6e-c316-4e1e-8258-c07a648cc43c"; + final String transferProcessId = "1b21e963-0bc5-422a-b30d-fd3511861d88"; + final String edcAssetId = "registry-asset"; + final String contractAgreementId = SubmodelFacadeWiremockConfig.prepareNegotiation(negotiationId, + transferProcessId, + "7681f966-36ea-4542-b5ea-0d0db81967de:registry-asset:a6144a2e-c1b1-4ec6-96e1-a221da134e4f", edcAssetId); + endpointDataReferenceStorage.put(contractAgreementId, createEndpointDataReference(contractAgreementId)); + } + + private void successfulAssetNegotiation() { + final String negotiationId = "1bbaec6e-c316-4e1e-8258-c07a648cc43c"; + final String transferProcessId = "1b21e963-0bc5-422a-b30d-fd3511861d88"; + final String edcAssetId = "urn:uuid:f8196d6a-1664-4531-bdee-f15dbb1daf26"; + final String contractAgreementId = SubmodelFacadeWiremockConfig.prepareNegotiation(negotiationId, + transferProcessId, + "7681f966-36ea-4542-b5ea-0d0db81967de:urn:uuid:f8196d6a-1664-4531-bdee-f15dbb1daf26:a6144a2e-c1b1-4ec6-96e1-a221da134e4f", + edcAssetId); + endpointDataReferenceStorage.put(contractAgreementId, createEndpointDataReference(contractAgreementId)); + } + + private static void successfulDiscovery() { + stubFor(postDiscoveryFinder200()); + stubFor(postEdcDiscovery200()); + } + + private static String encodedId(final String secondDTR) { + return Base64.encodeBase64String(secondDTR.getBytes(StandardCharsets.UTF_8)); + } + + private static void verifyDiscoveryCalls(final int times) { + verify(times, postRequestedFor(urlPathEqualTo(DISCOVERY_FINDER_PATH))); + verify(times, postRequestedFor(urlPathEqualTo(EDC_DISCOVERY_PATH))); + } + + private static void verifyNegotiationCalls(final int times) { + verify(times, postRequestedFor(urlPathEqualTo(PATH_NEGOTIATE))); + verify(times, postRequestedFor(urlPathEqualTo(PATH_CATALOG))); + verify(times * 2, getRequestedFor(urlPathMatching(PATH_NEGOTIATE + "/.*"))); + verify(times, getRequestedFor(urlPathMatching(PATH_NEGOTIATE + "/.*" + PATH_STATE))); + verify(times, postRequestedFor(urlPathEqualTo(PATH_TRANSFER))); + verify(times * 2, getRequestedFor(urlPathMatching(PATH_TRANSFER + "/.*"))); + verify(times, getRequestedFor(urlPathMatching(PATH_TRANSFER + "/.*" + PATH_STATE))); + } + + private static void successfulBpdmRequests() { + stubFor(get(urlPathMatching("/legal-entities/.*")).willReturn( + responseWithStatus(200).withBody(bpdmResponse(TEST_BPN, "Company Name")))); + } + + private static void successfulDataRequests() { + givenThat(get(urlPathMatching( + DATAPLANE_PUBLIC_PATH + "/urn:uuid:f53db6ef-7a58-4326-9169-0ae198b85dbf")).willReturn( + responseWithStatus(200).withBodyFile("integrationtesting/batch-1.json"))); + givenThat(get(urlPathMatching( + DATAPLANE_PUBLIC_PATH + "/urn:uuid:0e413809-966b-4107-aae5-aeb28bcdaadf")).willReturn( + responseWithStatus(200).withBodyFile("integrationtesting/singleLevelBomAsBuilt-1.json"))); + } + + private static void successfulSemanticHubRequests() { + stubFor(batchSchemaResponse200()); + stubFor(singleLevelBomAsBuiltSchemaResponse200()); + } + + private static void successfulDtrRequest(final String startId) { + stubFor(getLookupShells200(PUBLIC_LOOKUP_SHELLS_PATH).withQueryParam("assetIds", containing(startId))); + stubFor(getShellDescriptor200( + PUBLIC_SHELL_DESCRIPTORS_PATH + encodedId("urn:uuid:21f7ebea-fa8a-410c-a656-bd9082e67dcf"))); + + final String secondDTR = "urn:uuid:6d505432-8b31-4966-9514-4b753372683f"; + stubFor(get(urlPathEqualTo(PUBLIC_LOOKUP_SHELLS_PATH)).withQueryParam("assetIds", + containing("urn:uuid:7e4541ea-bb0f-464c-8cb3-021abccbfaf5")) + .willReturn(responseWithStatus(200).withBody( + lookupShellsResponse(List.of(secondDTR))))); + + stubFor(getShellDescriptor200(PUBLIC_SHELL_DESCRIPTORS_PATH + encodedId(secondDTR))); } public static class MinioConfigInitializer 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 16e9e92c3c..5cc1adcf9d 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 @@ -24,12 +24,21 @@ import static org.eclipse.tractusx.irs.configuration.RestTemplateConfig.EDC_REST_TEMPLATE; import static org.eclipse.tractusx.irs.configuration.RestTemplateConfig.NO_ERROR_REST_TEMPLATE; import static org.eclipse.tractusx.irs.configuration.RestTemplateConfig.SEMHUB_REST_TEMPLATE; +import static org.eclipse.tractusx.irs.testing.wiremock.SubmodelFacadeWiremockConfig.DATAPLANE_HOST; +import static org.eclipse.tractusx.irs.testing.wiremock.SubmodelFacadeWiremockConfig.PATH_DATAPLANE_PUBLIC; import static org.eclipse.tractusx.irs.testing.wiremock.WireMockConfig.restTemplateProxy; +import java.nio.charset.StandardCharsets; +import java.util.Base64; import java.util.List; +import java.util.Map; import com.fasterxml.jackson.databind.ObjectMapper; import org.eclipse.edc.policy.model.PolicyRegistrationTypes; +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.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.test.context.TestConfiguration; import org.springframework.context.annotation.Bean; @@ -44,6 +53,25 @@ public class WireMockTestConfig { public static final int HTTP_PORT = 8085; private static final String PROXY_SERVER_HOST = "127.0.0.1"; + 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(); + } + @Primary @Profile("integrationtest") @Bean(DTR_REST_TEMPLATE) diff --git a/irs-api/src/test/java/org/eclipse/tractusx/irs/bpdm/BpdmWireMockConfig.java b/irs-api/src/test/java/org/eclipse/tractusx/irs/bpdm/BpdmWireMockConfig.java new file mode 100644 index 0000000000..8a1089c5ce --- /dev/null +++ b/irs-api/src/test/java/org/eclipse/tractusx/irs/bpdm/BpdmWireMockConfig.java @@ -0,0 +1,86 @@ +/******************************************************************************** + * 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.bpdm; + +/** + * WireMock configurations and requests used for testing BPDM flow. + */ +public final class BpdmWireMockConfig { + + public static final String BPDM_TEST = "http://bpdm.test/legal-entities/{partnerId}?idType={idType}"; + + private BpdmWireMockConfig() { + } + + public static String bpdmResponse(final String bpn, final String companyName) { + return """ + { + "bpn": "%s", + "identifiers": [ + { + "value": "%s", + "type": { + "technicalKey": "BPN", + "name": "Business Partner Number", + "url": "" + }, + "issuingBody": { + "technicalKey": "CATENAX", + "name": "Catena-X", + "url": "" + }, + "status": { + "technicalKey": "UNKNOWN", + "name": "Unknown" + } + } + ], + "names": [ + { + "value": "%s", + "shortName": null, + "type": { + "technicalKey": "OTHER", + "name": "Any other alternative name used for a company, such as a specific language variant.", + "url": "" + }, + "language": { + "technicalKey": "undefined", + "name": "Undefined" + } + } + ], + "legalForm": null, + "status": null, + "profileClassifications": [], + "types": [ + { + "technicalKey": "UNKNOWN", + "name": "Unknown", + "url": "" + } + ], + "bankAccounts": [], + "roles": [], + "relations": [], + "currentness": "2022-07-26T08:17:38.737578Z" + } + """.formatted(bpn, bpn, companyName); + } +} \ No newline at end of file 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 e9e107cd8d..fc963f6163 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 @@ -18,12 +18,13 @@ ********************************************************************************/ package org.eclipse.tractusx.irs.bpdm; -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.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.bpdm.BpdmWireMockConfig.BPDM_TEST; +import static org.eclipse.tractusx.irs.bpdm.BpdmWireMockConfig.bpdmResponse; import static org.eclipse.tractusx.irs.testing.wiremock.WireMockConfig.responseWithStatus; import static org.eclipse.tractusx.irs.testing.wiremock.WireMockConfig.restTemplateProxy; @@ -45,67 +46,14 @@ class BpdmWiremockTest { void setUp(WireMockRuntimeInfo wireMockRuntimeInfo) { final RestTemplate restTemplate = restTemplateProxy(PROXY_SERVER_HOST, wireMockRuntimeInfo.getHttpPort()); - bpdmFacade = new BpdmFacade( - new BpdmClientImpl(restTemplate, "http://bpdm.test/legal-entities/{partnerId}?idType={idType}")); + bpdmFacade = new BpdmFacade(new BpdmClientImpl(restTemplate, BPDM_TEST)); } @Test void shouldResolveManufacturerName() { // Arrange givenThat(get(urlPathEqualTo("/legal-entities/BPNL00000000TEST")).willReturn( - aResponse().withStatus(200).withHeader("Content-Type", "application/json;charset=UTF-8").withBody(""" - { - "bpn": "BPNL00000000TEST", - "identifiers": [ - { - "value": "BPNL00000000TEST", - "type": { - "technicalKey": "BPN", - "name": "Business Partner Number", - "url": "" - }, - "issuingBody": { - "technicalKey": "CATENAX", - "name": "Catena-X", - "url": "" - }, - "status": { - "technicalKey": "UNKNOWN", - "name": "Unknown" - } - } - ], - "names": [ - { - "value": "TEST_BPN_DFT_1", - "shortName": null, - "type": { - "technicalKey": "OTHER", - "name": "Any other alternative name used for a company, such as a specific language variant.", - "url": "" - }, - "language": { - "technicalKey": "undefined", - "name": "Undefined" - } - } - ], - "legalForm": null, - "status": null, - "profileClassifications": [], - "types": [ - { - "technicalKey": "UNKNOWN", - "name": "Unknown", - "url": "" - } - ], - "bankAccounts": [], - "roles": [], - "relations": [], - "currentness": "2022-07-26T08:17:38.737578Z" - } - """))); + responseWithStatus(200).withBody(bpdmResponse("BPNL00000000TEST", "TEST_BPN_DFT_1")))); // Act final Optional manufacturerName = bpdmFacade.findManufacturerName("BPNL00000000TEST"); diff --git a/irs-api/src/test/java/org/eclipse/tractusx/irs/semanticshub/SemanticHubWireMockConfig.java b/irs-api/src/test/java/org/eclipse/tractusx/irs/semanticshub/SemanticHubWireMockConfig.java new file mode 100644 index 0000000000..16120c02cb --- /dev/null +++ b/irs-api/src/test/java/org/eclipse/tractusx/irs/semanticshub/SemanticHubWireMockConfig.java @@ -0,0 +1,52 @@ +/******************************************************************************** + * 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.semanticshub; + +import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; +import static com.github.tomakehurst.wiremock.client.WireMock.get; +import static com.github.tomakehurst.wiremock.client.WireMock.urlPathMatching; +import static org.eclipse.tractusx.irs.testing.wiremock.WireMockConfig.responseWithStatus; + +import com.github.tomakehurst.wiremock.client.MappingBuilder; + +/** + * WireMock configurations and requests used for testing Semantic Hub fLow. + */ +public final class SemanticHubWireMockConfig { + public static final String BATCH_URN = "urn:samm:io.catenax.batch:2.0.0%23Batch"; + public static final String SEMANTIC_HUB_SCHEMA_URL = "http://semantic.hub/models/{urn}/json-schema"; + private static final String SINGLE_LEVEL_BOM_AS_BUILT_URN = "urn:bamm:io.catenax.single_level_bom_as_built:2.0.0%23SingleLevelBomAsBuilt"; + + private SemanticHubWireMockConfig() { + } + + public static MappingBuilder batchSchemaResponse200() { + return schemaResponse200("/models/" + BATCH_URN + "/json-schema", "semantichub/batch-2.0.0-schema.json"); + } + + public static MappingBuilder singleLevelBomAsBuiltSchemaResponse200() { + return schemaResponse200("/models/" + SINGLE_LEVEL_BOM_AS_BUILT_URN + "/json-schema", + "semantichub/singleLevelBomAsBuilt-2.0.0-schema.json"); + } + + private static MappingBuilder schemaResponse200(final String urlRegex, final String fileName) { + return get(urlPathMatching(urlRegex)).withHost(equalTo("semantic.hub")) + .willReturn(responseWithStatus(200).withBodyFile(fileName)); + } +} \ No newline at end of file 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 321f4d6fef..2a52ef68d7 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 @@ -34,6 +34,8 @@ 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.semanticshub.SemanticHubWireMockConfig.SEMANTIC_HUB_SCHEMA_URL; +import static org.eclipse.tractusx.irs.semanticshub.SemanticHubWireMockConfig.batchSchemaResponse200; import static org.eclipse.tractusx.irs.testing.wiremock.WireMockConfig.responseWithStatus; import static org.eclipse.tractusx.irs.testing.wiremock.WireMockConfig.restTemplateProxy; @@ -67,7 +69,7 @@ void configureSystemUnderTest(WireMockRuntimeInfo wireMockRuntimeInfo) { final SemanticsHubConfiguration config = new SemanticsHubConfiguration(); config.setPageSize(10); config.setUrl("http://semantic.hub/models"); - config.setModelJsonSchemaEndpoint("http://semantic.hub/models/{urn}/json-schema"); + config.setModelJsonSchemaEndpoint(SEMANTIC_HUB_SCHEMA_URL); final SemanticsHubClient semanticsHubClient = new SemanticsHubClientImpl(restTemplate, config); semanticsHubFacade = new SemanticsHubFacade(semanticsHubClient); @@ -107,12 +109,7 @@ void shouldReturn2Pages() throws SchemaNotFoundException { @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"))); + stubFor(batchSchemaResponse200()); // Act final String modelJsonSchema = semanticsHubFacade.getModelJsonSchema("urn:samm:io.catenax.batch:2.0.0#Batch"); diff --git a/irs-api/src/test/resources/__files/integrationtesting/batch-1.json b/irs-api/src/test/resources/__files/integrationtesting/batch-1.json new file mode 100644 index 0000000000..76e6d99956 --- /dev/null +++ b/irs-api/src/test/resources/__files/integrationtesting/batch-1.json @@ -0,0 +1,22 @@ +{ + "localIdentifiers": [ + { + "value": "BPNL00000000TEST", + "key": "manufacturerId" + }, + { + "value": "BID12345678", + "key": "batchId" + } + ], + "manufacturingInformation": { + "date": "2022-02-04T14:48:54", + "country": "HUR" + }, + "catenaXId": "urn:uuid:334cce52-1f52-4bc9-9dd1-410bbe497bbc", + "partTypeInformation": { + "manufacturerPartId": "123-0.740-3434-A", + "classification": "product", + "nameAtManufacturer": "Glue" + } +} \ No newline at end of file diff --git a/irs-api/src/test/resources/__files/integrationtesting/singleLevelBomAsBuilt-1.json b/irs-api/src/test/resources/__files/integrationtesting/singleLevelBomAsBuilt-1.json new file mode 100644 index 0000000000..167a940ce7 --- /dev/null +++ b/irs-api/src/test/resources/__files/integrationtesting/singleLevelBomAsBuilt-1.json @@ -0,0 +1,16 @@ +{ + "catenaXId": "urn:uuid:334cce52-1f52-4bc9-9dd1-410bbe497bbc", + "childItems": [ + { + "catenaXId": "urn:uuid:7e4541ea-bb0f-464c-8cb3-021abccbfaf5", + "quantity": { + "quantityNumber": 0.2014, + "measurementUnit": "unit:kilogram" + }, + "hasAlternatives": true, + "businessPartner": "BPNL00000000TEST", + "createdOn": "2022-02-03T14:48:54.709Z", + "lastModifiedOn": "2022-02-03T14:48:54.709Z" + } + ] +} \ No newline at end of file diff --git a/irs-api/src/test/resources/__files/semantichub/singleLevelBomAsBuilt-2.0.0-schema.json b/irs-api/src/test/resources/__files/semantichub/singleLevelBomAsBuilt-2.0.0-schema.json new file mode 100644 index 0000000000..70fbf22bb0 --- /dev/null +++ b/irs-api/src/test/resources/__files/semantichub/singleLevelBomAsBuilt-2.0.0-schema.json @@ -0,0 +1,102 @@ +{ + "$schema" : "http://json-schema.org/draft-04/schema", + "description" : "The single-level bill of material represents one sub-level of an assembly and does not include any lower-level subassemblies. The as-built lifecycle references all child items as manufactured by the manufacturer referencing only child items in an as-built lifecycle themselves (e.g. serial parts or batches), unless parts can only be tracked by an part ID (on a type level).\n\nIf it is unclear which item has been built-in into the parent item, all potential parts must be listed. This is the case when, e.g. the same item is supplied by two suppliers and the item is only tracked by a customer part ID during assembly, these items can not be differentiated from each other.\n", + "type" : "object", + "components" : { + "schemas" : { + "urn_samm_io.catenax.single_level_bom_as_built_2.0.0_CatenaXIdTraitCharacteristic" : { + "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_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.single_level_bom_as_built_2.0.0_NumberOfObjects" : { + "type" : "number", + "description" : "Quantifiable number of objects in reference to the measurementUnit" + }, + "urn_samm_org.eclipse.esmf.samm_characteristic_2.1.0_UnitReference" : { + "type" : "string", + "pattern" : "[a-zA-Z]*:[a-zA-Z]+", + "description" : "Describes a Property containing a reference to one of the units in the Unit Catalog." + }, + "urn_samm_io.catenax.single_level_bom_as_built_2.0.0_QuantityCharacteristic" : { + "description" : "Describes the quantity in which the child item is assembled in the given parent item by providing a quantity value and the measurement unit in which the quantity is measured.", + "type" : "object", + "properties" : { + "quantityNumber" : { + "description" : "The number of objects related to the measurement unit", + "$ref" : "#/components/schemas/urn_samm_io.catenax.single_level_bom_as_built_2.0.0_NumberOfObjects" + }, + "measurementUnit" : { + "description" : "Unit of Measurement for the quantity of serialized objects", + "$ref" : "#/components/schemas/urn_samm_org.eclipse.esmf.samm_characteristic_2.1.0_UnitReference" + } + }, + "required" : [ "quantityNumber", "measurementUnit" ] + }, + "urn_samm_io.catenax.single_level_bom_as_built_2.0.0_BpnTrait" : { + "type" : "string", + "description" : "Business Partner Number Regular Expression allowing only BPNL which stands for a legal entity.", + "pattern" : "^(BPNL)([0-9]{8})([a-zA-Z0-9]{4})$" + }, + "urn_samm_io.catenax.single_level_bom_as_built_2.0.0_HasAlternativesCharacteristic" : { + "type" : "boolean", + "description" : "Describes the value whether the child data has alternatives." + }, + "urn_samm_io.catenax.single_level_bom_as_built_2.0.0_ChildData" : { + "description" : "Catena-X ID and meta data of the assembled child item.", + "type" : "object", + "properties" : { + "createdOn" : { + "description" : "Timestamp when the relation between the parent item and the child item was created, e.g. when the serialized child part was assembled into the given part.", + "$ref" : "#/components/schemas/urn_samm_org.eclipse.esmf.samm_characteristic_2.1.0_Timestamp" + }, + "quantity" : { + "description" : "Quantity of which the child item is assembled into the parent item. In general it is '1' for serialized parts.", + "$ref" : "#/components/schemas/urn_samm_io.catenax.single_level_bom_as_built_2.0.0_QuantityCharacteristic" + }, + "lastModifiedOn" : { + "description" : "Timestamp when the assembly relationship between parent item and child item was last modified.", + "$ref" : "#/components/schemas/urn_samm_org.eclipse.esmf.samm_characteristic_2.1.0_Timestamp" + }, + "catenaXId" : { + "description" : "The Catena-X ID of the given part (e.g. the assembly), valid for the Catena-X dataspace.", + "$ref" : "#/components/schemas/urn_samm_io.catenax.single_level_bom_as_built_2.0.0_CatenaXIdTraitCharacteristic" + }, + "businessPartner" : { + "description" : "The supplier of the given child item.", + "$ref" : "#/components/schemas/urn_samm_io.catenax.single_level_bom_as_built_2.0.0_BpnTrait" + }, + "hasAlternatives" : { + "description" : "Expresses wether the part is built-in or wether it is one of several options. If the value is false, it can be assumend this exact item is built-in. If the value is true, it is unknown wether this or an alternative item is built-in.\nThis is the case when, e.g. the same item is supplied by two suppliers, the item is only tracked by a customer part ID during assembly. Thus, these items can not be differentiated from each other.\n\n", + "$ref" : "#/components/schemas/urn_samm_io.catenax.single_level_bom_as_built_2.0.0_HasAlternativesCharacteristic" + } + }, + "required" : [ "createdOn", "quantity", "catenaXId", "businessPartner", "hasAlternatives" ] + }, + "urn_samm_io.catenax.single_level_bom_as_built_2.0.0_SetOfChildItemsCharacteristic" : { + "description" : "Set of child items the parent item is assembled by (one structural level down).", + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/urn_samm_io.catenax.single_level_bom_as_built_2.0.0_ChildData" + }, + "uniqueItems" : true + } + } + }, + "properties" : { + "catenaXId" : { + "description" : "The Catena-X ID of the given part (e.g. the assembly), valid for the Catena-X dataspace.", + "$ref" : "#/components/schemas/urn_samm_io.catenax.single_level_bom_as_built_2.0.0_CatenaXIdTraitCharacteristic" + }, + "childItems" : { + "description" : "Set of child items, of which the given parent item is assembled by (one structural level down).", + "$ref" : "#/components/schemas/urn_samm_io.catenax.single_level_bom_as_built_2.0.0_SetOfChildItemsCharacteristic" + } + }, + "required" : [ "catenaXId", "childItems" ] +} \ 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 43ef624320..44effe1593 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 @@ -29,16 +29,21 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.eclipse.tractusx.irs.edc.client.testutil.TestMother.createEdcTransformer; +import static org.eclipse.tractusx.irs.testing.wiremock.SubmodelFacadeWiremockConfig.DATAPLANE_HOST; +import static org.eclipse.tractusx.irs.testing.wiremock.SubmodelFacadeWiremockConfig.PATH_DATAPLANE_PUBLIC; 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; +import java.nio.charset.StandardCharsets; import java.time.Clock; import java.time.Duration; import java.time.OffsetDateTime; import java.util.ArrayList; +import java.util.Base64; import java.util.List; +import java.util.Map; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; @@ -49,9 +54,13 @@ 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.data.StringMapper; import org.eclipse.tractusx.irs.edc.client.cache.endpointdatareference.EndpointDataReferenceCacheService; +import org.eclipse.tractusx.irs.edc.client.configuration.JsonLdConfiguration; import org.eclipse.tractusx.irs.edc.client.exceptions.EdcClientException; import org.eclipse.tractusx.irs.edc.client.exceptions.UsagePolicyException; +import org.eclipse.tractusx.irs.edc.client.model.EDRAuthCode; import org.eclipse.tractusx.irs.edc.client.policy.AcceptedPoliciesProvider; import org.eclipse.tractusx.irs.edc.client.policy.AcceptedPolicy; import org.eclipse.tractusx.irs.edc.client.policy.Constraint; @@ -61,6 +70,7 @@ 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.eclipse.tractusx.irs.testing.wiremock.SubmodelFacadeWiremockConfig; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.http.converter.HttpMessageConverter; @@ -71,10 +81,10 @@ @WireMockTest class SubmodelFacadeWiremockTest { private static final String PROXY_SERVER_HOST = "127.0.0.1"; - private final static String connectorEndpoint = "https://connector.endpoint.com"; - private final static String submodelDataplanePath = "/api/public/shells/12345/submodels/5678/submodel"; - private final static String submodelDataplaneUrl = "http://dataplane.test" + submodelDataplanePath; - private final static String assetId = "12345"; + private final static String CONNECTOR_ENDPOINT_URL = "https://connector.endpoint.com"; + private final static String SUBMODEL_DATAPLANE_PATH = "/api/public/shells/12345/submodels/5678/submodel"; + private final static String SUBMODEL_DATAPLANE_URL = "http://dataplane.test" + SUBMODEL_DATAPLANE_PATH; + private final static String ASSET_ID = "12345"; private final EdcConfiguration config = new EdcConfiguration(); private final EndpointDataReferenceStorage storage = new EndpointDataReferenceStorage(Duration.ofMinutes(1)); private EdcSubmodelClient edcSubmodelClient; @@ -133,13 +143,13 @@ void configureSystemUnderTest(WireMockRuntimeInfo wireMockRuntimeInfo) { void shouldReturnAssemblyPartRelationshipAsString() throws EdcClientException, ExecutionException, InterruptedException { // Arrange - SubmodelFacadeWiremockConfig.prepareNegotiation(storage); - givenThat(get(urlPathEqualTo(submodelDataplanePath)).willReturn( + prepareNegotiation(); + givenThat(get(urlPathEqualTo(SUBMODEL_DATAPLANE_PATH)).willReturn( responseWithStatus(200).withBodyFile("singleLevelBomAsBuilt.json"))); // Act - final String submodel = edcSubmodelClient.getSubmodelRawPayload(connectorEndpoint, submodelDataplaneUrl, - assetId).get(); + final String submodel = edcSubmodelClient.getSubmodelRawPayload(CONNECTOR_ENDPOINT_URL, SUBMODEL_DATAPLANE_URL, + ASSET_ID).get(); // Assert assertThat(submodel).contains("\"catenaXId\": \"urn:uuid:fe99da3d-b0de-4e80-81da-882aebcca978\""); @@ -149,13 +159,13 @@ void shouldReturnAssemblyPartRelationshipAsString() void shouldReturnMaterialForRecyclingAsString() throws EdcClientException, ExecutionException, InterruptedException { // Arrange - SubmodelFacadeWiremockConfig.prepareNegotiation(storage); - givenThat(get(urlPathEqualTo(submodelDataplanePath)).willReturn( + prepareNegotiation(); + givenThat(get(urlPathEqualTo(SUBMODEL_DATAPLANE_PATH)).willReturn( responseWithStatus(200).withBodyFile("materialForRecycling.json"))); // Act - final String submodel = edcSubmodelClient.getSubmodelRawPayload(connectorEndpoint, submodelDataplaneUrl, - assetId).get(); + final String submodel = edcSubmodelClient.getSubmodelRawPayload(CONNECTOR_ENDPOINT_URL, SUBMODEL_DATAPLANE_URL, + ASSET_ID).get(); // Assert assertThat(submodel).contains("\"materialName\": \"Cooper\","); @@ -165,12 +175,12 @@ void shouldReturnMaterialForRecyclingAsString() void shouldReturnObjectAsStringWhenResponseNotJSON() throws EdcClientException, ExecutionException, InterruptedException { // Arrange - SubmodelFacadeWiremockConfig.prepareNegotiation(storage); - givenThat(get(urlPathEqualTo(submodelDataplanePath)).willReturn(responseWithStatus(200).withBody("test"))); + prepareNegotiation(); + givenThat(get(urlPathEqualTo(SUBMODEL_DATAPLANE_PATH)).willReturn(responseWithStatus(200).withBody("test"))); // Act - final String submodel = edcSubmodelClient.getSubmodelRawPayload(connectorEndpoint, submodelDataplaneUrl, - assetId).get(); + final String submodel = edcSubmodelClient.getSubmodelRawPayload(CONNECTOR_ENDPOINT_URL, SUBMODEL_DATAPLANE_URL, + ASSET_ID).get(); // Assert assertThat(submodel).isEqualTo("test"); @@ -189,26 +199,26 @@ void shouldThrowExceptionWhenPoliciesAreNotAccepted() { when(acceptedPoliciesProvider.getAcceptedPolicies()).thenReturn(List.of(acceptedPolicy)); - SubmodelFacadeWiremockConfig.prepareNegotiation(storage); - givenThat(get(urlPathEqualTo(submodelDataplanePath)).willReturn(responseWithStatus(200).withBody("test"))); + prepareNegotiation(); + givenThat(get(urlPathEqualTo(SUBMODEL_DATAPLANE_PATH)).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(CONNECTOR_ENDPOINT_URL, SUBMODEL_DATAPLANE_URL, ASSET_ID) + .get()).withMessageEndingWith(errorMessage); } @Test void shouldThrowExceptionWhenResponse_400() { // Arrange - SubmodelFacadeWiremockConfig.prepareNegotiation(storage); - givenThat(get(urlPathEqualTo(submodelDataplanePath)).willReturn( + prepareNegotiation(); + givenThat(get(urlPathEqualTo(SUBMODEL_DATAPLANE_PATH)).willReturn( responseWithStatus(400).withBody("{ error: '400'}"))); // Act final ThrowableAssert.ThrowingCallable throwingCallable = () -> edcSubmodelClient.getSubmodelRawPayload( - connectorEndpoint, submodelDataplaneUrl, assetId).get(5, TimeUnit.SECONDS); + CONNECTOR_ENDPOINT_URL, SUBMODEL_DATAPLANE_URL, ASSET_ID).get(5, TimeUnit.SECONDS); // Assert assertThatExceptionOfType(ExecutionException.class).isThrownBy(throwingCallable) @@ -218,19 +228,44 @@ void shouldThrowExceptionWhenResponse_400() { @Test void shouldThrowExceptionWhenResponse_500() { // Arrange - SubmodelFacadeWiremockConfig.prepareNegotiation(storage); - givenThat(get(urlPathEqualTo(submodelDataplanePath)).willReturn( + prepareNegotiation(); + givenThat(get(urlPathEqualTo(SUBMODEL_DATAPLANE_PATH)).willReturn( responseWithStatus(500).withBody("{ error: '500'}"))); // Act final ThrowableAssert.ThrowingCallable throwingCallable = () -> edcSubmodelClient.getSubmodelRawPayload( - connectorEndpoint, submodelDataplaneUrl, assetId).get(5, TimeUnit.SECONDS); + CONNECTOR_ENDPOINT_URL, SUBMODEL_DATAPLANE_URL, ASSET_ID).get(5, TimeUnit.SECONDS); // Assert assertThatExceptionOfType(ExecutionException.class).isThrownBy(throwingCallable) .withCauseInstanceOf(RestClientException.class); } + private void prepareNegotiation() { + final String contractAgreementId = SubmodelFacadeWiremockConfig.prepareNegotiation(); + final EndpointDataReference ref = createEndpointDataReference(contractAgreementId); + storage.put(contractAgreementId, ref); + } + + 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(); + } + 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-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 index 6130c1501d..db505bf240 100644 --- 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 @@ -20,24 +20,33 @@ 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 static org.eclipse.tractusx.irs.testing.wiremock.WireMockConfig.responseWithStatus; import java.util.List; import com.github.tomakehurst.wiremock.client.MappingBuilder; -public class DiscoveryServiceWiremockConfig { - public static final String CONTROLPLANE_PUBLIC_URL = "https://controlplane.test"; +/** + * WireMock configurations and requests used for testing the Discovery Service flow. + */ +public final class DiscoveryServiceWiremockConfig { + public static final String CONTROLPLANE_PUBLIC_URL = "https://test.edc.io"; 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 final String DISCOVERY_FINDER_URL = DISCOVERY_HOST + DISCOVERY_FINDER_PATH; + private DiscoveryServiceWiremockConfig() { + } public static MappingBuilder postEdcDiscovery200() { + return postEdcDiscovery200(TEST_BPN, List.of(CONTROLPLANE_PUBLIC_URL)); + } + + public static MappingBuilder postEdcDiscovery200(final String bpn, final List edcUrls) { return post(urlPathEqualTo(EDC_DISCOVERY_PATH)).willReturn( - responseWithStatus(200).withBody(edcDiscoveryResponse(TEST_BPN, List.of(CONTROLPLANE_PUBLIC_URL)))); + responseWithStatus(200).withBody(edcDiscoveryResponse(bpn, edcUrls))); } public static String edcDiscoveryResponse(final String bpn, final List connectorEndpoints) { @@ -74,15 +83,6 @@ public static String discoveryFinderResponse(final String discoveryFinderUrl) { """.formatted(discoveryFinderUrl); } - public static String discoveryFinderEmtpyResponse() { - return """ - { - "endpoints": [ - ] - } - """; - } - public static MappingBuilder postDiscoveryFinder404() { return post(urlPathEqualTo(DISCOVERY_FINDER_PATH)).willReturn(responseWithStatus(404)); } diff --git a/irs-testing/src/main/java/org/eclipse/tractusx/irs/testing/wiremock/DtrWiremockConfig.java b/irs-testing/src/main/java/org/eclipse/tractusx/irs/testing/wiremock/DtrWiremockConfig.java index 4f29f1fbd1..a4f743d578 100644 --- a/irs-testing/src/main/java/org/eclipse/tractusx/irs/testing/wiremock/DtrWiremockConfig.java +++ b/irs-testing/src/main/java/org/eclipse/tractusx/irs/testing/wiremock/DtrWiremockConfig.java @@ -27,18 +27,28 @@ import com.github.tomakehurst.wiremock.client.MappingBuilder; -public class DtrWiremockConfig { +/** + * WireMock configurations and requests used for testing the decentralized DigitalTwinRegistry flow. + */ +public final 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 DATAPLANE_PUBLIC_PATH = "/api/public"; + public static final String DATAPLANE_PUBLIC_URL = DATAPLANE_URL + DATAPLANE_PUBLIC_PATH; public static final String SHELL_DESCRIPTORS_PATH = "/shell-descriptors/"; + public static final String PUBLIC_SHELL_DESCRIPTORS_PATH = DATAPLANE_PUBLIC_PATH + SHELL_DESCRIPTORS_PATH; public static final String SHELL_DESCRIPTORS_TEMPLATE = SHELL_DESCRIPTORS_PATH + "{aasIdentifier}"; public static final String LOOKUP_SHELLS_PATH = "/lookup/shells"; + public static final String PUBLIC_LOOKUP_SHELLS_PATH = DATAPLANE_PUBLIC_PATH + LOOKUP_SHELLS_PATH; public static final String LOOKUP_SHELLS_TEMPLATE = LOOKUP_SHELLS_PATH + "?assetIds={assetIds}"; private DtrWiremockConfig() { } public static MappingBuilder getShellDescriptor200() { + return getShellDescriptor200(SHELL_DESCRIPTORS_PATH + ".*"); + } + + public static MappingBuilder getShellDescriptor200(final String urlRegex) { 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", @@ -48,15 +58,15 @@ public static MappingBuilder getShellDescriptor200() { DiscoveryServiceWiremockConfig.CONTROLPLANE_PUBLIC_URL, "Batch", "urn:uuid:f53db6ef-7a58-4326-9169-0ae198b85dbf", "urn:samm:io.catenax.batch:2.0.0#Batch"); - 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 String singleLevelBomAsBuilt = submodelDescriptor(DATAPLANE_PUBLIC_URL, + "urn:uuid:234edd2f-0223-47c7-9fe4-3984ab14c4f9", DiscoveryServiceWiremockConfig.CONTROLPLANE_PUBLIC_URL, + "SingleLevelBomAsBuilt", "urn:uuid:0e413809-966b-4107-aae5-aeb28bcdaadf", + "urn:bamm:io.catenax.single_level_bom_as_built:2.0.0#SingleLevelBomAsBuilt"); - final List submodelDescriptors = List.of(materialForRecycling, batch, singleLevelUsageAsBuilt); + final List submodelDescriptors = List.of(batch, singleLevelBomAsBuilt, materialForRecycling); final List specificAssetIds = List.of(specificAssetId("manufacturerId", "BPNL00000003B0Q0"), specificAssetId("batchId", "BID12345678")); - return get(urlPathMatching(SHELL_DESCRIPTORS_PATH + ".*")).willReturn(responseWithStatus(200).withBody( + return get(urlPathMatching(urlRegex)).willReturn(responseWithStatus(200).withBody( assetAdministrationShellResponse(submodelDescriptors, "urn:uuid:7e4541ea-bb0f-464c-8cb3-021abccbfaf5", "EngineeringPlastics", "urn:uuid:9ce43b21-75e3-4cea-b13e-9a34f4f6822a", specificAssetIds))); } @@ -103,7 +113,7 @@ public static String specificAssetId(final String key, final String value) { 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; + final String href = dataplaneUrl + "/" + submodelDescriptorId; return """ { "endpoints": [ @@ -147,7 +157,11 @@ public static String submodelDescriptor(final String dataplaneUrl, final String } public static MappingBuilder getLookupShells200() { - return get(urlPathEqualTo(LOOKUP_SHELLS_PATH)).willReturn(responseWithStatus(200).withBody( + return getLookupShells200(LOOKUP_SHELLS_PATH); + } + + public static MappingBuilder getLookupShells200(final String lookupShellsPath) { + return get(urlPathEqualTo(lookupShellsPath)).willReturn(responseWithStatus(200).withBody( lookupShellsResponse(List.of("urn:uuid:21f7ebea-fa8a-410c-a656-bd9082e67dcf")))); } diff --git a/irs-edc-client/src/test/java/org/eclipse/tractusx/irs/edc/client/SubmodelFacadeWiremockConfig.java b/irs-testing/src/main/java/org/eclipse/tractusx/irs/testing/wiremock/SubmodelFacadeWiremockConfig.java similarity index 67% rename from irs-edc-client/src/test/java/org/eclipse/tractusx/irs/edc/client/SubmodelFacadeWiremockConfig.java rename to irs-testing/src/main/java/org/eclipse/tractusx/irs/testing/wiremock/SubmodelFacadeWiremockConfig.java index 762ddeaef1..6c3453b6f5 100644 --- a/irs-edc-client/src/test/java/org/eclipse/tractusx/irs/edc/client/SubmodelFacadeWiremockConfig.java +++ b/irs-testing/src/main/java/org/eclipse/tractusx/irs/testing/wiremock/SubmodelFacadeWiremockConfig.java @@ -16,27 +16,19 @@ * * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -package org.eclipse.tractusx.irs.edc.client; +package org.eclipse.tractusx.irs.testing.wiremock; 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.stubFor; 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 { +/** + * WireMock configurations and requests used for testing the EDC Flow. + */ +public final class SubmodelFacadeWiremockConfig { public static final String PATH_CATALOG = "/catalog/request"; public static final String PATH_NEGOTIATE = "/contractnegotiations"; public static final String PATH_TRANSFER = "/transferprocesses"; @@ -54,46 +46,50 @@ public class SubmodelFacadeWiremockConfig { }"""; 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"; + public static final String EDC_PROVIDER_BPN = "BPNL00000003CRHK"; - // TODO move to irs-testing + private SubmodelFacadeWiremockConfig() { + } - public static void prepareNegotiation(final EndpointDataReferenceStorage storage) { - prepareNegotiation(storage, "1bbaec6e-c316-4e1e-8258-c07a648cc43c", "1b21e963-0bc5-422a-b30d-fd3511861d88", + public static String prepareNegotiation() { + return prepareNegotiation("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)))); + public static String prepareNegotiation(final String negotiationId, final String transferProcessId, + final String contractAgreementId, final String edcAssetId) { + stubFor(post(urlPathEqualTo(PATH_CATALOG)).willReturn(WireMockConfig.responseWithStatus(200) + .withBody(getCatalogResponse(edcAssetId, + "USE", EDC_PROVIDER_BPN)))); + + stubFor(post(urlPathEqualTo(PATH_NEGOTIATE)).willReturn( + WireMockConfig.responseWithStatus(200).withBody(startNegotiationResponse(negotiationId)))); final String negotiationState = "FINALIZED"; - givenThat(get(urlPathEqualTo(PATH_NEGOTIATE + "/" + negotiationId)).willReturn(responseWithStatus(200).withBody( - getNegotiationConfirmedResponse(negotiationId, negotiationState, contractAgreementId)))); + stubFor(get(urlPathEqualTo(PATH_NEGOTIATE + "/" + negotiationId)).willReturn( + WireMockConfig.responseWithStatus(200) + .withBody(getNegotiationConfirmedResponse(negotiationId, negotiationState, + contractAgreementId)))); - givenThat(get(urlPathEqualTo(PATH_NEGOTIATE + "/" + negotiationId + PATH_STATE)).willReturn( - responseWithStatus(200).withBody(getNegotiationStateResponse(negotiationState)))); + stubFor(get(urlPathEqualTo(PATH_NEGOTIATE + "/" + negotiationId + PATH_STATE)).willReturn( + WireMockConfig.responseWithStatus(200).withBody(getNegotiationStateResponse(negotiationState)))); - givenThat(post(urlPathEqualTo(PATH_TRANSFER)).willReturn( - responseWithStatus(200).withBody(startTransferProcessResponse(transferProcessId)) + stubFor(post(urlPathEqualTo(PATH_TRANSFER)).willReturn( + WireMockConfig.responseWithStatus(200).withBody(startTransferProcessResponse(transferProcessId)) )); final String transferProcessState = "COMPLETED"; - givenThat(get(urlPathEqualTo(PATH_TRANSFER + "/" + transferProcessId + PATH_STATE)).willReturn( - responseWithStatus(200).withBody(getTransferProcessStateResponse(transferProcessState)) + stubFor(get(urlPathEqualTo(PATH_TRANSFER + "/" + transferProcessId + PATH_STATE)).willReturn( + WireMockConfig.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); + stubFor(get(urlPathEqualTo(PATH_TRANSFER + "/" + transferProcessId)).willReturn( + WireMockConfig.responseWithStatus(200) + .withBody( + getTransferConfirmedResponse(transferProcessId, transferProcessState, edcAssetId, + contractAgreementId)))); + return contractAgreementId; } private static String startTransferProcessResponse(final String transferProcessId) { @@ -164,16 +160,16 @@ private static String getTransferConfirmedResponse(final String transferProcessI "@id": "%s", "edc:assetId": "%s", "edc:contractId": "%s", - "edc:connectorId": "BPNL00000003CRHK" + "edc:connectorId": "%s" }, "edc:receiverHttpEndpoint": "%s", "@context": %s } """.formatted(transferProcessId, transferState, transferProcessId, edcAssetId, contractAgreementId, - IRS_INTERNAL_CALLBACK_URL, CONTEXT); + EDC_PROVIDER_BPN, IRS_INTERNAL_CALLBACK_URL, CONTEXT); } - private static String getCatalogResponse(final String edcAssetId, final String permissionType, + public static String getCatalogResponse(final String edcAssetId, final String permissionType, final String edcProviderBpn) { return """ { @@ -221,7 +217,6 @@ private static String getCatalogResponse(final String edcAssetId, final String p EDC_PROVIDER_DUMMY_URL, edcProviderBpn, CONTEXT); } - @NotNull private static String createConstraints() { final List atomitConstraints = List.of(createAtomicConstraint("Membership", "active"), createAtomicConstraint("FrameworkAgreement.traceability", "active")); @@ -244,22 +239,4 @@ private static String createAtomicConstraint(final String leftOperand, final Str }""".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