diff --git a/bpdm-system-tester/pom.xml b/bpdm-system-tester/pom.xml new file mode 100644 index 000000000..f48edeee0 --- /dev/null +++ b/bpdm-system-tester/pom.xml @@ -0,0 +1,110 @@ + + 4.0.0 + + + org.eclipse.tractusx + bpdm-parent + ${revision} + + + bpdm-system-tester + Business Partner Data Management System Tester + Application to perform system tests for the BPDM system and golden record process + jar + + + 21 + 2.0.10 + 3.0.5 + 7.18.1 + + + + + org.jetbrains.kotlinx + kotlinx-coroutines-core + 1.8.1 + + + org.springframework.boot + spring-boot-starter + + + org.jetbrains.kotlin + kotlin-reflect + + + org.jetbrains.kotlin + kotlin-stdlib + + + ${project.groupId} + bpdm-pool-api + + + ${project.groupId} + bpdm-gate-api + + + io.github.microutils + kotlin-logging-jvm + + + org.assertj + assertj-core + + + io.cucumber + cucumber-java + 7.14.0 + + + io.cucumber + cucumber-junit + 7.14.0 + + + io.cucumber + cucumber-spring + 7.14.0 + + + ${project.groupId} + bpdm-common-test + test + + + org.springframework.boot + spring-boot-starter-test + + + + org.mockito + mockito-core + + + + + io.cucumber + cucumber-junit-platform-engine + 7.14.0 + + + + + ${project.basedir}/src/main/kotlin + + + org.springframework.boot + spring-boot-maven-plugin + + + org.jetbrains.kotlin + kotlin-maven-plugin + + + + + + diff --git a/bpdm-system-tester/src/main/kotlin/org/eclipse/tractusx/bpdm/test/system/Application.kt b/bpdm-system-tester/src/main/kotlin/org/eclipse/tractusx/bpdm/test/system/Application.kt new file mode 100644 index 000000000..e191273d2 --- /dev/null +++ b/bpdm-system-tester/src/main/kotlin/org/eclipse/tractusx/bpdm/test/system/Application.kt @@ -0,0 +1,45 @@ +/******************************************************************************* + * 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.bpdm.test.system + +import io.cucumber.junit.Cucumber +import io.cucumber.junit.CucumberOptions +import io.cucumber.spring.CucumberContextConfiguration +import org.junit.runner.RunWith +import org.springframework.boot.autoconfigure.SpringBootApplication +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration +import org.springframework.boot.context.properties.ConfigurationPropertiesScan +import org.springframework.boot.test.context.SpringBootTest + +@RunWith(Cucumber::class) +@CucumberOptions(features = ["bpdm-system-tester/src/main/resources"], glue = ["org.eclipse.tractusx.bpdm.test.system.stepdefinations"]) +class CucumberTestRunConfiguration + +@CucumberContextConfiguration +@SpringBootTest +class SpringTestRunConfiguration + +@SpringBootApplication(exclude=[DataSourceAutoConfiguration::class]) +@ConfigurationPropertiesScan +class SpringApplicationConfiguration + +fun main(args: Array) { + io.cucumber.core.cli.Main.main(*args) +} \ No newline at end of file diff --git a/bpdm-system-tester/src/main/kotlin/org/eclipse/tractusx/bpdm/test/system/config/GateClientConfig.kt b/bpdm-system-tester/src/main/kotlin/org/eclipse/tractusx/bpdm/test/system/config/GateClientConfig.kt new file mode 100644 index 000000000..c37ada7e2 --- /dev/null +++ b/bpdm-system-tester/src/main/kotlin/org/eclipse/tractusx/bpdm/test/system/config/GateClientConfig.kt @@ -0,0 +1,54 @@ +/******************************************************************************* + * 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.bpdm.test.system.config + +import org.eclipse.tractusx.bpdm.common.util.BpdmClientProperties +import org.eclipse.tractusx.bpdm.common.util.BpdmWebClientProvider +import org.eclipse.tractusx.bpdm.common.util.ClientConfigurationProperties +import org.eclipse.tractusx.bpdm.gate.api.client.GateClient +import org.eclipse.tractusx.bpdm.gate.api.client.GateClientImpl +import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientProperties +import org.springframework.boot.context.properties.ConfigurationProperties +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration + +@ConfigurationProperties(prefix = PoolClientConfigurationProperties.PREFIX) +data class GateClientConfigProperties( + override val baseUrl: String = "http://localhost:8081", + val searchChangelogPageSize: Int = 100, + override val securityEnabled: Boolean = false, + override val registration: OAuth2ClientProperties.Registration, + override val provider: OAuth2ClientProperties.Provider +) : BpdmClientProperties { + companion object { + const val PREFIX = "${ClientConfigurationProperties.PREFIX}.gate" + } + + override fun getId() = PREFIX +} + +@Configuration +class GateClientConfig{ + + @Bean + fun gateClient(webClientProvider: BpdmWebClientProvider, properties: GateClientConfigProperties): GateClient { + return GateClientImpl { webClientProvider.builder(properties).build() } + } +} \ No newline at end of file diff --git a/bpdm-system-tester/src/main/kotlin/org/eclipse/tractusx/bpdm/test/system/config/PoolClientConfig.kt b/bpdm-system-tester/src/main/kotlin/org/eclipse/tractusx/bpdm/test/system/config/PoolClientConfig.kt new file mode 100644 index 000000000..56f9eadbc --- /dev/null +++ b/bpdm-system-tester/src/main/kotlin/org/eclipse/tractusx/bpdm/test/system/config/PoolClientConfig.kt @@ -0,0 +1,56 @@ +/******************************************************************************* + * 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.bpdm.test.system.config + +import org.eclipse.tractusx.bpdm.common.util.BpdmClientProperties +import org.eclipse.tractusx.bpdm.common.util.BpdmWebClientProvider +import org.eclipse.tractusx.bpdm.common.util.ClientConfigurationProperties +import org.eclipse.tractusx.bpdm.pool.api.client.PoolApiClient +import org.eclipse.tractusx.bpdm.pool.api.client.PoolClientImpl +import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientProperties +import org.springframework.boot.context.properties.ConfigurationProperties +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration + + +@ConfigurationProperties(prefix = PoolClientConfigurationProperties.PREFIX) +data class PoolClientConfigurationProperties( + override val baseUrl: String = "http://localhost:8080", + val searchChangelogPageSize: Int = 100, + override val securityEnabled: Boolean = false, + override val registration: OAuth2ClientProperties.Registration, + override val provider: OAuth2ClientProperties.Provider +) : BpdmClientProperties { + companion object { + const val PREFIX = "${ClientConfigurationProperties.PREFIX}.pool" + } + + override fun getId() = PREFIX +} + +@Configuration +class PoolClientConfiguration{ + + @Bean + fun poolClient(webClientProvider: BpdmWebClientProvider, properties: PoolClientConfigurationProperties): PoolApiClient{ + return PoolClientImpl { webClientProvider.builder(properties).build() } + } +} + diff --git a/bpdm-system-tester/src/main/kotlin/org/eclipse/tractusx/bpdm/test/system/config/TestDataConfiguration.kt b/bpdm-system-tester/src/main/kotlin/org/eclipse/tractusx/bpdm/test/system/config/TestDataConfiguration.kt new file mode 100644 index 000000000..a7ff00cee --- /dev/null +++ b/bpdm-system-tester/src/main/kotlin/org/eclipse/tractusx/bpdm/test/system/config/TestDataConfiguration.kt @@ -0,0 +1,104 @@ +/******************************************************************************* + * 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.bpdm.test.system.config + +import org.eclipse.tractusx.bpdm.gate.api.client.GateClient +import org.eclipse.tractusx.bpdm.pool.api.client.PoolApiClient +import org.eclipse.tractusx.bpdm.pool.api.model.IdentifierBusinessPartnerType +import org.eclipse.tractusx.bpdm.pool.api.model.IdentifierTypeDto +import org.eclipse.tractusx.bpdm.pool.api.model.request.LegalFormRequest +import org.eclipse.tractusx.bpdm.test.system.utils.GateInputFactory +import org.eclipse.tractusx.bpdm.test.system.utils.GateOutputFactory +import org.eclipse.tractusx.bpdm.test.system.utils.StepUtils +import org.eclipse.tractusx.bpdm.test.system.utils.TestMetadata +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import java.time.Instant + +@Configuration +class TestDataConfiguration { + + @Bean + fun testMetadata(poolClient: PoolApiClient): TestMetadata { + val testMetadata = TestMetadata( + identifierTypes = listOf("id1", "id2"), + legalForms = listOf("legalform1", "legalform2"), + adminAreas = listOf("DE-BW", "DE-BY") + ) + + testMetadata.identifierTypes.forEach { technicalKey -> + createIdentifierType(poolClient, technicalKey, IdentifierBusinessPartnerType.LEGAL_ENTITY) + createIdentifierType(poolClient, technicalKey, IdentifierBusinessPartnerType.ADDRESS) + } + + testMetadata.legalForms.forEach { technicalKey -> + createLegalForm(poolClient, technicalKey) + } + + return testMetadata + } + + private fun createIdentifierType( + poolClient: PoolApiClient, + technicalKey: String, + partnerType: IdentifierBusinessPartnerType + ) { + runCatching { + poolClient.metadata.createIdentifierType( + IdentifierTypeDto( + technicalKey, + partnerType, + technicalKey, + null, null, null + ) + ) + }.onFailure { + // Log or handle failure if needed + } + } + + private fun createLegalForm(poolClient: PoolApiClient, technicalKey: String) { + runCatching { + poolClient.metadata.createLegalForm(LegalFormRequest(technicalKey, technicalKey, null, null, null, null, null, null, true)) + }.onFailure { + // Log or handle failure if needed + } + } + + @Bean + fun gateTestDataFactory(testMetadata: TestMetadata, testRunData: TestRunData): GateInputFactory { + return GateInputFactory(testMetadata, testRunData) + } + + @Bean + fun gateOutputFactory(gateInputDataFactory: GateInputFactory): GateOutputFactory { + return GateOutputFactory(gateInputDataFactory) + } + + @Bean + fun testRunData(): TestRunData { + return TestRunData(Instant.now()) + } + + @Bean + fun stepUtils(testRunData: TestRunData, gateClient: GateClient): StepUtils{ + return StepUtils(gateClient) + } +} \ No newline at end of file diff --git a/bpdm-system-tester/src/main/kotlin/org/eclipse/tractusx/bpdm/test/system/config/TestRunData.kt b/bpdm-system-tester/src/main/kotlin/org/eclipse/tractusx/bpdm/test/system/config/TestRunData.kt new file mode 100644 index 000000000..31f8a27da --- /dev/null +++ b/bpdm-system-tester/src/main/kotlin/org/eclipse/tractusx/bpdm/test/system/config/TestRunData.kt @@ -0,0 +1,28 @@ +/******************************************************************************* + * 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.bpdm.test.system.config + +import java.time.Instant + +data class TestRunData ( + val testTime: Instant +){ + fun toExternalId(seed: String): String = "${seed}_$testTime" +} \ No newline at end of file diff --git a/bpdm-system-tester/src/main/kotlin/org/eclipse/tractusx/bpdm/test/system/stepdefinations/ShareGenericNoBpnStepDefs.kt b/bpdm-system-tester/src/main/kotlin/org/eclipse/tractusx/bpdm/test/system/stepdefinations/ShareGenericNoBpnStepDefs.kt new file mode 100644 index 000000000..36173b0fc --- /dev/null +++ b/bpdm-system-tester/src/main/kotlin/org/eclipse/tractusx/bpdm/test/system/stepdefinations/ShareGenericNoBpnStepDefs.kt @@ -0,0 +1,132 @@ +/******************************************************************************* + * 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.bpdm.test.system.stepdefinations + +import io.cucumber.java.en.Given +import io.cucumber.java.en.Then +import io.cucumber.java.en.When +import org.assertj.core.api.Assertions.assertThat +import org.eclipse.tractusx.bpdm.common.dto.AddressType +import org.eclipse.tractusx.bpdm.common.dto.PaginationRequest +import org.eclipse.tractusx.bpdm.gate.api.client.GateClient +import org.eclipse.tractusx.bpdm.test.system.SpringTestRunConfiguration +import org.eclipse.tractusx.bpdm.test.system.config.TestRunData +import org.eclipse.tractusx.bpdm.test.system.utils.* + +class ShareGenericNoBpnStepDefs( + private val gateInputDataFactory: GateInputFactory, + private val gateOutputFactory: GateOutputFactory, + private val gateClient: GateClient, + private val stepUtils: StepUtils, + private val testRunData: TestRunData +): SpringTestRunConfiguration() { + + @Given("^output \"([^\"]*)\" with external-ID \"([^\"]*)\"$") + fun `given output seed with externalId`(seed: String, externalId: String) { + uploadInput(seed, externalId, null) + stepUtils.waitForResult(testRunData.toExternalId(externalId)) + } + + @When("^the sharing member uploads full valid input \"([^\"]*)\" with external-ID \"([^\"]*)\" with address type \"([^\"]*)\"$") + fun `when the sharing member uploads input seed with address type`(seed: String, externalId: String, addressType: String) { + uploadInput(seed, externalId, AddressType.valueOf(addressType)) + } + + @When("^the sharing member uploads full valid input \"([^\"]*)\" with external-ID \"([^\"]*)\" without address type") + fun `when the sharing member uploads input seed without address type`(seed: String, externalId: String) { + uploadInput(seed, externalId, null) + } + + @Then("^the sharing member receives output \"([^\"]*)\" with external-ID \"([^\"]*)\" with address type \"([^\"]*)\"$") + fun `then the sharing member receives output seed with modifier`(seed: String, externalId: String, addressType: String) { + val expectedOutput = gateOutputFactory.createOutput(seed, externalId) + .withAddressType(AddressType.valueOf(addressType)) + // On no auth config we can't get own company data to true + .copy(isOwnCompanyData = false) + + stepUtils.waitForResult(expectedOutput.externalId) + + val actualOutput = gateClient.businessParters.getBusinessPartnersOutput(listOf(expectedOutput.externalId), PaginationRequest()).content.single() + + stepUtils.assertEqualIgnoreBpns(actualOutput, expectedOutput) + } + + @When("^the sharing member uploads input \"([^\"]*)\" with external-ID \"([^\"]*)\" without mandatory field \"([^\"]*)\"$") + fun `when the sharing member upload input seed without mandatory field`(seed: String, externalId: String, mandatoryField: String) { + uploadInputWithoutMandatoryField(seed, externalId, mandatoryField) + } + + @Then("^the sharing member receives sharing error \"([^\"]*)\" with external-ID \"([^\"]*)\" with error message \"([^\"]*)\"$") + fun `then the sharing member receives sharing error with error message`(seed: String, externalId: String, errorMessage: String) { + stepUtils.waitForResult(testRunData.toExternalId(externalId)) + val sharingState = gateClient.sharingState.getSharingStates(PaginationRequest(), listOf(testRunData.toExternalId(externalId))).content.single() + assertThat(sharingState.sharingErrorMessage).contains(errorMessage) + } + + private fun uploadInput(seed: String, externalId: String, addressType: AddressType?){ + val inputRequest = gateInputDataFactory.createFullValid(seed, externalId).withAddressType(addressType).withoutAnyBpn() + gateClient.businessParters.upsertBusinessPartnersInput(listOf(inputRequest)) + } + + private fun uploadInputWithoutMandatoryField(seed: String, externalId: String, mandatoryField: String) { + // Prepare invalid input by removing the necessary field to simulate the error + val inputRequest = when (mandatoryField) { + "legalName" -> gateInputDataFactory.createFullValid(seed, externalId).withoutAnyBpn().copy( + nameParts = emptyList(), + legalEntity = gateInputDataFactory.createFullValid(seed, externalId).withoutAnyBpn().legalEntity.copy(legalName = null) + ) + "physicalAddress.country" -> gateInputDataFactory.createFullValid(seed, externalId).withoutAnyBpn().copy( + address = gateInputDataFactory.createFullValid(seed, externalId).address.copy( + physicalPostalAddress = gateInputDataFactory.createFullValid(seed, externalId).address.physicalPostalAddress.copy(country = null) + ) + ) + "physicalAddress.city" -> gateInputDataFactory.createFullValid(seed, externalId).withoutAnyBpn().copy( + address = gateInputDataFactory.createFullValid(seed, externalId).address.copy( + physicalPostalAddress = gateInputDataFactory.createFullValid(seed, externalId).address.physicalPostalAddress.copy(city = null) + ) + ) + "alternativeAddress.country" -> gateInputDataFactory.createFullValid(seed, externalId).withoutAnyBpn().copy( + address = gateInputDataFactory.createFullValid(seed, externalId).address.copy( + alternativePostalAddress = gateInputDataFactory.createFullValid(seed, externalId).address.alternativePostalAddress!!.copy(country = null) + ) + ) + "alternativeAddress.city" -> gateInputDataFactory.createFullValid(seed, externalId).withoutAnyBpn().copy( + address = gateInputDataFactory.createFullValid(seed, externalId).address.copy( + alternativePostalAddress = gateInputDataFactory.createFullValid(seed, externalId).address.alternativePostalAddress!!.copy(city = null) + ) + ) + "alternativeAddress.deliveryServiceType" -> gateInputDataFactory.createFullValid(seed, externalId).withoutAnyBpn().copy( + address = gateInputDataFactory.createFullValid(seed, externalId).address.copy( + alternativePostalAddress = gateInputDataFactory.createFullValid(seed, externalId).address.alternativePostalAddress!!.copy(deliveryServiceType = null) + ) + ) + "alternativeAddress.deliveryServiceNumber" -> gateInputDataFactory.createFullValid(seed, externalId).withoutAnyBpn().copy( + address = gateInputDataFactory.createFullValid(seed, externalId).address.copy( + alternativePostalAddress = gateInputDataFactory.createFullValid(seed, externalId).address.alternativePostalAddress!!.copy(deliveryServiceNumber = null) + ) + ) + // Similarly we can handle other fields... + else -> throw IllegalArgumentException("Unsupported error field: $mandatoryField") + } + gateClient.businessParters.upsertBusinessPartnersInput(listOf(inputRequest)) + } + + +} \ No newline at end of file diff --git a/bpdm-system-tester/src/main/kotlin/org/eclipse/tractusx/bpdm/test/system/utils/GateInputFactory.kt b/bpdm-system-tester/src/main/kotlin/org/eclipse/tractusx/bpdm/test/system/utils/GateInputFactory.kt new file mode 100644 index 000000000..b8f5ce782 --- /dev/null +++ b/bpdm-system-tester/src/main/kotlin/org/eclipse/tractusx/bpdm/test/system/utils/GateInputFactory.kt @@ -0,0 +1,171 @@ +/******************************************************************************* + * 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.bpdm.test.system.utils + +import com.neovisionaries.i18n.CountryCode +import org.eclipse.tractusx.bpdm.common.dto.AddressType +import org.eclipse.tractusx.bpdm.common.dto.BusinessPartnerRole +import org.eclipse.tractusx.bpdm.common.dto.GeoCoordinateDto +import org.eclipse.tractusx.bpdm.common.model.BusinessStateType +import org.eclipse.tractusx.bpdm.common.model.DeliveryServiceType +import org.eclipse.tractusx.bpdm.gate.api.model.* +import org.eclipse.tractusx.bpdm.gate.api.model.request.BusinessPartnerInputRequest +import org.eclipse.tractusx.bpdm.gate.api.model.response.AddressRepresentationInputDto +import org.eclipse.tractusx.bpdm.gate.api.model.response.LegalEntityRepresentationInputDto +import org.eclipse.tractusx.bpdm.gate.api.model.response.SiteRepresentationInputDto +import org.eclipse.tractusx.bpdm.test.system.config.TestRunData +import java.time.Duration +import java.time.Instant +import java.time.ZoneOffset +import kotlin.random.Random + +class GateInputFactory( + private val testMetadata: TestMetadata, + private val testRunData: TestRunData +) { + val genericFullValidWithSiteWithoutAnyBpn = createAllFieldsFilled("genericFullValidWithSiteWithoutAnyBpn") + { it.withoutAnyBpn().withAddressType(null) } + + fun createAllFieldsFilled(seed: String, transform: (BusinessPartnerInputRequest) -> BusinessPartnerInputRequest = {it}): InputTestData { + return InputTestData(seed, transform(SeededTestDataCreator(seed).createAllFieldsFilled())) + } + + fun createFullValid(seed: String, externalId: String = seed): BusinessPartnerInputRequest { + return SeededTestDataCreator(seed).createAllFieldsFilled().copy(externalId = testRunData.toExternalId(externalId)) + } + + inner class SeededTestDataCreator( + private val seed: String, + ){ + private val longSeed = seed.hashCode().toLong() + private val random = Random(longSeed) + private val listRange = 1 .. 3 + + + fun createAllFieldsFilled(): BusinessPartnerInputRequest{ + + return BusinessPartnerInputRequest( + externalId = "${seed}_${testRunData.testTime}", + nameParts = listRange.map { "Name Part $seed $it" }, + identifiers = createIdentifiers(), + roles = BusinessPartnerRole.entries, + isOwnCompanyData = random.nextBoolean(), + states = createStates(), + legalEntity = LegalEntityRepresentationInputDto( + legalEntityBpn = "BPNL $seed", + legalName = "Legal Name $seed", + shortName = "Short Name $seed", + legalForm = testMetadata.legalForms.random(random), + states = createStates() + ), + site = SiteRepresentationInputDto( + siteBpn = "BPNS $seed", + name = "Site Name $seed", + states = createStates() + ), + address = AddressRepresentationInputDto( + addressBpn = "BPNA $seed", + name = "Address Name $seed", + addressType = AddressType.AdditionalAddress, + physicalPostalAddress = createPhysicalAddress(), + alternativePostalAddress = createAlternativeAddress(), + states = createStates() + ) + ) + } + + + private fun createIdentifiers(): List{ + return listRange.map { testMetadata.identifierTypes.random(random) } + .mapIndexed{ index, type -> BusinessPartnerIdentifierDto(type = type, value = "Identifier Value $seed $index", issuingBody = "Issuing Body $seed $index") } + } + + private fun createStates(): List{ + return random.nextTime().let { + listRange.runningFold(Pair(it, it.plus(random.nextDuration()))){ current, _ -> Pair(current.second, current.second.plus(random.nextDuration())) } + }.map { (validFrom, validTo) -> BusinessPartnerStateDto(validFrom = validFrom, validTo = validTo, BusinessStateType.entries.random(random)) } + } + + private fun createPhysicalAddress(): PhysicalPostalAddressDto{ + return PhysicalPostalAddressDto( + geographicCoordinates = GeoCoordinateDto(longitude = random.nextDouble(), latitude = random.nextDouble(), altitude = random.nextDouble()), + country = CountryCode.entries.random(random), + administrativeAreaLevel1 = testMetadata.adminAreas.random(random), + administrativeAreaLevel2 = "Admin Level 2 $seed", + administrativeAreaLevel3 = "Admin Level 3 $seed", + postalCode = "Postal Code $seed", + city = "City $seed", + district = "District $seed", + street = StreetDto( + name = "Street Name $seed", + houseNumber = "House Number $seed", + houseNumberSupplement = "House Number Supplement $seed", + milestone = "Milestone $seed", + direction = "Direction $seed", + namePrefix = "Name Prefix $seed", + nameSuffix = "Name Suffix $seed", + additionalNamePrefix = "Additional Name Prefix $seed", + additionalNameSuffix = "Additional Name Suffix $seed" + ), + companyPostalCode = "Company Postal Code $seed", + industrialZone = "Industrial Zone $seed", + building = "Building $seed", + floor = "Floor $seed", + door = "Door $seed", + taxJurisdictionCode = "123" + ) + } + + private fun createAlternativeAddress(): AlternativePostalAddressDto{ + return AlternativePostalAddressDto( + geographicCoordinates = GeoCoordinateDto(longitude = random.nextDouble(), latitude = random.nextDouble(), altitude = random.nextDouble()), + country = CountryCode.entries.random(random), + administrativeAreaLevel1 = testMetadata.adminAreas.random(random), + postalCode = "Alt Postal Code $seed", + city = "Alt City $seed", + deliveryServiceNumber = "Delivery Service Number $seed", + deliveryServiceType = DeliveryServiceType.entries.random(random), + deliveryServiceQualifier = "Delivery Service Qualifier $seed" + ) + } + + private fun Random.nextInstant() = Instant.ofEpochSecond(nextLong(0, 365241780471)) + private fun Random.nextTime() = nextInstant().atOffset(ZoneOffset.UTC).toLocalDateTime() + private fun Random.nextDuration() = Duration.ofHours(nextLong(0, 10000)) + + } +} + +data class InputTestData( + val seed: String, + val request: BusinessPartnerInputRequest +) + +data class TestMetadata( + val identifierTypes: List, + val legalForms: List, + val adminAreas: List +) + +fun BusinessPartnerInputRequest.withoutAnyBpn() = withoutLegalEntityBpn().withoutSiteBpn().withoutAddressBpn() +fun BusinessPartnerInputRequest.withAddressType(addressType: AddressType?) = copy(address = address.copy(addressType = addressType)) +fun BusinessPartnerInputRequest.withoutLegalEntityBpn() = copy(legalEntity = legalEntity.copy(legalEntityBpn = null)) +fun BusinessPartnerInputRequest.withoutSiteBpn() = copy(site = site.copy(siteBpn = null)) +fun BusinessPartnerInputRequest.withoutAddressBpn() = copy(address = address.copy(addressBpn = null)) \ No newline at end of file diff --git a/bpdm-system-tester/src/main/kotlin/org/eclipse/tractusx/bpdm/test/system/utils/GateOutputFactory.kt b/bpdm-system-tester/src/main/kotlin/org/eclipse/tractusx/bpdm/test/system/utils/GateOutputFactory.kt new file mode 100644 index 000000000..1908fb42b --- /dev/null +++ b/bpdm-system-tester/src/main/kotlin/org/eclipse/tractusx/bpdm/test/system/utils/GateOutputFactory.kt @@ -0,0 +1,95 @@ +/******************************************************************************* + * 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.bpdm.test.system.utils + +import org.eclipse.tractusx.bpdm.common.dto.AddressType +import org.eclipse.tractusx.bpdm.gate.api.model.ConfidenceCriteriaDto +import org.eclipse.tractusx.bpdm.gate.api.model.request.BusinessPartnerInputRequest +import org.eclipse.tractusx.bpdm.gate.api.model.response.AddressComponentOutputDto +import org.eclipse.tractusx.bpdm.gate.api.model.response.BusinessPartnerOutputDto +import org.eclipse.tractusx.bpdm.gate.api.model.response.LegalEntityRepresentationOutputDto +import org.eclipse.tractusx.bpdm.gate.api.model.response.SiteRepresentationOutputDto +import java.time.Instant +import java.time.LocalDateTime +import java.time.temporal.ChronoUnit + +class GateOutputFactory( + private val gateInputTestDataFactory: GateInputFactory +) { + + private val dummyConfidence = ConfidenceCriteriaDto( + sharedByOwner = false, + numberOfSharingMembers = 1, + checkedByExternalDataSource = false, + lastConfidenceCheckAt = LocalDateTime.now(), + nextConfidenceCheckAt = LocalDateTime.now().plus (5, ChronoUnit.DAYS), + confidenceLevel = 0 + ) + + fun createOutput(fromSeed: String, externalId: String = fromSeed): BusinessPartnerOutputDto{ + return createOutput(gateInputTestDataFactory.createFullValid(fromSeed, externalId)) + } + + fun createOutput( + fromInput: BusinessPartnerInputRequest + ): BusinessPartnerOutputDto{ + return BusinessPartnerOutputDto( + externalId = fromInput.externalId, + nameParts = fromInput.nameParts, + identifiers = fromInput.identifiers, + states = fromInput.states, + roles = fromInput.roles, + isOwnCompanyData = fromInput.isOwnCompanyData, + legalEntity = with(fromInput.legalEntity){ + LegalEntityRepresentationOutputDto( + legalEntityBpn = legalEntityBpn ?: "INVALID", + legalName = legalName, + shortName = shortName, + legalForm = legalForm, + confidenceCriteria = dummyConfidence, + states = states + ) + }, + site = with(fromInput.site){ + SiteRepresentationOutputDto( + siteBpn = siteBpn ?: "INVALID", + name = name, + confidenceCriteria = dummyConfidence, + states = states + ) + }, + address = with(fromInput.address){ + AddressComponentOutputDto( + addressBpn = addressBpn ?: "INVALID", + name = name, + addressType = addressType, + physicalPostalAddress = physicalPostalAddress, + alternativePostalAddress = alternativePostalAddress, + confidenceCriteria = dummyConfidence, + states = states + ) + }, + createdAt = Instant.now(), + updatedAt = Instant.now() + ) + } +} + +fun BusinessPartnerOutputDto.withAddressType(addressType: AddressType) = copy(address = address.copy(addressType = addressType)) \ No newline at end of file diff --git a/bpdm-system-tester/src/main/kotlin/org/eclipse/tractusx/bpdm/test/system/utils/StepUtils.kt b/bpdm-system-tester/src/main/kotlin/org/eclipse/tractusx/bpdm/test/system/utils/StepUtils.kt new file mode 100644 index 000000000..612878687 --- /dev/null +++ b/bpdm-system-tester/src/main/kotlin/org/eclipse/tractusx/bpdm/test/system/utils/StepUtils.kt @@ -0,0 +1,77 @@ +/******************************************************************************* + * 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.bpdm.test.system.utils + +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.time.delay +import kotlinx.coroutines.time.withTimeout +import org.assertj.core.api.Assertions.assertThat +import org.eclipse.tractusx.bpdm.common.dto.PaginationRequest +import org.eclipse.tractusx.bpdm.gate.api.client.GateClient +import org.eclipse.tractusx.bpdm.gate.api.model.ConfidenceCriteriaDto +import org.eclipse.tractusx.bpdm.gate.api.model.SharingStateType +import org.eclipse.tractusx.bpdm.gate.api.model.response.AddressComponentOutputDto +import org.eclipse.tractusx.bpdm.gate.api.model.response.BusinessPartnerOutputDto +import org.eclipse.tractusx.bpdm.gate.api.model.response.LegalEntityRepresentationOutputDto +import org.eclipse.tractusx.bpdm.gate.api.model.response.SiteRepresentationOutputDto +import java.time.Duration + +class StepUtils( + private val gateClient: GateClient +) { + + fun waitForResult(externalId: String): SharingStateType = runBlocking { + println("Waiting for result for $externalId ...") + withTimeout(Duration.ofMinutes(3)) { + while (true) { + val sharingState = gateClient.sharingState.getSharingStates(PaginationRequest(), listOf(externalId)).content.single() + if (sharingState.sharingStateType == SharingStateType.Success || sharingState.sharingStateType == SharingStateType.Error) { + return@withTimeout sharingState.sharingStateType + } + delay(Duration.ofSeconds(10)) + } + } as SharingStateType + } + + fun assertEqualIgnoreBpns(actualOutput: BusinessPartnerOutputDto, expectedOutput: BusinessPartnerOutputDto){ + assertThat(actualOutput) + .usingRecursiveComparison() + .ignoringCollectionOrder() + .ignoringFields(*ignoredFields) + .isEqualTo(expectedOutput) + } + + val ignoredFields = arrayOf( + BusinessPartnerOutputDto::createdAt.name, + BusinessPartnerOutputDto::updatedAt.name, + "${BusinessPartnerOutputDto::legalEntity.name}.${LegalEntityRepresentationOutputDto::legalEntityBpn.name}", + "${BusinessPartnerOutputDto::site.name}.${SiteRepresentationOutputDto::siteBpn.name}", + "${BusinessPartnerOutputDto::address.name}.${AddressComponentOutputDto::addressBpn.name}", + // ToDo: Cleaning service dummy should have fixed confidence criteria dummy times otherwise we need to keep ignoring these fields + "${BusinessPartnerOutputDto::legalEntity.name}.${AddressComponentOutputDto::confidenceCriteria.name}.${ConfidenceCriteriaDto::lastConfidenceCheckAt.name}", + "${BusinessPartnerOutputDto::legalEntity.name}.${AddressComponentOutputDto::confidenceCriteria.name}.${ConfidenceCriteriaDto::nextConfidenceCheckAt.name}", + "${BusinessPartnerOutputDto::site.name}.${AddressComponentOutputDto::confidenceCriteria.name}.${ConfidenceCriteriaDto::lastConfidenceCheckAt.name}", + "${BusinessPartnerOutputDto::site.name}.${AddressComponentOutputDto::confidenceCriteria.name}.${ConfidenceCriteriaDto::nextConfidenceCheckAt.name}", + "${BusinessPartnerOutputDto::address.name}.${AddressComponentOutputDto::confidenceCriteria.name}.${ConfidenceCriteriaDto::lastConfidenceCheckAt.name}", + "${BusinessPartnerOutputDto::address.name}.${AddressComponentOutputDto::confidenceCriteria.name}.${ConfidenceCriteriaDto::nextConfidenceCheckAt.name}", + ) + + +} \ No newline at end of file diff --git a/bpdm-system-tester/src/main/resources/application.yml b/bpdm-system-tester/src/main/resources/application.yml new file mode 100644 index 000000000..67a04b585 --- /dev/null +++ b/bpdm-system-tester/src/main/resources/application.yml @@ -0,0 +1,13 @@ +bpdm: + client: + pool: + enabled: false + provider: + issuer-uri: ${bpdm.security.auth-server-url:http://localhost:8180}/realms/${bpdm.security.realm:master} + registration: + authorization-grant-type: client_credentials + # Use a default client id for the client credentials request + client-id: BPDM-POOL + # Please provide a secret here +cucumber: + glue: "org.eclipse.tractusx.bpdm.test.system.stepdefinations" diff --git a/bpdm-system-tester/src/main/resources/cucumber/share_generic_business_partner.feature b/bpdm-system-tester/src/main/resources/cucumber/share_generic_business_partner.feature new file mode 100644 index 000000000..c68c564aa --- /dev/null +++ b/bpdm-system-tester/src/main/resources/cucumber/share_generic_business_partner.feature @@ -0,0 +1,34 @@ +Feature: Share Valid Generic Business Partner without BPNs + Scenario: Update Without Address Type + Given output "CC_SHG_UWAT_1" with external-ID "CC_SHG_UWAT" + When the sharing member uploads full valid input "CC_SHG_UWAT_2" with external-ID "CC_SHG_UWAT" without address type + Then the sharing member receives output "CC_SHG_UWAT_2" with external-ID "CC_SHG_UWAT" with address type "LegalAndSiteMainAddress" + + Scenario: Share Without Address Type + When the sharing member uploads full valid input "CC_SHG_WAT" with external-ID "CC_SHG_WAT" without address type + Then the sharing member receives output "CC_SHG_WAT" with external-ID "CC_SHG_WAT" with address type "LegalAndSiteMainAddress" + + Scenario Outline: Share With Address Type + When the sharing member uploads full valid input "CC_SHG_WAT" with external-ID "" with address type "" + Then the sharing member receives output "CC_SHG_WAT" with external-ID "" with address type "" + + Examples: + | externalId | inputAddressType | outputAddressType | + | CC_SHG_WAT_1 | LegalAndSiteMainAddress | LegalAndSiteMainAddress | + | CC_SHG_WAT_2 | LegalAddress | LegalAndSiteMainAddress | + | CC_SHG_WAT_3 | SiteMainAddress | SiteMainAddress | + | CC_SHG_WAT_4 | AdditionalAddress | AdditionalAddress | + + Scenario Outline: Share With Missing or Invalid data + When the sharing member uploads input "CC_SHG_WATT" with external-ID "" without mandatory field "" + Then the sharing member receives sharing error "CC_SHG_WATT" with external-ID "" with error message "" + + Examples: + | externalId | mandatoryField | errorMessage | + | CC_SHG_WATT_1 | legalName | Legal name is null | + | CC_SHG_WATT_2 | physicalAddress.country | Physical Address has no country | + | CC_SHG_WATT_3 | physicalAddress.city | Physical Address has no city | + | CC_SHG_WATT_4 | alternativeAddress.country | Alternative Address has no country | + | CC_SHG_WATT_5 | alternativeAddress.city | Alternative Address has no city | + | CC_SHG_WATT_6 | alternativeAddress.deliveryServiceType | Alternative Address has no delivery service type | + | CC_SHG_WATT_7 | alternativeAddress.deliveryServiceNumber | Alternative Address has no delivery service number | \ No newline at end of file diff --git a/pom.xml b/pom.xml index bcb08a0e0..5e167bf43 100644 --- a/pom.xml +++ b/pom.xml @@ -46,6 +46,7 @@ bpdm-cleaning-service-dummy bpdm-orchestrator bpdm-orchestrator-api + bpdm-system-tester