From df2ad94ee9b641a4c2c19969816a6275f6d056e3 Mon Sep 17 00:00:00 2001 From: Fiete Ostkamp Date: Thu, 11 Apr 2024 20:25:29 +0200 Subject: [PATCH] Add model-loader integration tests - add integration tests that assert the external communication towards other services using Wiremock - remove tests that are asserting getters and setters of objects Issue-ID: AAI-3826 Change-Id: I1f627801869f40cb0eaa61b10148b41bd3b1bdb8 Signed-off-by: Fiete Ostkamp --- pom.xml | 11 + .../onap/aai/modelloader/config/BeanConfig.java | 18 +- .../config/DistributionClientStartupConfig.java | 7 +- .../aai/modelloader/config/ModelLoaderConfig.java | 34 ++++ .../notification/NotificationDataImpl.java | 94 ++------- .../aai/modelloader/restclient/AaiRestClient.java | 2 + .../restclient/HttpsBabelServiceClient.java | 4 +- .../aai/modelloader/service/ArtifactInfoImpl.java | 109 +--------- .../DistributionClientTestConfiguration.java | 81 ++++++++ .../modelloader/TestModelLoaderApplication.java | 44 ---- .../distribution/NotificationIntegrationTest.java | 14 +- .../notification/ArtifactDownloadManagerTest.java | 107 ++++++++++ .../notification/ModelArtifactHandlerTest.java | 226 +++++++++++++++++++++ .../notification/TestNotificationDataImpl.java | 83 -------- .../restclient/TestBabelServiceClient.java | 90 ++++---- .../aai/modelloader/restclient/TracingTest.java | 63 ++++++ .../modelloader/service/TestArtifactInfoImpl.java | 123 ----------- src/test/resources/__files/artifactTypes.json | 63 ++++++ src/test/resources/__files/kafkaBootstrap.json | 5 + src/test/resources/__files/modelResponse.xml | 7 + .../service-TestSvc-csar-babel-response.json | 1 + .../resources/__files/service-TestSvc-csar.csar | Bin 0 -> 55342 bytes src/test/resources/application.properties | 9 +- src/test/resources/logback-test.xml | 2 +- src/test/resources/model-loader.properties | 8 +- 25 files changed, 696 insertions(+), 509 deletions(-) create mode 100644 src/test/java/org/onap/aai/modelloader/DistributionClientTestConfiguration.java delete mode 100644 src/test/java/org/onap/aai/modelloader/TestModelLoaderApplication.java create mode 100644 src/test/java/org/onap/aai/modelloader/notification/ArtifactDownloadManagerTest.java create mode 100644 src/test/java/org/onap/aai/modelloader/notification/ModelArtifactHandlerTest.java delete mode 100644 src/test/java/org/onap/aai/modelloader/notification/TestNotificationDataImpl.java create mode 100644 src/test/java/org/onap/aai/modelloader/restclient/TracingTest.java delete mode 100644 src/test/java/org/onap/aai/modelloader/service/TestArtifactInfoImpl.java create mode 100644 src/test/resources/__files/artifactTypes.json create mode 100644 src/test/resources/__files/kafkaBootstrap.json create mode 100644 src/test/resources/__files/modelResponse.xml create mode 100644 src/test/resources/__files/service-TestSvc-csar-babel-response.json create mode 100644 src/test/resources/__files/service-TestSvc-csar.csar diff --git a/pom.xml b/pom.xml index 644e408..1bf31f4 100644 --- a/pom.xml +++ b/pom.xml @@ -384,6 +384,17 @@ org.springframework.boot spring-boot-starter-test test + + + com.vaadin.external.google + android-json + + + + + org.springframework.cloud + spring-cloud-contract-wiremock + test diff --git a/src/main/java/org/onap/aai/modelloader/config/BeanConfig.java b/src/main/java/org/onap/aai/modelloader/config/BeanConfig.java index 8f7b2bb..63956e2 100644 --- a/src/main/java/org/onap/aai/modelloader/config/BeanConfig.java +++ b/src/main/java/org/onap/aai/modelloader/config/BeanConfig.java @@ -30,9 +30,11 @@ import org.onap.aai.cl.eelf.LoggerFactory; import org.onap.aai.modelloader.service.ModelLoaderMsgs; import org.onap.sdc.api.IDistributionClient; import org.onap.sdc.impl.DistributionClientFactory; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.web.client.RestTemplate; @Configuration public class BeanConfig { @@ -44,13 +46,18 @@ public class BeanConfig { private String configDir; @Bean - public ModelLoaderConfig modelLoaderConfig() throws IOException { + public Properties configProperties() throws IOException { // Load model loader system configuration logger.info(ModelLoaderMsgs.LOADING_CONFIGURATION); - ModelLoaderConfig.setConfigHome(configDir); - Properties configProperties = new Properties(); InputStream configInputStream = Files.newInputStream(Paths.get(configDir, "model-loader.properties")); + Properties configProperties = new Properties(); configProperties.load(configInputStream); + return configProperties; + } + + @Bean + public ModelLoaderConfig modelLoaderConfig(Properties configProperties) { + ModelLoaderConfig.setConfigHome(configDir); return new ModelLoaderConfig(configProperties); } @@ -58,4 +65,9 @@ public class BeanConfig { public IDistributionClient iDistributionClient() { return DistributionClientFactory.createDistributionClient(); } + + @Bean + public RestTemplate restTemplate() { + return new RestTemplate(); + } } diff --git a/src/main/java/org/onap/aai/modelloader/config/DistributionClientStartupConfig.java b/src/main/java/org/onap/aai/modelloader/config/DistributionClientStartupConfig.java index 84c79f2..cd9d919 100644 --- a/src/main/java/org/onap/aai/modelloader/config/DistributionClientStartupConfig.java +++ b/src/main/java/org/onap/aai/modelloader/config/DistributionClientStartupConfig.java @@ -33,10 +33,10 @@ import org.onap.sdc.api.results.IDistributionClientResult; import org.onap.sdc.utils.DistributionActionResultEnum; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.context.annotation.Configuration; import org.springframework.context.event.EventListener; -import org.springframework.stereotype.Component; -@Component +@Configuration @ConditionalOnProperty(value = "ml.distribution.connection.enabled", havingValue = "true", matchIfMissing = true) public class DistributionClientStartupConfig { @@ -57,7 +57,8 @@ public class DistributionClientStartupConfig { protected void initSdcClient() { // Initialize distribution client logger.debug(ModelLoaderMsgs.INITIALIZING, "Initializing distribution client..."); - IDistributionClientResult initResult = client.init(config, eventCallback); + IDistributionClientResult initResult = null; + initResult = client.init(config, eventCallback); if (initResult.getDistributionActionResult() == DistributionActionResultEnum.SUCCESS) { // Start distribution client diff --git a/src/main/java/org/onap/aai/modelloader/config/ModelLoaderConfig.java b/src/main/java/org/onap/aai/modelloader/config/ModelLoaderConfig.java index 7da90d9..6723e75 100644 --- a/src/main/java/org/onap/aai/modelloader/config/ModelLoaderConfig.java +++ b/src/main/java/org/onap/aai/modelloader/config/ModelLoaderConfig.java @@ -77,6 +77,9 @@ public class ModelLoaderConfig implements IConfiguration { protected static final String PROP_ML_DISTRIBUTION_HTTP_PROXY_PORT = PREFIX_DISTRIBUTION_CLIENT + "HTTP_PROXY_PORT"; protected static final String PROP_ML_DISTRIBUTION_HTTPS_PROXY_HOST = PREFIX_DISTRIBUTION_CLIENT + "HTTPS_PROXY_HOST"; protected static final String PROP_ML_DISTRIBUTION_HTTPS_PROXY_PORT = PREFIX_DISTRIBUTION_CLIENT + "HTTPS_PROXY_PORT"; + protected static final String PROP_ML_DISTRIBUTION_SASL_JAAS_CONFIG = PREFIX_DISTRIBUTION_CLIENT + "SASL_JAAS_CONFIG"; + protected static final String PROP_ML_DISTRIBUTION_SASL_MECHANISM = PREFIX_DISTRIBUTION_CLIENT + "SASL_MECHANISM"; + protected static final String PROP_ML_DISTRIBUTION_SECURITY_PROTOCOL = PREFIX_DISTRIBUTION_CLIENT + "SECURITY_PROTOCOL"; protected static final String PROP_AAI_BASE_URL = PREFIX_AAI + "BASE_URL"; protected static final String PROP_AAI_KEYSTORE_FILE = PREFIX_AAI + SUFFIX_KEYSTORE_FILE; protected static final String PROP_AAI_KEYSTORE_PASSWORD = PREFIX_AAI + SUFFIX_KEYSTORE_PASS; @@ -413,4 +416,35 @@ public class ModelLoaderConfig implements IConfiguration { return Integer.parseInt(connectTimeout); } + @Override + public String getKafkaSaslJaasConfig() { + String saslJaasConfFromEnv = System.getenv("SASL_JAAS_CONFIG"); + if(saslJaasConfFromEnv != null) { + return saslJaasConfFromEnv; + } + if(get(PROP_ML_DISTRIBUTION_SASL_JAAS_CONFIG) != null) { + return get(PROP_ML_DISTRIBUTION_SASL_JAAS_CONFIG); + } + return null; + } + + @Override + public String getKafkaSaslMechanism() { + if(get(PROP_ML_DISTRIBUTION_SASL_MECHANISM) != null) { + return get(PROP_ML_DISTRIBUTION_SASL_MECHANISM); + } + return System.getenv().getOrDefault("SASL_MECHANISM", "SCRAM-SHA-512"); + } + + /** + * One of PLAINTEXT, SSL, SASL_PLAINTEXT, SASL_SSL + */ + @Override + public String getKafkaSecurityProtocolConfig() { + if(get(PROP_ML_DISTRIBUTION_SECURITY_PROTOCOL) != null) { + return get(PROP_ML_DISTRIBUTION_SECURITY_PROTOCOL); + } + return System.getenv().getOrDefault("SECURITY_PROTOCOL", "SASL_PLAINTEXT"); + } + } diff --git a/src/main/java/org/onap/aai/modelloader/notification/NotificationDataImpl.java b/src/main/java/org/onap/aai/modelloader/notification/NotificationDataImpl.java index 566415e..3eb07f3 100644 --- a/src/main/java/org/onap/aai/modelloader/notification/NotificationDataImpl.java +++ b/src/main/java/org/onap/aai/modelloader/notification/NotificationDataImpl.java @@ -20,98 +20,28 @@ */ package org.onap.aai.modelloader.notification; -import java.util.Collections; import java.util.List; import org.onap.sdc.api.notification.IArtifactInfo; import org.onap.sdc.api.notification.INotificationData; import org.onap.sdc.api.notification.IResourceInstance; +import lombok.Data; + +@Data public class NotificationDataImpl implements INotificationData { private String distributionID; + private String serviceName; + private String serviceVersion; + private String serviceUUID; + private String serviceDescription; + private List resources; + private List serviceArtifacts; + private String serviceInvariantUUID; + private String workloadContext; @Override - public IArtifactInfo getArtifactMetadataByUUID(String arg0) { - return null; - } - - @Override - public String getDistributionID() { - return distributionID; - } - - public void setDistributionID(String distributionID) { - this.distributionID = distributionID; - } - - @Override - public List getResources() { - return Collections.emptyList(); - } - - @Override - public List getServiceArtifacts() { - return Collections.emptyList(); - } - - @Override - public String getServiceDescription() { - return null; - } - - @Override - public String getServiceInvariantUUID() { - return null; - } - - @Override - public String getServiceName() { - return null; - } - - @Override - public String getServiceUUID() { - return null; - } - - @Override - public String getServiceVersion() { + public IArtifactInfo getArtifactMetadataByUUID(String uuid) { return null; } - - @Override - public String getWorkloadContext() { - return null; - } - - @Override - public void setWorkloadContext(String arg0) { - // Unsupported method - not expected to be called - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((distributionID == null) ? 0 : distributionID.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - NotificationDataImpl other = (NotificationDataImpl) obj; - if (distributionID == null) { - if (other.distributionID != null) - return false; - } else if (!distributionID.equals(other.distributionID)) - return false; - return true; - } - } diff --git a/src/main/java/org/onap/aai/modelloader/restclient/AaiRestClient.java b/src/main/java/org/onap/aai/modelloader/restclient/AaiRestClient.java index 40aeacc..45f84d6 100644 --- a/src/main/java/org/onap/aai/modelloader/restclient/AaiRestClient.java +++ b/src/main/java/org/onap/aai/modelloader/restclient/AaiRestClient.java @@ -42,6 +42,7 @@ import org.onap.aai.modelloader.service.ModelLoaderMsgs; import org.onap.aai.restclient.client.OperationResult; import org.onap.aai.restclient.client.RestClient; import org.onap.aai.restclient.enums.RestAuthenticationMode; +import org.springframework.stereotype.Component; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.NodeList; @@ -52,6 +53,7 @@ import org.xml.sax.SAXException; * Wrapper around the standard A&AI Rest Client interface. This currently uses Jersey client 1.x * */ +@Component public class AaiRestClient { public static final String HEADER_TRANS_ID = "X-TransactionId"; diff --git a/src/main/java/org/onap/aai/modelloader/restclient/HttpsBabelServiceClient.java b/src/main/java/org/onap/aai/modelloader/restclient/HttpsBabelServiceClient.java index c76996f..0789996 100644 --- a/src/main/java/org/onap/aai/modelloader/restclient/HttpsBabelServiceClient.java +++ b/src/main/java/org/onap/aai/modelloader/restclient/HttpsBabelServiceClient.java @@ -229,8 +229,8 @@ public class HttpsBabelServiceClient implements BabelServiceClient { String resourceUrl = config.getBabelBaseUrl() + config.getBabelGenerateArtifactsUrl(); WebResource webResource = client.resource(resourceUrl); ClientResponse response = webResource.type("application/json") - .header(AaiRestClient.HEADER_TRANS_ID, Collections.singletonList(transactionId)) - .header(AaiRestClient.HEADER_FROM_APP_ID, Collections.singletonList(AaiRestClient.ML_APP_NAME)) + .header(AaiRestClient.HEADER_TRANS_ID, transactionId) + .header(AaiRestClient.HEADER_FROM_APP_ID, AaiRestClient.ML_APP_NAME) .post(ClientResponse.class, obj.toString()); String sanitizedJson = JsonSanitizer.sanitize(response.getEntity(String.class)); diff --git a/src/main/java/org/onap/aai/modelloader/service/ArtifactInfoImpl.java b/src/main/java/org/onap/aai/modelloader/service/ArtifactInfoImpl.java index 9af92be..f83e44d 100644 --- a/src/main/java/org/onap/aai/modelloader/service/ArtifactInfoImpl.java +++ b/src/main/java/org/onap/aai/modelloader/service/ArtifactInfoImpl.java @@ -20,116 +20,25 @@ */ package org.onap.aai.modelloader.service; -import java.util.Collections; import java.util.List; import org.onap.sdc.api.notification.IArtifactInfo; +import lombok.Data; + /** * This class is an implementation of IArtifactInfo for test purposes. */ +@Data public class ArtifactInfoImpl implements IArtifactInfo { private String artifactName; private String artifactType; private String artifactDescription; private String artifactVersion; - - @Override - public String getArtifactName() { - return artifactName; - } - - public void setArtifactName(String artifactName) { - this.artifactName = artifactName; - } - - @Override - public String getArtifactType() { - return artifactType; - } - - public void setArtifactType(String artifactType) { - this.artifactType = artifactType; - } - - @Override - public String getArtifactURL() { - return null; - } - - @Override - public String getArtifactChecksum() { - return null; - } - - @Override - public String getArtifactDescription() { - return artifactDescription; - } - - public void setArtifactDescription(String artifactDescription) { - this.artifactDescription = artifactDescription; - } - - @Override - public Integer getArtifactTimeout() { - return null; - } - - @Override - public String getArtifactVersion() { - return artifactVersion; - } - - public void setArtifactVersion(String artifactVersion) { - this.artifactVersion = artifactVersion; - } - - @Override - public String getArtifactUUID() { - return null; - } - - @Override - public IArtifactInfo getGeneratedArtifact() { - return null; - } - - @Override - public List getRelatedArtifacts() { - return Collections.emptyList(); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - - ArtifactInfoImpl that = (ArtifactInfoImpl) o; - - if (artifactName != null ? !artifactName.equals(that.artifactName) : that.artifactName != null) { - return false; - } - if (artifactType != null ? !artifactType.equals(that.artifactType) : that.artifactType != null) { - return false; - } - if (artifactDescription != null ? !artifactDescription.equals(that.artifactDescription) - : that.artifactDescription != null) { - return false; - } - return artifactVersion != null ? artifactVersion.equals(that.artifactVersion) : that.artifactVersion == null; - } - - @Override - public int hashCode() { - int result = artifactName != null ? artifactName.hashCode() : 0; - result = 31 * result + (artifactType != null ? artifactType.hashCode() : 0); - result = 31 * result + (artifactDescription != null ? artifactDescription.hashCode() : 0); - result = 31 * result + (artifactVersion != null ? artifactVersion.hashCode() : 0); - return result; - } + private String artifactURL; + private String artifactChecksum; + private Integer artifactTimeout; + private String artifactUUID; + private IArtifactInfo generatedArtifact; + private List relatedArtifacts; } diff --git a/src/test/java/org/onap/aai/modelloader/DistributionClientTestConfiguration.java b/src/test/java/org/onap/aai/modelloader/DistributionClientTestConfiguration.java new file mode 100644 index 0000000..1eed62f --- /dev/null +++ b/src/test/java/org/onap/aai/modelloader/DistributionClientTestConfiguration.java @@ -0,0 +1,81 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2024 Deutsche Telekom AG Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://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. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.modelloader; + +import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; +import static com.github.tomakehurst.wiremock.client.WireMock.get; +import static com.github.tomakehurst.wiremock.client.WireMock.matching; +import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; +import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Properties; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.context.event.ApplicationStartedEvent; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Primary; +import org.springframework.context.event.EventListener; + +@TestConfiguration +public class DistributionClientTestConfiguration { + + @Value("${CONFIG_HOME}") + private String configDir; + + @Value("${wiremock.server.port}") + private int wiremockPort; + + @Primary + @Bean(name = "testProperties") + public Properties configProperties() throws IOException { + // Load model loader system configuration + InputStream configInputStream = Files.newInputStream(Paths.get(configDir, "model-loader.properties")); + Properties configProperties = new Properties(); + configProperties.load(configInputStream); + + setOverrides(configProperties); + + return configProperties; + } + + private void setOverrides(Properties configProperties) { + configProperties.setProperty("ml.distribution.ASDC_ADDRESS", "localhost:" + wiremockPort); + configProperties.setProperty("ml.babel.BASE_URL", "http://localhost:" + wiremockPort); + } + + @EventListener(ApplicationStartedEvent.class) + public void mockSdcInit() { + stubFor(get(urlEqualTo("/sdc/v1/artifactTypes")) + .withHeader("X-ECOMP-RequestID", matching(".+")) + .withHeader("X-ECOMP-InstanceID", equalTo("aai-ml-id-test")) + .willReturn(aResponse().withHeader("Content-Type", "application/json").withBodyFile("artifactTypes.json"))); + + stubFor(get(urlEqualTo("/sdc/v1/distributionKafkaData")) + .withHeader("X-ECOMP-RequestID", matching(".+")) + .withHeader("X-ECOMP-InstanceID", equalTo("aai-ml-id-test")) + .willReturn(aResponse().withHeader("Content-Type", "application/json").withBodyFile("kafkaBootstrap.json"))); + } +} \ No newline at end of file diff --git a/src/test/java/org/onap/aai/modelloader/TestModelLoaderApplication.java b/src/test/java/org/onap/aai/modelloader/TestModelLoaderApplication.java deleted file mode 100644 index cd39066..0000000 --- a/src/test/java/org/onap/aai/modelloader/TestModelLoaderApplication.java +++ /dev/null @@ -1,44 +0,0 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. - * Copyright © 2017-2018 European Software Marketing Ltd. - * ================================================================================ - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://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. - * ============LICENSE_END========================================================= - */ -package org.onap.aai.modelloader; - -import static org.junit.jupiter.api.Assertions.assertTrue; - -import org.junit.jupiter.api.Test; - -/** - * Tests for ModelLoaderApplication class. - * - */ -public class TestModelLoaderApplication { - - static { - System.setProperty("CONFIG_HOME", "src/test/resources"); - } - - @Test - public void testServiceStarts() { - // The SDC Distribution Client is disabled. - ModelLoaderApplication.main(new String[0]); - assertTrue(true); - } - -} diff --git a/src/test/java/org/onap/aai/modelloader/distribution/NotificationIntegrationTest.java b/src/test/java/org/onap/aai/modelloader/distribution/NotificationIntegrationTest.java index 2645871..abdefd4 100644 --- a/src/test/java/org/onap/aai/modelloader/distribution/NotificationIntegrationTest.java +++ b/src/test/java/org/onap/aai/modelloader/distribution/NotificationIntegrationTest.java @@ -26,6 +26,7 @@ import java.util.concurrent.TimeUnit; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; +import org.onap.aai.modelloader.notification.NotificationDataImpl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.test.context.SpringBootTest; @@ -35,25 +36,26 @@ import org.springframework.test.annotation.DirtiesContext; @DirtiesContext @SpringBootTest(properties = { "model-loader.sdc.connection.enabled=true"}) -@EmbeddedKafka(partitions = 1, brokerProperties = { "listeners=PLAINTEXT://localhost:9092", "port=9092" }) +@EmbeddedKafka(partitions = 1, ports = 9092, topics = {"${topics.distribution.notification}"}) public class NotificationIntegrationTest { @Autowired EventCallbackAspect eventCallbackAspect; @Autowired - private KafkaTemplate kafkaTemplate; + private KafkaTemplate kafkaTemplate; - @Value("${test.topic}") + @Value("${topics.distribution.notification}") private String topic; @Test @Disabled("This test is not yet implemented") public void thatActivateCallbackIsCalled() throws Exception { - String data = "Smth"; - + NotificationDataImpl notificationData = new NotificationDataImpl(); + notificationData.setDistributionID("distributionID"); + // TODO: send distribution event here - kafkaTemplate.send(topic, data); + kafkaTemplate.send(topic, notificationData); // TODO: mock distribution client requests to /sdc/v1/artifactTypes // TODO: mock distribution client requests to /sdc/v1/distributionKafkaData diff --git a/src/test/java/org/onap/aai/modelloader/notification/ArtifactDownloadManagerTest.java b/src/test/java/org/onap/aai/modelloader/notification/ArtifactDownloadManagerTest.java new file mode 100644 index 0000000..0985790 --- /dev/null +++ b/src/test/java/org/onap/aai/modelloader/notification/ArtifactDownloadManagerTest.java @@ -0,0 +1,107 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2024 Deutsche Telekom AG Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://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. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.modelloader.notification; + +import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; +import static com.github.tomakehurst.wiremock.client.WireMock.get; +import static com.github.tomakehurst.wiremock.client.WireMock.matching; +import static com.github.tomakehurst.wiremock.client.WireMock.matchingJsonPath; +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.urlEqualTo; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.http.entity.ContentType; +import org.junit.jupiter.api.Test; +import org.onap.aai.modelloader.DistributionClientTestConfiguration; +import org.onap.aai.modelloader.entity.Artifact; +import org.onap.aai.modelloader.entity.ArtifactType; +import org.onap.aai.modelloader.service.ArtifactInfoImpl; +import org.onap.sdc.api.notification.IArtifactInfo; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.contract.wiremock.AutoConfigureWireMock; +import org.springframework.context.annotation.Import; +import org.springframework.http.MediaType; +import org.springframework.kafka.test.context.EmbeddedKafka; +import org.springframework.test.annotation.DirtiesContext; + +import com.fasterxml.jackson.core.JsonProcessingException; + +@DirtiesContext +@AutoConfigureWireMock(port = 0) +@EmbeddedKafka(partitions = 1, ports = 9092, topics = {"${topics.distribution.notification}"}) +@SpringBootTest(properties = { "ml.distribution.connection.enabled=true" }) +@Import(DistributionClientTestConfiguration.class) +public class ArtifactDownloadManagerTest { + + @Autowired ArtifactDownloadManager artifactDownloadManager; + + @Test + public void downloadArtifacts() throws JsonProcessingException { + NotificationDataImpl notificationData = new NotificationDataImpl(); + notificationData.setDistributionID("distributionID"); + notificationData.setServiceVersion("2.0"); + + stubFor(get(urlEqualTo("/sdc/v1/catalog/services/DemovlbCds/1.0/artifacts/service-TestSvc-csar.csar")) + .withHeader("Accept", equalTo(ContentType.APPLICATION_OCTET_STREAM.toString())) + .withHeader("X-ECOMP-RequestID", matching(".+")) + .withHeader("X-ECOMP-InstanceID", equalTo("aai-ml-id-test")) + .willReturn(aResponse() + .withHeader("Content-Type", MediaType.APPLICATION_OCTET_STREAM.toString()) + .withBodyFile("service-TestSvc-csar.csar"))); + + stubFor( + post(urlEqualTo("/services/babel-service/v1/app/generateArtifacts")) + .withHeader("X-TransactionId", equalTo("distributionID")) + .withHeader("X-FromAppId", equalTo("ModelLoader")) + .withHeader("Content-Type", equalTo(MediaType.APPLICATION_JSON_VALUE)) + .withRequestBody(matchingJsonPath("$.artifactName", equalTo("service-TestSvc-csar.csar"))) + .withRequestBody(matchingJsonPath("$.artifactVersion", equalTo("2.0"))) + .withRequestBody(matchingJsonPath("$.csar", matching(".*"))) + .willReturn( + aResponse() + .withHeader("Content-Type", "application/json") + .withBodyFile("service-TestSvc-csar-babel-response.json"))); + + ArtifactInfoImpl artifactInfo = new ArtifactInfoImpl(); + artifactInfo.setArtifactName("service-TestSvc-csar.csar"); + artifactInfo.setArtifactVersion("1.0"); + artifactInfo.setArtifactURL("/sdc/v1/catalog/services/DemovlbCds/1.0/artifacts/service-TestSvc-csar.csar"); + artifactInfo.setArtifactType("TOSCA_CSAR"); + artifactInfo.setArtifactChecksum("ZmI5NzQ1MWViZGFkMjRjZWEwNTQzY2U0OWQwYjlmYjQ="); + artifactInfo.setArtifactUUID("f6f907f1-3f45-4fb4-8cbe-15a4c6ee16db"); + List artifacts = new ArrayList<>(); + artifacts.add(artifactInfo); + List modelArtifacts = new ArrayList<>(); // processed artifacts will be written to this list + List catalogArtifacts = new ArrayList<>(); // processed artifacts will be written to this list + boolean result = artifactDownloadManager.downloadArtifacts(notificationData, artifacts, modelArtifacts, catalogArtifacts); + + assertEquals(1, modelArtifacts.size()); + assertEquals(ArtifactType.MODEL, modelArtifacts.get(0).getType()); + assertTrue(result); + } + +} diff --git a/src/test/java/org/onap/aai/modelloader/notification/ModelArtifactHandlerTest.java b/src/test/java/org/onap/aai/modelloader/notification/ModelArtifactHandlerTest.java new file mode 100644 index 0000000..b1269ee --- /dev/null +++ b/src/test/java/org/onap/aai/modelloader/notification/ModelArtifactHandlerTest.java @@ -0,0 +1,226 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2024 Deutsche Telekom AG Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://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. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.modelloader.notification; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; +import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; +import static com.github.tomakehurst.wiremock.client.WireMock.verify; +import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; +import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.deleteRequestedFor; +import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; +import static com.github.tomakehurst.wiremock.client.WireMock.ok; + +import java.util.List; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.onap.aai.modelloader.config.ModelLoaderConfig; +import org.onap.aai.modelloader.entity.Artifact; +import org.onap.aai.modelloader.entity.model.ModelArtifact; +import org.onap.aai.modelloader.entity.model.ModelArtifactHandler; +import org.onap.aai.modelloader.restclient.AaiRestClient; +import org.onap.aai.restclient.client.OperationResult; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.contract.wiremock.AutoConfigureWireMock; +import org.springframework.http.HttpStatus; +import org.springframework.web.client.RestTemplate; +import org.w3c.dom.Node; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.tomakehurst.wiremock.client.WireMock; + +import java.util.ArrayList; + +@SpringBootTest +@AutoConfigureWireMock(port = 0) +public class ModelArtifactHandlerTest { + + @Value("${wiremock.server.port}") + private int wiremockPort; + + final ObjectMapper objectMapper = new ObjectMapper(); + @Mock + ModelLoaderConfig config; + @InjectMocks + ModelArtifactHandler modelArtifactHandler; + + @Autowired + AaiRestClient restClient; + + @BeforeEach + public void setUp() { + when(config.getAaiBaseUrl()).thenReturn("http://localhost:" + wiremockPort); + when(config.getAaiModelUrl(any())).thenReturn("/aai/v28/service-design-and-creation/models/model/"); + } + + @Test + public void thatArtifactsCanBeCreated() { + WireMock.stubFor( + WireMock.get(urlEqualTo("/aai/v28/service-design-and-creation/models/model/modelInvariantId")) + .withHeader("Accept", equalTo("application/xml")) + .withHeader("X-TransactionId", equalTo("someId")) + .withHeader("X-FromAppId", equalTo("ModelLoader")) + .willReturn( + WireMock.aResponse() + .withStatus(HttpStatus.NOT_FOUND.value()))); + + WireMock.stubFor( + WireMock.put(urlEqualTo("/aai/v28/service-design-and-creation/models/model/modelInvariantId")) + .withHeader("Content-Type", equalTo("application/xml")) + .withHeader("X-TransactionId", equalTo("someId")) + .withHeader("X-FromAppId", equalTo("ModelLoader")) + .willReturn( + WireMock.aResponse() + .withStatus(HttpStatus.CREATED.value()))); + + ModelArtifact modelArtifact = new ModelArtifact(); + modelArtifact.setModelInvariantId("modelInvariantId"); + List artifacts = List.of(modelArtifact); + List completedArtifacts = new ArrayList<>(); + + boolean result = modelArtifactHandler.pushArtifacts(artifacts, + "someId", completedArtifacts, restClient); + assertTrue(result); + WireMock.verify( + WireMock.putRequestedFor(urlEqualTo("/aai/v28/service-design-and-creation/models/model/modelInvariantId"))); + } + + @Test + public void thatArtifactsCanBeUpdated() { + // Checks if model exists in resources service + WireMock.stubFor( + WireMock.get(urlEqualTo("/aai/v28/service-design-and-creation/models/model/modelInvariantId")) + .withHeader("Accept", equalTo("application/xml")) + .withHeader("X-TransactionId", equalTo("distributionId")) + .withHeader("X-FromAppId", equalTo("ModelLoader")) + .willReturn( + WireMock.aResponse() + .withStatus(HttpStatus.OK.value()))); + + // Checks if specific version of model exists in aai-resources + WireMock.stubFor( + WireMock.get(urlEqualTo( + "/aai/v28/service-design-and-creation/models/model/modelInvariantId/model-vers/model-ver/modelVersionId")) + .withHeader("Accept", equalTo("application/xml")) + .withHeader("X-TransactionId", equalTo("distributionId")) + .withHeader("X-FromAppId", equalTo("ModelLoader")) + .willReturn( + WireMock.aResponse() + .withStatus(HttpStatus.NOT_FOUND.value()))); + + WireMock.stubFor( + WireMock.put(urlEqualTo( + "/aai/v28/service-design-and-creation/models/model/modelInvariantId/model-vers/model-ver/modelVersionId")) + .withHeader("Content-Type", equalTo("application/xml")) + .withHeader("X-TransactionId", equalTo("distributionId")) + .withHeader("X-FromAppId", equalTo("ModelLoader")) + .willReturn( + WireMock.aResponse() + .withStatus(HttpStatus.CREATED.value()))); + + ModelArtifact modelArtifact = new ModelArtifact(); + modelArtifact.setModelInvariantId("modelInvariantId"); + modelArtifact.setModelVerId("modelVersionId"); + Node node = Mockito.mock(Node.class); + modelArtifact.setModelVer(node); + List artifacts = List.of(modelArtifact); + List completedArtifacts = new ArrayList<>(); + + boolean result = modelArtifactHandler.pushArtifacts(artifacts, + "distributionId", completedArtifacts, restClient); + verify(WireMock.putRequestedFor(urlEqualTo( + "/aai/v28/service-design-and-creation/models/model/modelInvariantId/model-vers/model-ver/modelVersionId"))); + assertTrue(result); + } + + @Test + public void thatModelCanBeRolledBack() { + stubFor(WireMock.get(urlEqualTo("/aai/v28/service-design-and-creation/models/model/3a40ab73-6694-4e75-bb6d-9a4a86ce35b3")) + .withHeader("X-FromAppId", equalTo("ModelLoader")) + .withHeader("X-TransactionId", equalTo("distributionId")) + .willReturn(aResponse() + .withBodyFile("modelResponse.xml"))); + + stubFor(WireMock.delete(urlEqualTo("/aai/v28/service-design-and-creation/models/model/3a40ab73-6694-4e75-bb6d-9a4a86ce35b3?resource-version=1710523260974")) + .withHeader("X-FromAppId", equalTo("ModelLoader")) + .withHeader("X-TransactionId", equalTo("distributionId")) + .willReturn(aResponse().withStatus(HttpStatus.OK.value()))); + + ModelArtifact modelArtifact = new ModelArtifact(); + modelArtifact.setModelInvariantId("3a40ab73-6694-4e75-bb6d-9a4a86ce35b3"); + modelArtifact.setModelVerId("modelVersionId"); + Node node = Mockito.mock(Node.class); + modelArtifact.setModelVer(node); + List completedArtifacts = new ArrayList<>(); + completedArtifacts.add(modelArtifact); + + mockModelCreation(modelArtifact); + + modelArtifactHandler.rollback(completedArtifacts, "distributionId", restClient); + + verify( + deleteRequestedFor( + urlEqualTo("/aai/v28/service-design-and-creation/models/model/3a40ab73-6694-4e75-bb6d-9a4a86ce35b3?resource-version=1710523260974"))); + } + + /** + * To test the rollback of the full model, the ModelArtifact must have the + * firstVersionOfModel = true state. + * This flag is set during the model creation and thus needs to run before + * testing this particular aspect. + * + * @param modelArtifact + */ + private void mockModelCreation(ModelArtifact modelArtifact) { + OperationResult createdResult = Mockito.mock(OperationResult.class); + when(createdResult.getResultCode()).thenReturn(HttpStatus.CREATED.value()); + OperationResult notFoundResult = Mockito.mock(OperationResult.class); + when(notFoundResult.getResultCode()).thenReturn(HttpStatus.NOT_FOUND.value()); + AaiRestClient aaiClient = Mockito.mock(AaiRestClient.class); + when(aaiClient.putResource(any(), any(), any(), any())).thenReturn(createdResult); + when(aaiClient.getResource(any(), any(), any())).thenReturn(notFoundResult); + modelArtifact.push(aaiClient, config, null, new ArrayList<>()); + } + + @Test + public void thatModelVersionCanBeRolledBack() { + + // "http://localhost:10594/aai/v28/service-design-and-creation/models/model/modelInvariantId/model-vers/model-ver/modelVersionId" + + ModelArtifact modelArtifact = new ModelArtifact(); + modelArtifact.setModelInvariantId("modelInvariantId"); + modelArtifact.setModelVerId("modelVersionId"); + Node node = Mockito.mock(Node.class); + modelArtifact.setModelVer(node); + List completedArtifacts = new ArrayList<>(); + completedArtifacts.add(modelArtifact); + + modelArtifactHandler.rollback(completedArtifacts, "distributionId", restClient); + + } +} \ No newline at end of file diff --git a/src/test/java/org/onap/aai/modelloader/notification/TestNotificationDataImpl.java b/src/test/java/org/onap/aai/modelloader/notification/TestNotificationDataImpl.java deleted file mode 100644 index 4d6858a..0000000 --- a/src/test/java/org/onap/aai/modelloader/notification/TestNotificationDataImpl.java +++ /dev/null @@ -1,83 +0,0 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. - * Copyright © 2017-2018 European Software Marketing Ltd. - * ================================================================================ - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://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. - * ============LICENSE_END========================================================= - */ -package org.onap.aai.modelloader.notification; - -import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.not; -import static org.hamcrest.MatcherAssert.assertThat; - -import org.junit.jupiter.api.Test; - -/** - * Tests for NotificationDataImpl class - * - */ -public class TestNotificationDataImpl { - - @Test - public void testGettersAndSetters() { - NotificationDataImpl data = new NotificationDataImpl(); - String distributionId = "testid"; - - data.setDistributionID(distributionId); - assertThat(data.getDistributionID(), is(equalTo(distributionId))); - - // Getters return empty data - assertThat(data.getArtifactMetadataByUUID(null), is(equalTo(null))); - assertThat(data.getServiceDescription(), is(equalTo(null))); - assertThat(data.getServiceInvariantUUID(), is(equalTo(null))); - assertThat(data.getServiceName(), is(equalTo(null))); - assertThat(data.getServiceUUID(), is(equalTo(null))); - assertThat(data.getServiceVersion(), is(equalTo(null))); - assertThat(data.getResources().size(), is(0)); - assertThat(data.getServiceArtifacts().size(), is(0)); - - // Unsupported method! - String context = "testcontext"; - data.setWorkloadContext(context); - assertThat(data.getWorkloadContext(), is(equalTo(null))); - } - - - @Test - public void testEquality() { - NotificationDataImpl data = new NotificationDataImpl(); - assertThat(data, is(not(equalTo(null)))); - assertThat(data, is(not(equalTo("")))); // NOSONAR - assertThat(data, is(equalTo(data))); - - NotificationDataImpl other = new NotificationDataImpl(); - assertThat(data, is(equalTo(other))); - assertThat(data.hashCode(), is(equalTo(other.hashCode()))); - - other.setDistributionID(""); - assertThat(data, is(not(equalTo(other)))); - - data.setDistributionID("1234"); - assertThat(data, is(not(equalTo(other)))); - - other.setDistributionID("1234"); - assertThat(data, is(equalTo(other))); - assertThat(data.hashCode(), is(equalTo(other.hashCode()))); - } - -} diff --git a/src/test/java/org/onap/aai/modelloader/restclient/TestBabelServiceClient.java b/src/test/java/org/onap/aai/modelloader/restclient/TestBabelServiceClient.java index 77e1594..169943c 100644 --- a/src/test/java/org/onap/aai/modelloader/restclient/TestBabelServiceClient.java +++ b/src/test/java/org/onap/aai/modelloader/restclient/TestBabelServiceClient.java @@ -20,68 +20,65 @@ */ package org.onap.aai.modelloader.restclient; -import static javax.servlet.http.HttpServletResponse.SC_OK; -import static org.apache.commons.io.IOUtils.write; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import java.io.IOException; import java.net.URISyntaxException; -import java.nio.charset.Charset; import java.nio.file.Files; import java.nio.file.Paths; -import java.util.ArrayList; import java.util.List; import java.util.Properties; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.eclipse.jetty.server.Handler; -import org.eclipse.jetty.server.Request; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.server.handler.AbstractHandler; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.onap.aai.babel.service.data.BabelArtifact; import org.onap.aai.modelloader.config.ModelLoaderConfig; import org.onap.aai.modelloader.service.HttpsBabelServiceClientFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.contract.wiremock.AutoConfigureWireMock; -import com.google.gson.Gson; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.tomakehurst.wiremock.client.WireMock; +import com.github.tomakehurst.wiremock.matching.EqualToPattern; /** * Local testing of the Babel service client. * */ +@SpringBootTest +@AutoConfigureWireMock(port = 0) public class TestBabelServiceClient { - private Server server; - private String responseBody; - - @BeforeEach - public void startJetty() throws Exception { - List response = new ArrayList<>(); - response.add(new BabelArtifact("", null, "")); - response.add(new BabelArtifact("", null, "")); - response.add(new BabelArtifact("", null, "")); - responseBody = new Gson().toJson(response); - - server = new Server(0); - server.setHandler(getMockHandler()); - server.start(); - } + @Value("${wiremock.server.port}") + private int wiremockPort; - @AfterEach - public void stopJetty() throws Exception { - server.stop(); + @BeforeAll + public static void setup() throws JsonProcessingException { + ObjectMapper objectMapper = new ObjectMapper(); + List artifacts = List.of( + new BabelArtifact("art1", null, ""), + new BabelArtifact("art2", null, ""), + new BabelArtifact("art3", null, "")); + WireMock.stubFor( + WireMock.post(WireMock.urlEqualTo("/generate")) + .withHeader("X-TransactionId", WireMock.equalTo("Test-Transaction-ID-BabelClient")) + .withHeader("X-FromAppId", WireMock.equalTo("ModelLoader")) + .withRequestBody(WireMock.matchingJsonPath("$.artifactName", WireMock.equalTo("service-Vscpass-Test"))) + .withRequestBody(WireMock.matchingJsonPath("$.artifactVersion", WireMock.equalTo("1.0"))) + .withRequestBody(WireMock.matchingJsonPath("$.csar", WireMock.matching(".*"))) + .willReturn( + WireMock.aResponse() + .withHeader("Content-Type", "application/json") + .withBody(objectMapper.writeValueAsString(artifacts)))); } @Test public void testRestClient() throws BabelServiceClientException, IOException, URISyntaxException { - String url = server.getURI().toString(); + String url = "http://localhost:" + wiremockPort; Properties configProperties = new Properties(); configProperties.put("ml.babel.KEYSTORE_PASSWORD", "OBF:1vn21ugu1saj1v9i1v941sar1ugw1vo0"); configProperties.put("ml.babel.KEYSTORE_FILE", "src/test/resources/auth/aai-client-dummy.p12"); @@ -89,7 +86,7 @@ public class TestBabelServiceClient { // In a real deployment this would be a different file (to the client keystore) configProperties.put("ml.babel.TRUSTSTORE_FILE", "src/test/resources/auth/aai-client-dummy.p12"); configProperties.put("ml.babel.BASE_URL", url); - configProperties.put("ml.babel.GENERATE_ARTIFACTS_URL", "generate"); + configProperties.put("ml.babel.GENERATE_ARTIFACTS_URL", "/generate"); configProperties.put("ml.aai.RESTCLIENT_CONNECT_TIMEOUT", "12000"); configProperties.put("ml.aai.RESTCLIENT_READ_TIMEOUT", "12000"); BabelServiceClient client = @@ -102,11 +99,11 @@ public class TestBabelServiceClient { @Test public void testRestClientHttp() throws BabelServiceClientException, IOException, URISyntaxException { - String url = server.getURI().toString(); + String url = "http://localhost:" + wiremockPort; Properties configProperties = new Properties(); configProperties.put("ml.babel.USE_HTTPS", "false"); configProperties.put("ml.babel.BASE_URL", url); - configProperties.put("ml.babel.GENERATE_ARTIFACTS_URL", "generate"); + configProperties.put("ml.babel.GENERATE_ARTIFACTS_URL", "/generate"); configProperties.put("ml.aai.RESTCLIENT_CONNECT_TIMEOUT", "3000"); configProperties.put("ml.aai.RESTCLIENT_READ_TIMEOUT", "3000"); BabelServiceClient client = @@ -121,23 +118,4 @@ public class TestBabelServiceClient { private byte[] readBytesFromFile(String resourceFile) throws IOException, URISyntaxException { return Files.readAllBytes(Paths.get(ClassLoader.getSystemResource(resourceFile).toURI())); } - - /** - * Creates an {@link AbstractHandler handler} returning an arbitrary String as a response. - * - * @return never null. - */ - private Handler getMockHandler() { - Handler handler = new AbstractHandler() { - @Override - public void handle(String target, Request request, HttpServletRequest servletRequest, - HttpServletResponse response) throws IOException, ServletException { - response.setStatus(SC_OK); - response.setContentType("text/xml;charset=utf-8"); - write(responseBody, response.getOutputStream(), Charset.defaultCharset()); - request.setHandled(true); - } - }; - return handler; - } } diff --git a/src/test/java/org/onap/aai/modelloader/restclient/TracingTest.java b/src/test/java/org/onap/aai/modelloader/restclient/TracingTest.java new file mode 100644 index 0000000..3bfceb5 --- /dev/null +++ b/src/test/java/org/onap/aai/modelloader/restclient/TracingTest.java @@ -0,0 +1,63 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2024 Deutsche Telekom AG Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://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. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.modelloader.restclient; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.contract.wiremock.AutoConfigureWireMock; +import org.springframework.http.HttpStatus; +import org.springframework.web.client.RestTemplate; + +import com.github.tomakehurst.wiremock.client.WireMock; + +@SpringBootTest(properties = { + "spring.sleuth.enabled=true", + "spring.zipkin.baseUrl=http://localhost:${wiremock.server.port}" +}) +@AutoConfigureWireMock(port = 0) +public class TracingTest { + + @Value("${wiremock.server.port}") + private int wiremockPort; + + @Autowired RestTemplate restTemplate; + + @Test + public void thatArtifactsCanBePushed() { + WireMock.stubFor( + WireMock.post(WireMock.urlEqualTo("/api/v2/spans")) + .willReturn( + WireMock.aResponse() + .withStatus(HttpStatus.OK.value()))); + + WireMock.stubFor( + WireMock.get(WireMock.urlEqualTo("/")) + .withHeader("X-B3-TraceId", WireMock.matching(".*")) + .willReturn( + WireMock.aResponse() + .withStatus(HttpStatus.OK.value()))); + + + String response = restTemplate.getForObject("http://localhost:" + wiremockPort + "/", String.class); + } + +} diff --git a/src/test/java/org/onap/aai/modelloader/service/TestArtifactInfoImpl.java b/src/test/java/org/onap/aai/modelloader/service/TestArtifactInfoImpl.java deleted file mode 100644 index d1d46f0..0000000 --- a/src/test/java/org/onap/aai/modelloader/service/TestArtifactInfoImpl.java +++ /dev/null @@ -1,123 +0,0 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. - * Copyright © 2017-2018 European Software Marketing Ltd. - * ================================================================================ - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://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. - * ============LICENSE_END========================================================= - */ -package org.onap.aai.modelloader.service; - -import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.not; -import static org.hamcrest.CoreMatchers.nullValue; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.empty; - -import org.junit.jupiter.api.Test; - -/** - * Tests for NotificationDataImpl class - * - */ -public class TestArtifactInfoImpl { - - @Test - public void testGettersAndSetters() { - ArtifactInfoImpl info = new ArtifactInfoImpl(); - String artifactName = "testname"; - - info.setArtifactName(artifactName); - assertThat(info.getArtifactName(), is(equalTo(artifactName))); - - String artifactType = "test-type"; - info.setArtifactType(artifactType); - assertThat(info.getArtifactType(), is(equalTo(artifactType))); - - String artifactVersion = "v1"; - info.setArtifactVersion(artifactVersion); - assertThat(info.getArtifactVersion(), is(equalTo(artifactVersion))); - - String artifactDescription = "test description"; - info.setArtifactDescription(artifactDescription); - assertThat(info.getArtifactDescription(), is(equalTo(artifactDescription))); - - assertThat(info.getArtifactChecksum(), is(nullValue())); - assertThat(info.getArtifactTimeout(), is(nullValue())); - assertThat(info.getArtifactURL(), is(nullValue())); - assertThat(info.getArtifactUUID(), is(nullValue())); - assertThat(info.getGeneratedArtifact(), is(nullValue())); - assertThat(info.getRelatedArtifacts(), is(empty())); - } - - - @Test - public void testEquality() { - ArtifactInfoImpl info = new ArtifactInfoImpl(); - assertThat(info, is(not(equalTo(null)))); - assertThat(info, is(not(equalTo("")))); // NOSONAR - assertThat(info, is(equalTo(info))); - - ArtifactInfoImpl other = new ArtifactInfoImpl(); - assertThat(info, is(equalTo(other))); - assertThat(info.hashCode(), is(equalTo(other.hashCode()))); - - // Artifact Name - other.setArtifactName(""); - assertThat(info, is(not(equalTo(other)))); - - info.setArtifactName("1234"); - assertThat(info, is(not(equalTo(other)))); - - other.setArtifactName("1234"); - assertThat(info, is(equalTo(other))); - assertThat(info.hashCode(), is(equalTo(other.hashCode()))); - - // Artifact Type - other.setArtifactType(""); - assertThat(info, is(not(equalTo(other)))); - - info.setArtifactType("type"); - assertThat(info, is(not(equalTo(other)))); - - other.setArtifactType("type"); - assertThat(info, is(equalTo(other))); - assertThat(info.hashCode(), is(equalTo(other.hashCode()))); - - // Artifact Description - other.setArtifactDescription(""); - assertThat(info, is(not(equalTo(other)))); - - info.setArtifactDescription("type"); - assertThat(info, is(not(equalTo(other)))); - - other.setArtifactDescription("type"); - assertThat(info, is(equalTo(other))); - assertThat(info.hashCode(), is(equalTo(other.hashCode()))); - - // Artifact Version - other.setArtifactVersion(""); - assertThat(info, is(not(equalTo(other)))); - - info.setArtifactVersion("v1"); - assertThat(info, is(not(equalTo(other)))); - - other.setArtifactVersion("v1"); - assertThat(info, is(equalTo(other))); - assertThat(info.hashCode(), is(equalTo(other.hashCode()))); - } - -} diff --git a/src/test/resources/__files/artifactTypes.json b/src/test/resources/__files/artifactTypes.json new file mode 100644 index 0000000..de3cfce --- /dev/null +++ b/src/test/resources/__files/artifactTypes.json @@ -0,0 +1,63 @@ +[ + "AAI_SERVICE_MODEL", + "AAI_VF_INSTANCE_MODEL", + "AAI_VF_MODEL", + "AAI_VF_MODULE_MODEL", + "ANSIBLE_PLAYBOOK", + "APPC_CONFIG", + "BPEL", + "CHEF", + "CLOUD_TECHNOLOGY_SPECIFIC_ARTIFACT", + "CONTROLLER_BLUEPRINT_ARCHIVE", + "DCAE_DOC", + "DCAE_EVENT", + "DCAE_INVENTORY_BLUEPRINT", + "DCAE_INVENTORY_DOC", + "DCAE_INVENTORY_EVENT", + "DCAE_INVENTORY_JSON", + "DCAE_INVENTORY_POLICY", + "DCAE_INVENTORY_TOSCA", + "DCAE_JSON", + "DCAE_POLICY", + "DCAE_TOSCA", + "DG_XML", + "ETSI_PACKAGE", + "ASD_PACKAGE", + "GUIDE", + "HEAT_ARTIFACT", + "HEAT_ENV", + "HEAT", + "HEAT_NESTED", + "HEAT_NET", + "HEAT_VOL", + "HELM", + "ICON", + "LIFECYCLE_OPERATIONS", + "MODEL_INVENTORY_PROFILE", + "MODEL_QUERY_SPEC", + "MURANO_PKG", + "NETWORK_CALL_FLOW", + "ONBOARDED_PACKAGE", + "OTHER", + "PERFORMANCE_COUNTER", + "PLAN", + "PM_DICTIONARY", + "PNF_SW_INFORMATION", + "PUPPET", + "SHELL_SCRIPT", + "SHELL", + "SNMP_POLL", + "SNMP_TRAP", + "TOSCA_CSAR", + "TOSCA_TEMPLATE", + "UCPE_LAYER_2_CONFIGURATION", + "VENDOR_LICENSE", + "VES_EVENTS", + "VF_LICENSE", + "VF_MODULES_METADATA", + "VNF_CATALOG", + "WORKFLOW", + "YANG_MODULE", + "YANG_XML", + "YANG" +] \ No newline at end of file diff --git a/src/test/resources/__files/kafkaBootstrap.json b/src/test/resources/__files/kafkaBootstrap.json new file mode 100644 index 0000000..1820ade --- /dev/null +++ b/src/test/resources/__files/kafkaBootstrap.json @@ -0,0 +1,5 @@ +{ + "kafkaBootStrapServer": "localhost:9092", + "distrNotificationTopicName": "SDC-DISTR-NOTIF-TOPIC-AUTO", + "distrStatusTopicName": "SDC-DISTR-STATUS-TOPIC-AUTO" +} \ No newline at end of file diff --git a/src/test/resources/__files/modelResponse.xml b/src/test/resources/__files/modelResponse.xml new file mode 100644 index 0000000..36d07d8 --- /dev/null +++ b/src/test/resources/__files/modelResponse.xml @@ -0,0 +1,7 @@ + + + 3a40ab73-6694-4e75-bb6d-9a4a86ce35b3 + service + Network Service + 1710523260974 + \ No newline at end of file diff --git a/src/test/resources/__files/service-TestSvc-csar-babel-response.json b/src/test/resources/__files/service-TestSvc-csar-babel-response.json new file mode 100644 index 0000000..7103156 --- /dev/null +++ b/src/test/resources/__files/service-TestSvc-csar-babel-response.json @@ -0,0 +1 @@ +[{"name":"AAI-test-svc-service-2.0.xml","type":"MODEL","payload":"\n 3c8bc8e7-e387-46ed-8616-70e99e2206dc\n service\n Network L1-3\n \n \n 71f47717-b100-4eac-940b-7d4e86a4cbb7\n test-svc\n 2.0\n test-svc\n \n \n T\n unbounded\n \n \n \n model-ver\n \n model-ver.model-version-id\n 46b92144-923a-4d20-b85a-3cbd847668a9\n \n \n model.model-invariant-id\n 82194af1-3c2c-485a-8f44-420e22a9eaa4\n \n \n \n \n \n \n \n"}] \ No newline at end of file diff --git a/src/test/resources/__files/service-TestSvc-csar.csar b/src/test/resources/__files/service-TestSvc-csar.csar new file mode 100644 index 0000000000000000000000000000000000000000..711f06813bbdec898be82708ed78e643ad8df7ee GIT binary patch literal 55342 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 Y(x$3_i - + diff --git a/src/test/resources/model-loader.properties b/src/test/resources/model-loader.properties index 4c24679..2db7c09 100644 --- a/src/test/resources/model-loader.properties +++ b/src/test/resources/model-loader.properties @@ -1,16 +1,20 @@ # Model Loader Distribution Client Configuration ml.distribution.ACTIVE_SERVER_TLS_AUTH=false ml.distribution.ASDC_ADDRESS=localhost:8443 +ml.distribution.ASDC_USE_HTTPS=false ml.distribution.CONSUMER_GROUP=aai-ml-group-test ml.distribution.CONSUMER_ID=aai-ml-id-test -ml.distribution.ENVIRONMENT_NAME=env +ml.distribution.ENVIRONMENT_NAME=AUTO ml.distribution.KEYSTORE_PASSWORD= ml.distribution.KEYSTORE_FILE= ml.distribution.PASSWORD=Aa123456 -ml.distribution.POLLING_INTERVAL=5 +ml.distribution.POLLING_INTERVAL=15 ml.distribution.POLLING_TIMEOUT=3 ml.distribution.USER=ci ml.distribution.ARTIFACT_TYPES=MODEL_QUERY_SPEC,TOSCA_CSAR +ml.distribution.SASL_JAAS_CONFIG=org.apache.kafka.common.security.scram.ScramLoginModule required username="aai-modelloader-ku" password="somePassword"; +ml.distribution.SECURITY_PROTOCOL=PLAINTEXT +ml.distribution.SASL_MECHANISM=PLAIN # Disable ASDC polling & enable REST interface ml.distribution.ASDC_CONNECTION_DISABLE=true -- 2.16.6