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