From 3d962090dad9de06cf854fab8daceaf18e0ec469 Mon Sep 17 00:00:00 2001 From: ebo Date: Mon, 30 Sep 2019 13:23:43 +0100 Subject: Implemented UAT runtime services Issue-ID: CCSDK-1759 Change-Id: I9e3df315074bfcfa5e341feefdabd00671194dc3 Signed-off-by: ebo --- .../BlueprintsAcceptanceTest.kt | 242 ------------------- .../cds/blueprintsprocessor/CollectionUtils2.kt | 31 --- .../blueprintsprocessor/ExtendedTemporaryFolder.kt | 56 ----- .../cds/blueprintsprocessor/JsonNormalizer.kt | 79 ------- .../ccsdk/cds/blueprintsprocessor/MoreMatchers.kt | 34 --- .../cds/blueprintsprocessor/PathDeserializer.kt | 52 ----- .../blueprintsprocessor/TestSecuritySettings.kt | 45 ---- .../ccsdk/cds/blueprintsprocessor/UatDefinition.kt | 61 ----- .../WorkingFoldersInitializer.kt | 48 ---- .../cds/blueprintsprocessor/uat/BaseUatTest.kt | 56 +++++ .../uat/BlueprintsAcceptanceTest.kt | 91 ++++++++ .../uat/ExtendedTemporaryFolder.kt | 57 +++++ .../blueprintsprocessor/uat/MarkedSlf4jNotifier.kt | 43 ++++ .../uat/TestSecuritySettings.kt | 44 ++++ .../cds/blueprintsprocessor/uat/UatServicesTest.kt | 260 +++++++++++++++++++++ .../uat/WorkingFoldersInitializer.kt | 48 ++++ 16 files changed, 599 insertions(+), 648 deletions(-) delete mode 100644 ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/BlueprintsAcceptanceTest.kt delete mode 100644 ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/CollectionUtils2.kt delete mode 100644 ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/ExtendedTemporaryFolder.kt delete mode 100644 ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/JsonNormalizer.kt delete mode 100644 ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/MoreMatchers.kt delete mode 100644 ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/PathDeserializer.kt delete mode 100644 ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/TestSecuritySettings.kt delete mode 100644 ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/UatDefinition.kt delete mode 100644 ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/WorkingFoldersInitializer.kt create mode 100644 ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/uat/BaseUatTest.kt create mode 100644 ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/uat/BlueprintsAcceptanceTest.kt create mode 100644 ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/uat/ExtendedTemporaryFolder.kt create mode 100644 ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/uat/MarkedSlf4jNotifier.kt create mode 100644 ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/uat/TestSecuritySettings.kt create mode 100644 ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/uat/UatServicesTest.kt create mode 100644 ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/uat/WorkingFoldersInitializer.kt (limited to 'ms/blueprintsprocessor/application/src/test/kotlin') diff --git a/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/BlueprintsAcceptanceTest.kt b/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/BlueprintsAcceptanceTest.kt deleted file mode 100644 index ce7434f8e..000000000 --- a/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/BlueprintsAcceptanceTest.kt +++ /dev/null @@ -1,242 +0,0 @@ -/*- - * ============LICENSE_START======================================================= - * Copyright (C) 2019 Nordix Foundation. - * ================================================================================ - * 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. - * - * SPDX-License-Identifier: Apache-2.0 - * ============LICENSE_END========================================================= - */ -package org.onap.ccsdk.cds.blueprintsprocessor - -import com.fasterxml.jackson.databind.JsonNode -import com.fasterxml.jackson.databind.ObjectMapper -import com.fasterxml.jackson.databind.node.MissingNode -import com.nhaarman.mockitokotlin2.any -import com.nhaarman.mockitokotlin2.argThat -import com.nhaarman.mockitokotlin2.atLeast -import com.nhaarman.mockitokotlin2.atLeastOnce -import com.nhaarman.mockitokotlin2.eq -import com.nhaarman.mockitokotlin2.mock -import com.nhaarman.mockitokotlin2.verify -import com.nhaarman.mockitokotlin2.verifyNoMoreInteractions -import com.nhaarman.mockitokotlin2.whenever -import org.junit.ClassRule -import org.junit.Rule -import org.junit.runner.RunWith -import org.junit.runners.Parameterized -import org.mockito.Answers -import org.onap.ccsdk.cds.blueprintsprocessor.rest.RestLibConstants -import org.onap.ccsdk.cds.blueprintsprocessor.rest.service.BluePrintRestLibPropertyService -import org.onap.ccsdk.cds.blueprintsprocessor.rest.service.BlueprintWebClientService -import org.onap.ccsdk.cds.blueprintsprocessor.rest.service.BlueprintWebClientService.WebClientResponse -import org.onap.ccsdk.cds.controllerblueprints.core.utils.BluePrintArchiveUtils.Companion.compressToBytes -import org.skyscreamer.jsonassert.JSONAssert -import org.skyscreamer.jsonassert.JSONCompareMode -import org.slf4j.Logger -import org.slf4j.LoggerFactory -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient -import org.springframework.boot.test.context.SpringBootTest -import org.springframework.boot.test.mock.mockito.MockBean -import org.springframework.core.io.ByteArrayResource -import org.springframework.core.io.Resource -import org.springframework.http.MediaType -import org.springframework.test.context.ContextConfiguration -import org.springframework.test.context.TestPropertySource -import org.springframework.test.context.junit4.rules.SpringClassRule -import org.springframework.test.context.junit4.rules.SpringMethodRule -import org.springframework.test.web.reactive.server.EntityExchangeResult -import org.springframework.test.web.reactive.server.WebTestClient -import reactor.core.publisher.Mono -import java.io.File -import java.nio.charset.StandardCharsets -import java.nio.file.Paths -import kotlin.test.BeforeTest -import kotlin.test.Test - -// Only one runner can be configured with jUnit 4. We had to replace the SpringRunner by equivalent jUnit rules. -// See more on https://docs.spring.io/autorepo/docs/spring-framework/current/spring-framework-reference/testing.html#testcontext-junit4-rules -@RunWith(Parameterized::class) -// Set blueprintsprocessor.httpPort=0 to trigger a random port selection -@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT) -@AutoConfigureWebTestClient(timeout = "PT10S") -@ContextConfiguration(initializers = [ - WorkingFoldersInitializer::class, - TestSecuritySettings.ServerContextInitializer::class -]) -@TestPropertySource(locations = ["classpath:application-test.properties"]) -class BlueprintsAcceptanceTest(private val blueprintName: String, private val filename: String) { - - companion object { - const val UAT_BLUEPRINTS_BASE_DIR = "../../../components/model-catalog/blueprint-model/uat-blueprints" - const val EMBEDDED_UAT_FILE = "Tests/uat.yaml" - - @ClassRule - @JvmField - val springClassRule = SpringClassRule() - - val log: Logger = LoggerFactory.getLogger(BlueprintsAcceptanceTest::class.java) - - /** - * Generates the parameters to create a test instance for every blueprint found under UAT_BLUEPRINTS_BASE_DIR - * that contains the proper UAT definition file. - */ - @Parameterized.Parameters(name = "{index} {0}") - @JvmStatic - fun testParameters(): List> { - return File(UAT_BLUEPRINTS_BASE_DIR) - .listFiles { file -> file.isDirectory && File(file, EMBEDDED_UAT_FILE).isFile } - ?.map { file -> arrayOf(file.nameWithoutExtension, file.canonicalPath) } - ?: emptyList() - } - } - - @Rule - @JvmField - val springMethodRule = SpringMethodRule() - - @MockBean(name = RestLibConstants.SERVICE_BLUEPRINT_REST_LIB_PROPERTY, answer = Answers.RETURNS_SMART_NULLS) - lateinit var restClientFactory: BluePrintRestLibPropertyService - - @Autowired - // Bean is created programmatically by {@link WorkingFoldersInitializer#initialize(String)} - @Suppress("SpringJavaInjectionPointsAutowiringInspection") - lateinit var tempFolder: ExtendedTemporaryFolder - - @Autowired - lateinit var webTestClient: WebTestClient - - @Autowired - lateinit var mapper: ObjectMapper - - @BeforeTest - fun cleanupTemporaryFolder() { - tempFolder.deleteAllFiles() - } - - @Test - fun testBlueprint() { - val uat = UatDefinition.load(mapper, Paths.get(filename, EMBEDDED_UAT_FILE)) - - uploadBlueprint(blueprintName) - - // Configure mocked external services and save their expected requests for further validation - val requestsPerClient = uat.externalServices.associateBy( - { service -> createRestClientMock(service.selector, service.expectations) }, - { service -> service.expectations.map { it.request } } - ) - - // Run processes - for (process in uat.processes) { - log.info("Executing process '${process.name}'") - processBlueprint(process.request, process.expectedResponse, - JsonNormalizer.getNormalizer(mapper, process.responseNormalizerSpec)) - } - - // Validate requests to external services - for ((mockClient, requests) in requestsPerClient) { - requests.forEach { request -> - verify(mockClient, atLeastOnce()).exchangeResource( - eq(request.method), - eq(request.path), - argThat { assertJsonEqual(request.body, this) }, - argThat(RequiredMapEntriesMatcher(request.headers))) - } - // Don't mind the invocations to the overloaded exchangeResource(String, String, String) - verify(mockClient, atLeast(0)).exchangeResource(any(), any(), any()) - verifyNoMoreInteractions(mockClient) - } - } - - private fun createRestClientMock(selector: String, restExpectations: List) - : BlueprintWebClientService { - val restClient = mock(verboseLogging = true, - defaultAnswer = Answers.RETURNS_SMART_NULLS) - - // Delegates to overloaded exchangeResource(String, String, String, Map) - whenever(restClient.exchangeResource(any(), any(), any())) - .thenAnswer { invocation -> - val method = invocation.arguments[0] as String - val path = invocation.arguments[1] as String - val request = invocation.arguments[2] as String - restClient.exchangeResource(method, path, request, emptyMap()) - } - for (expectation in restExpectations) { - whenever(restClient.exchangeResource( - eq(expectation.request.method), - eq(expectation.request.path), - any(), - any())) - .thenReturn(WebClientResponse(expectation.response.status, expectation.response.body.toString())) - } - - whenever(restClientFactory.blueprintWebClientService(selector)) - .thenReturn(restClient) - return restClient - } - - private fun uploadBlueprint(blueprintName: String) { - val body = toMultiValueMap("file", getBlueprintAsResource(blueprintName)) - webTestClient - .post() - .uri("/api/v1/blueprint-model/publish") - .header("Authorization", TestSecuritySettings.clientAuthToken()) - .syncBody(body) - .exchange() - .expectStatus().isOk - } - - private fun processBlueprint(request: JsonNode, expectedResponse: JsonNode, - responseNormalizer: (String) -> String) { - webTestClient - .post() - .uri("/api/v1/execution-service/process") - .header("Authorization", TestSecuritySettings.clientAuthToken()) - .contentType(MediaType.APPLICATION_JSON_UTF8) - .body(Mono.just(request.toString()), String::class.java) - .exchange() - .expectStatus().isOk - .expectBody() - .consumeWith { response -> - assertJsonEqual(expectedResponse, responseNormalizer(getBodyAsString(response))) - } - } - - private fun getBlueprintAsResource(blueprintName: String): Resource { - val baseDir = Paths.get(UAT_BLUEPRINTS_BASE_DIR, blueprintName) - val zipBytes = compressToBytes(baseDir) - return object : ByteArrayResource(zipBytes) { - // Filename has to be returned in order to be able to post - override fun getFilename() = "$blueprintName.zip" - } - } - - private fun assertJsonEqual(expected: JsonNode, actual: String): Boolean { - if ((actual == "") && (expected is MissingNode)) { - return true - } - JSONAssert.assertEquals(expected.toString(), actual, JSONCompareMode.LENIENT) - // assertEquals throws an exception whenever match fails - return true - } - - private fun getBodyAsString(result: EntityExchangeResult): String { - val body = result.responseBody - if ((body == null) || body.isEmpty()) { - return "" - } - val charset = result.responseHeaders.contentType?.charset ?: StandardCharsets.UTF_8 - return String(body, charset) - } -} \ No newline at end of file diff --git a/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/CollectionUtils2.kt b/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/CollectionUtils2.kt deleted file mode 100644 index 63d64cae4..000000000 --- a/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/CollectionUtils2.kt +++ /dev/null @@ -1,31 +0,0 @@ -/*- - * ============LICENSE_START======================================================= - * Copyright (C) 2019 Nordix Foundation. - * ================================================================================ - * 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. - * - * SPDX-License-Identifier: Apache-2.0 - * ============LICENSE_END========================================================= - */ -package org.onap.ccsdk.cds.blueprintsprocessor - -import org.springframework.util.CollectionUtils -import org.springframework.util.MultiValueMap - - -/** - * Convenient method to create a single-entry MultiValueMap. - */ -fun toMultiValueMap(key: K, vararg values: V): MultiValueMap { - return CollectionUtils.toMultiValueMap(mapOf(key to values.asList())) -} diff --git a/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/ExtendedTemporaryFolder.kt b/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/ExtendedTemporaryFolder.kt deleted file mode 100644 index 57b4573ef..000000000 --- a/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/ExtendedTemporaryFolder.kt +++ /dev/null @@ -1,56 +0,0 @@ -/*- - * ============LICENSE_START======================================================= - * Copyright (C) 2019 Nordix Foundation. - * ================================================================================ - * 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. - * - * SPDX-License-Identifier: Apache-2.0 - * ============LICENSE_END========================================================= - */ -package org.onap.ccsdk.cds.blueprintsprocessor - -import org.junit.rules.TemporaryFolder -import java.io.File -import java.io.IOException -import java.nio.file.* -import java.nio.file.attribute.* -import javax.annotation.PreDestroy - -class ExtendedTemporaryFolder { - private val tempFolder = TemporaryFolder() - - init { - tempFolder.create() - } - - @PreDestroy - fun delete() = tempFolder.delete() - - /** - * A delegate to org.junit.rules.TemporaryFolder.TemporaryFolder.newFolder(String). - */ - fun newFolder(folder: String): File = tempFolder.newFolder(folder) - - /** - * Delete all files under the root temporary folder recursively. The folders are preserved. - */ - fun deleteAllFiles() { - Files.walkFileTree(tempFolder.root.toPath(), object : SimpleFileVisitor() { - @Throws(IOException::class) - override fun visitFile(file: Path?, attrs: BasicFileAttributes?): FileVisitResult { - file?.toFile()?.delete() - return FileVisitResult.CONTINUE - } - }) - } -} diff --git a/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/JsonNormalizer.kt b/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/JsonNormalizer.kt deleted file mode 100644 index 69673f931..000000000 --- a/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/JsonNormalizer.kt +++ /dev/null @@ -1,79 +0,0 @@ -/*- - * ============LICENSE_START======================================================= - * Copyright (C) 2019 Nordix Foundation. - * ================================================================================ - * 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. - * - * SPDX-License-Identifier: Apache-2.0 - * ============LICENSE_END========================================================= - */ -package org.onap.ccsdk.cds.blueprintsprocessor - -import com.fasterxml.jackson.databind.JsonNode -import com.fasterxml.jackson.databind.ObjectMapper -import com.fasterxml.jackson.databind.node.ContainerNode -import com.fasterxml.jackson.databind.node.MissingNode -import com.fasterxml.jackson.databind.node.ObjectNode -import com.schibsted.spt.data.jslt.Parser - -class JsonNormalizer { - - companion object { - - fun getNormalizer(mapper: ObjectMapper, jsltSpec: JsonNode): (String) -> String { - if (jsltSpec is MissingNode) { - return { it } - } - return { s: String -> - val input = mapper.readTree(s) - val expandedJstlSpec = expandJstlSpec(jsltSpec) - val jslt = Parser.compileString(expandedJstlSpec) - val output = jslt.apply(input) - output.toString() - } - } - - /** - * Creates an extended JSTL spec by appending the "*: ." wildcard pattern to every inner JSON object, and - * removing the extra quotes added by the standard YAML/JSON converters on fields prefixed by "?". - * - * @param jstlSpec the JSTL spec as a structured JSON object. - * @return the string representation of the extended JSTL spec. - */ - private fun expandJstlSpec(jstlSpec: JsonNode): String { - val extendedJstlSpec = updateObjectNodes(jstlSpec, "*", ".") - return extendedJstlSpec.toString() - // Handle the "?" as a prefix to literal/non-quoted values - .replace("\"\\?([^\"]+)\"".toRegex(), "$1") - // Also, remove the quotes added by Jackson for key and value of the wildcard matcher - .replace("\"([.*])\"".toRegex(), "$1") - } - - /** - * Expands a structured JSON object, by adding the given key and value to every nested ObjectNode. - * - * @param jsonNode the root node. - * @param fieldName the fixed field name. - * @param fieldValue the fixed field value. - */ - private fun updateObjectNodes(jsonNode: JsonNode, fieldName: String, fieldValue: String): JsonNode { - if (jsonNode is ContainerNode<*>) { - (jsonNode as? ObjectNode)?.put(fieldName, fieldValue) - jsonNode.forEach { child -> - updateObjectNodes(child, fieldName, fieldValue) - } - } - return jsonNode - } - } -} diff --git a/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/MoreMatchers.kt b/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/MoreMatchers.kt deleted file mode 100644 index 71e07ab4c..000000000 --- a/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/MoreMatchers.kt +++ /dev/null @@ -1,34 +0,0 @@ -/*- - * ============LICENSE_START======================================================= - * Copyright (C) 2019 Nordix Foundation. - * ================================================================================ - * 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. - * - * SPDX-License-Identifier: Apache-2.0 - * ============LICENSE_END========================================================= - */ -package org.onap.ccsdk.cds.blueprintsprocessor - -import com.google.common.collect.Maps -import org.mockito.ArgumentMatcher - -class RequiredMapEntriesMatcher(private val requiredEntries: Map) : ArgumentMatcher> { - override fun matches(argument: Map?): Boolean { - val missingEntries = Maps.difference(requiredEntries, argument).entriesOnlyOnLeft() - return missingEntries.isEmpty() - } - - override fun toString(): String { - return requiredEntries.toString() - } -} diff --git a/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/PathDeserializer.kt b/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/PathDeserializer.kt deleted file mode 100644 index 1a232f2d3..000000000 --- a/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/PathDeserializer.kt +++ /dev/null @@ -1,52 +0,0 @@ -/*- - * ============LICENSE_START======================================================= - * Copyright (C) 2019 Nordix Foundation. - * ================================================================================ - * 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. - * - * SPDX-License-Identifier: Apache-2.0 - * ============LICENSE_END========================================================= - */ -package org.onap.ccsdk.cds.blueprintsprocessor - -import com.fasterxml.jackson.core.JsonParser -import com.fasterxml.jackson.databind.DeserializationContext -import com.fasterxml.jackson.databind.deser.std.StdDeserializer - -class PathDeserializer : StdDeserializer(String::class.java) { - override fun deserialize(jp: JsonParser, ctxt: DeserializationContext?): String { - val path = jp.codec.readValue(jp, Any::class.java) - return flatJoin(path) - } - - /** - * Join a multilevel lists of strings. - * Example: flatJoin(listOf("a", listOf("b", "c"), "d")) will result in "a/b/c/d". - */ - private fun flatJoin(path: Any): String { - fun flatJoinTo(sb: StringBuilder, path: Any): StringBuilder { - when (path) { - is List<*> -> path.filterNotNull().forEach { flatJoinTo(sb, it) } - is String -> { - if (sb.isNotEmpty()) { - sb.append('/') - } - sb.append(path) - } - else -> throw IllegalArgumentException("Unsupported type: ${path.javaClass}") - } - return sb - } - return flatJoinTo(StringBuilder(), path).toString() - } -} \ No newline at end of file diff --git a/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/TestSecuritySettings.kt b/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/TestSecuritySettings.kt deleted file mode 100644 index f7ab2554c..000000000 --- a/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/TestSecuritySettings.kt +++ /dev/null @@ -1,45 +0,0 @@ -/*- - * ============LICENSE_START======================================================= - * Copyright (C) 2019 Nordix Foundation. - * ================================================================================ - * 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. - * - * SPDX-License-Identifier: Apache-2.0 - * ============LICENSE_END========================================================= - */ -package org.onap.ccsdk.cds.blueprintsprocessor - -import org.springframework.context.ApplicationContextInitializer -import org.springframework.context.ConfigurableApplicationContext -import org.springframework.test.context.support.TestPropertySourceUtils -import org.springframework.util.Base64Utils -import java.nio.charset.StandardCharsets - -class TestSecuritySettings { - companion object { - private const val authUsername = "walter.white" - private const val authPassword = "Heisenberg" - - fun clientAuthToken() = - "Basic " + Base64Utils.encodeToString("$authUsername:$authPassword".toByteArray(StandardCharsets.UTF_8)) - } - - class ServerContextInitializer : ApplicationContextInitializer { - override fun initialize(context: ConfigurableApplicationContext) { - TestPropertySourceUtils.addInlinedPropertiesToEnvironment(context, - "security.user.name=$authUsername", - "security.user.password={noop}$authPassword" - ) - } - } -} diff --git a/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/UatDefinition.kt b/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/UatDefinition.kt deleted file mode 100644 index abb1dfcd1..000000000 --- a/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/UatDefinition.kt +++ /dev/null @@ -1,61 +0,0 @@ -/*- - * ============LICENSE_START======================================================= - * Copyright (C) 2019 Nordix Foundation. - * ================================================================================ - * 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. - * - * SPDX-License-Identifier: Apache-2.0 - * ============LICENSE_END========================================================= - */ -package org.onap.ccsdk.cds.blueprintsprocessor - -import com.fasterxml.jackson.annotation.JsonAlias -import com.fasterxml.jackson.databind.JsonNode -import com.fasterxml.jackson.databind.ObjectMapper -import com.fasterxml.jackson.databind.annotation.JsonDeserialize -import com.fasterxml.jackson.databind.node.MissingNode -import org.yaml.snakeyaml.Yaml -import java.nio.file.Path - -data class ProcessDefinition(val name: String, val request: JsonNode, val expectedResponse: JsonNode, - val responseNormalizerSpec: JsonNode = MissingNode.getInstance()) - -data class RequestDefinition(val method: String, - @JsonDeserialize(using = PathDeserializer::class) - val path: String, - val headers: Map = emptyMap(), - val body: JsonNode = MissingNode.getInstance()) - -data class ResponseDefinition(val status: Int = 200, val body: JsonNode = MissingNode.getInstance()) { - companion object { - val DEFAULT_RESPONSE = ResponseDefinition() - } -} - -data class ExpectationDefinition(val request: RequestDefinition, - val response: ResponseDefinition = ResponseDefinition.DEFAULT_RESPONSE) - -data class ServiceDefinition(val selector: String, val expectations: List) - -data class UatDefinition(val processes: List, - @JsonAlias("external-services") - val externalServices: List = emptyList()) { - - companion object { - fun load(mapper: ObjectMapper, path: Path): UatDefinition { - return path.toFile().reader().use { reader -> - mapper.convertValue(Yaml().load(reader), UatDefinition::class.java) - } - } - } -} diff --git a/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/WorkingFoldersInitializer.kt b/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/WorkingFoldersInitializer.kt deleted file mode 100644 index 37615cb1a..000000000 --- a/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/WorkingFoldersInitializer.kt +++ /dev/null @@ -1,48 +0,0 @@ -/*- - * ============LICENSE_START======================================================= - * Copyright (C) 2019 Nordix Foundation. - * ================================================================================ - * 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. - * - * SPDX-License-Identifier: Apache-2.0 - * ============LICENSE_END========================================================= - */ -package org.onap.ccsdk.cds.blueprintsprocessor - -import org.springframework.beans.factory.support.BeanDefinitionBuilder -import org.springframework.beans.factory.support.BeanDefinitionRegistry -import org.springframework.context.ApplicationContextInitializer -import org.springframework.context.ConfigurableApplicationContext -import org.springframework.stereotype.Component -import org.springframework.test.context.support.TestPropertySourceUtils - -@Component -class WorkingFoldersInitializer : ApplicationContextInitializer { - - override fun initialize(context: ConfigurableApplicationContext) { - val tempFolder = ExtendedTemporaryFolder() - val properties = listOf("Deploy", "Archive", "Working") - .map { "blueprintsprocessor.blueprint${it}Path=${tempFolder.newFolder(it)}" } - .toTypedArray() - TestPropertySourceUtils.addInlinedPropertiesToEnvironment(context, *properties) - // Expose tempFolder as a bean so it can be accessed via DI - registerSingleton(context, "tempFolder", ExtendedTemporaryFolder::class.java, tempFolder) - } - - @Suppress("SameParameterValue") - private fun registerSingleton(context: ConfigurableApplicationContext, - beanName: String, beanClass: Class, instance: T) { - val builder = BeanDefinitionBuilder.genericBeanDefinition(beanClass) { instance } - (context.beanFactory as BeanDefinitionRegistry).registerBeanDefinition(beanName, builder.beanDefinition) - } -} diff --git a/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/uat/BaseUatTest.kt b/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/uat/BaseUatTest.kt new file mode 100644 index 000000000..ec338f274 --- /dev/null +++ b/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/uat/BaseUatTest.kt @@ -0,0 +1,56 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2019 Nordix Foundation. + * ================================================================================ + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ +package org.onap.ccsdk.cds.blueprintsprocessor.uat + +import org.junit.runner.RunWith +import org.onap.ccsdk.cds.blueprintsprocessor.uat.logging.LogColor.COLOR_TEST_CLIENT +import org.onap.ccsdk.cds.blueprintsprocessor.uat.logging.LogColor.resetContextColor +import org.onap.ccsdk.cds.blueprintsprocessor.uat.logging.LogColor.setContextColor +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.test.context.ContextConfiguration +import org.springframework.test.context.TestPropertySource +import org.springframework.test.context.junit4.SpringRunner +import kotlin.test.AfterTest +import kotlin.test.BeforeTest + +@RunWith(SpringRunner::class) +// Also set blueprintsprocessor.httpPort=0 +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@ContextConfiguration(initializers = [ + WorkingFoldersInitializer::class, + TestSecuritySettings.ServerContextInitializer::class +]) +@TestPropertySource(locations = ["classpath:application-test.properties"]) +abstract class BaseUatTest { + + @BeforeTest + fun setScope() { + setContextColor(COLOR_TEST_CLIENT) + } + + @AfterTest + fun clearScope() { + resetContextColor() + } + + companion object { + const val UAT_BLUEPRINTS_BASE_DIR = "../../../components/model-catalog/blueprint-model/uat-blueprints" + } +} \ No newline at end of file diff --git a/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/uat/BlueprintsAcceptanceTest.kt b/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/uat/BlueprintsAcceptanceTest.kt new file mode 100644 index 000000000..4fed0ce67 --- /dev/null +++ b/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/uat/BlueprintsAcceptanceTest.kt @@ -0,0 +1,91 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2019 Nordix Foundation. + * ================================================================================ + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ +package org.onap.ccsdk.cds.blueprintsprocessor.uat + +import org.junit.ClassRule +import org.junit.Rule +import org.junit.runner.RunWith +import org.junit.runners.Parameterized +import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintConstants.UAT_SPECIFICATION_FILE +import org.onap.ccsdk.cds.controllerblueprints.core.utils.BluePrintArchiveUtils.Companion.compressToBytes +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.test.context.junit4.rules.SpringClassRule +import org.springframework.test.context.junit4.rules.SpringMethodRule +import java.io.File +import java.nio.file.FileSystem +import java.nio.file.FileSystems +import kotlin.test.BeforeTest +import kotlin.test.Test + +// Only one runner can be configured with jUnit 4. We had to replace the SpringRunner by equivalent jUnit rules. +// See more on https://docs.spring.io/autorepo/docs/spring-framework/current/spring-framework-reference/testing.html#testcontext-junit4-rules +@RunWith(Parameterized::class) +class BlueprintsAcceptanceTest(@Suppress("unused") private val blueprintName: String, // readable test description + private val rootFs: FileSystem): BaseUatTest() { + + companion object { + + @ClassRule + @JvmField + val springClassRule = SpringClassRule() + + /** + * Generates the parameters to create a test instance for every blueprint found under UAT_BLUEPRINTS_BASE_DIR + * that contains the proper UAT definition file. + */ + @Parameterized.Parameters(name = "{index} {0}") + @JvmStatic + fun scanUatEmpoweredBlueprints(): List> { + return (File(UAT_BLUEPRINTS_BASE_DIR) + .listFiles { file -> file.isDirectory && File(file, UAT_SPECIFICATION_FILE).isFile } + ?: throw RuntimeException("Failed to scan $UAT_BLUEPRINTS_BASE_DIR")) + .map { file -> + arrayOf( + file.nameWithoutExtension, + FileSystems.newFileSystem(file.canonicalFile.toPath(), null) + ) + } + } + } + + @Rule + @JvmField + val springMethodRule = SpringMethodRule() + + @Autowired + // Bean is created programmatically by {@link WorkingFoldersInitializer#initialize(String)} + @Suppress("SpringJavaInjectionPointsAutowiringInspection") + lateinit var tempFolder: ExtendedTemporaryFolder + + @Autowired + lateinit var uatExecutor: UatExecutor + + @BeforeTest + fun cleanupTemporaryFolder() { + tempFolder.deleteAllFiles() + } + + @Test + fun runUat() { + val uatSpec = rootFs.getPath(UAT_SPECIFICATION_FILE).toFile().readText() + val cbaBytes = compressToBytes(rootFs.getPath("/")) + uatExecutor.execute(uatSpec, cbaBytes) + } +} \ No newline at end of file diff --git a/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/uat/ExtendedTemporaryFolder.kt b/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/uat/ExtendedTemporaryFolder.kt new file mode 100644 index 000000000..1c0067c36 --- /dev/null +++ b/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/uat/ExtendedTemporaryFolder.kt @@ -0,0 +1,57 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2019 Nordix Foundation. + * ================================================================================ + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ +package org.onap.ccsdk.cds.blueprintsprocessor.uat + +import java.io.File +import java.io.IOException +import java.nio.file.* +import java.nio.file.attribute.* +import javax.annotation.PreDestroy + +class ExtendedTemporaryFolder { + private val tempFolder = createTempDir("uat") + + @PreDestroy + fun delete() = tempFolder.deleteRecursively() + + /** + * A delegate to org.junit.rules.TemporaryFolder.TemporaryFolder.newFolder(String). + */ + fun newFolder(folderName: String): File { + val dir = File(tempFolder, folderName) + if (!dir.mkdir()) { + throw IOException("Unable to create temporary directory $dir.") + } + return dir + } + + /** + * Delete all files under the root temporary folder recursively. The folders are preserved. + */ + fun deleteAllFiles() { + Files.walkFileTree(tempFolder.toPath(), object : SimpleFileVisitor() { + @Throws(IOException::class) + override fun visitFile(file: Path?, attrs: BasicFileAttributes?): FileVisitResult { + file?.toFile()?.delete() + return FileVisitResult.CONTINUE + } + }) + } +} diff --git a/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/uat/MarkedSlf4jNotifier.kt b/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/uat/MarkedSlf4jNotifier.kt new file mode 100644 index 000000000..13ebd9e4b --- /dev/null +++ b/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/uat/MarkedSlf4jNotifier.kt @@ -0,0 +1,43 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2019 Nordix Foundation. + * ================================================================================ + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ +package org.onap.ccsdk.cds.blueprintsprocessor.uat + +import com.github.tomakehurst.wiremock.common.Notifier +import org.slf4j.LoggerFactory +import org.slf4j.Marker + +class MarkedSlf4jNotifier(private val marker: Marker) : Notifier { + + override fun info(message: String) { + log.info(marker, message) + } + + override fun error(message: String) { + log.error(marker, message) + } + + override fun error(message: String, t: Throwable) { + log.error(marker, message, t) + } + + companion object { + private val log = LoggerFactory.getLogger("uat.WireMock") + } +} diff --git a/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/uat/TestSecuritySettings.kt b/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/uat/TestSecuritySettings.kt new file mode 100644 index 000000000..216df9aef --- /dev/null +++ b/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/uat/TestSecuritySettings.kt @@ -0,0 +1,44 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2019 Nordix Foundation. + * ================================================================================ + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ +package org.onap.ccsdk.cds.blueprintsprocessor.uat + +import org.springframework.context.ApplicationContextInitializer +import org.springframework.context.ConfigurableApplicationContext +import org.springframework.test.context.support.TestPropertySourceUtils +import org.springframework.util.Base64Utils + +class TestSecuritySettings { + companion object { + private const val authUsername = "walter.white" + private const val authPassword = "Heisenberg" + + fun clientAuthToken() = + "Basic " + Base64Utils.encodeToString("$authUsername:$authPassword".toByteArray()) + } + + class ServerContextInitializer : ApplicationContextInitializer { + override fun initialize(context: ConfigurableApplicationContext) { + TestPropertySourceUtils.addInlinedPropertiesToEnvironment(context, + "security.user.name=$authUsername", + "security.user.password={noop}$authPassword" + ) + } + } +} diff --git a/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/uat/UatServicesTest.kt b/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/uat/UatServicesTest.kt new file mode 100644 index 000000000..78dc7099c --- /dev/null +++ b/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/uat/UatServicesTest.kt @@ -0,0 +1,260 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2019 Nordix Foundation. + * ================================================================================ + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ +package org.onap.ccsdk.cds.blueprintsprocessor.uat + +import com.fasterxml.jackson.databind.ObjectMapper +import com.github.tomakehurst.wiremock.WireMockServer +import com.github.tomakehurst.wiremock.client.MappingBuilder +import com.github.tomakehurst.wiremock.client.ResponseDefinitionBuilder +import com.github.tomakehurst.wiremock.client.VerificationException +import com.github.tomakehurst.wiremock.client.WireMock.aResponse +import com.github.tomakehurst.wiremock.client.WireMock.equalTo +import com.github.tomakehurst.wiremock.client.WireMock.equalToJson +import com.github.tomakehurst.wiremock.client.WireMock.request +import com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo +import com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig +import org.apache.http.HttpStatus +import org.apache.http.client.methods.HttpPost +import org.apache.http.entity.ContentType +import org.apache.http.entity.mime.HttpMultipartMode +import org.apache.http.entity.mime.MultipartEntityBuilder +import org.apache.http.impl.client.CloseableHttpClient +import org.apache.http.impl.client.HttpClientBuilder +import org.apache.http.message.BasicHeader +import org.hamcrest.CoreMatchers +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.Matchers.equalToIgnoringCase +import org.jetbrains.kotlin.konan.util.prefixIfNot +import org.onap.ccsdk.cds.blueprintsprocessor.uat.logging.LogColor.COLOR_WIREMOCK +import org.onap.ccsdk.cds.blueprintsprocessor.uat.logging.LogColor.markerOf +import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintConstants.UAT_SPECIFICATION_FILE +import org.onap.ccsdk.cds.controllerblueprints.core.utils.BluePrintArchiveUtils.Companion.compressToBytes +import org.skyscreamer.jsonassert.JSONAssert +import org.skyscreamer.jsonassert.JSONCompareMode +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.web.server.LocalServerPort +import org.springframework.core.env.ConfigurableEnvironment +import org.springframework.core.env.MapPropertySource +import org.springframework.http.HttpHeaders +import org.springframework.http.MediaType +import org.springframework.test.context.ActiveProfiles +import org.springframework.test.context.support.TestPropertySourceUtils.INLINED_PROPERTIES_PROPERTY_SOURCE_NAME +import org.yaml.snakeyaml.Yaml +import java.nio.file.Paths +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertNotNull + +@ActiveProfiles("uat") +@Suppress("MemberVisibilityCanBePrivate") +class UatServicesTest : BaseUatTest() { + + companion object { + private const val BLUEPRINT_NAME = "pnf_config" + private val BLUEPRINT_BASE_DIR = Paths.get(UAT_BLUEPRINTS_BASE_DIR, BLUEPRINT_NAME) + private val UAT_PATH = BLUEPRINT_BASE_DIR.resolve(UAT_SPECIFICATION_FILE) + private val wireMockMarker = markerOf(COLOR_WIREMOCK) + } + + @Autowired + lateinit var mapper: ObjectMapper + + @Autowired + lateinit var environment: ConfigurableEnvironment + + private val ephemeralProperties = mutableSetOf() + private val startedMockServers = mutableListOf() + + private fun setProperties(properties: Map) { + inlinedPropertySource().putAll(properties) + ephemeralProperties += properties.keys + } + + @AfterTest + fun resetProperties() { + val source = inlinedPropertySource() + ephemeralProperties.forEach { key -> source.remove(key) } + ephemeralProperties.clear() + } + + @AfterTest + fun stopMockServers() { + startedMockServers.forEach { mockServer -> + try { + mockServer.checkForUnmatchedRequests() + } finally { + mockServer.stop() + } + } + startedMockServers.clear() + } + + private fun inlinedPropertySource(): MutableMap = + (environment.propertySources[INLINED_PROPERTIES_PROPERTY_SOURCE_NAME] as MapPropertySource).source + + @LocalServerPort + var localServerPort: Int = 0 + + // use lazy evaluation to postpone until localServerPort is injected by Spring + val baseUrl: String by lazy { + "http://127.0.0.1:$localServerPort" + } + + lateinit var httpClient: CloseableHttpClient + + @BeforeTest + fun setupHttpClient() { + val defaultHeaders = listOf(BasicHeader(org.apache.http.HttpHeaders.AUTHORIZATION, + TestSecuritySettings.clientAuthToken())) + httpClient = HttpClientBuilder.create() + .setDefaultHeaders(defaultHeaders) + .build() + } + + @Test + fun `verify service validates candidate UAT`() { + // GIVEN + val cbaBytes = compressToBytes(BLUEPRINT_BASE_DIR) + val multipartEntity = MultipartEntityBuilder.create() + .setMode(HttpMultipartMode.BROWSER_COMPATIBLE) + .addBinaryBody("cba", cbaBytes, ContentType.DEFAULT_BINARY, "cba.zip") + .build() + val request = HttpPost("$baseUrl/api/v1/uat/verify").apply { + entity = multipartEntity + } + + // WHEN + httpClient.execute(request) { response -> + + // THEN + val statusLine = response.statusLine + assertThat(statusLine.statusCode, CoreMatchers.equalTo(HttpStatus.SC_OK)) + } + } + + @Test + fun `spy service generates complete UAT from bare UAT`() { + // GIVEN + val uatSpec = UAT_PATH.toFile().readText() + val fullUat = UatDefinition.load(mapper, uatSpec) + val expectedJson = mapper.writeValueAsString(fullUat) + + val bareUatBytes = fullUat.toBare().dump(mapper).toByteArray() + + fullUat.externalServices.forEach { service -> + val mockServer = createMockServer(service) + mockServer.start() + startedMockServers += mockServer + setPropertiesForMockServer(service, mockServer) + } + + val cbaBytes = compressToBytes(BLUEPRINT_BASE_DIR) + val multipartEntity = MultipartEntityBuilder.create() + .setMode(HttpMultipartMode.BROWSER_COMPATIBLE) + .addBinaryBody("cba", cbaBytes, ContentType.DEFAULT_BINARY, "cba.zip") + .addBinaryBody("uat", bareUatBytes, ContentType.DEFAULT_BINARY, "uat.yaml") + .build() + val request = HttpPost("$baseUrl/api/v1/uat/spy").apply { + entity = multipartEntity + } + + // WHEN + httpClient.execute(request) { response -> + + // THEN + val statusLine = response.statusLine + assertThat(statusLine.statusCode, CoreMatchers.equalTo(HttpStatus.SC_OK)) + val entity = response.entity + assertNotNull(entity) + val contentType = ContentType.get(entity) + assertThat(contentType.mimeType, equalToIgnoringCase("text/vnd.yaml")) + val yamlResponse = entity.content.bufferedReader().readText() + val jsonResponse = yamlToJson(yamlResponse) + JSONAssert.assertEquals(expectedJson, jsonResponse, JSONCompareMode.LENIENT) + } + } + + private fun createMockServer(service: ServiceDefinition): WireMockServer { + val mockServer = WireMockServer(wireMockConfig() + .dynamicPort() + .notifier(MarkedSlf4jNotifier(wireMockMarker)) + ) + service.expectations.forEach { expectation -> + + val request = expectation.request + val response = expectation.response + // WebTestClient always use absolute path, prefixing with "/" if necessary + val urlPattern = urlEqualTo(request.path.prefixIfNot("/")) + val mappingBuilder: MappingBuilder = request(request.method, urlPattern) + request.headers.forEach { (key, value) -> + mappingBuilder.withHeader(key, equalTo(value)) + } + if (request.body != null) { + mappingBuilder.withRequestBody(equalToJson(mapper.writeValueAsString(request.body), true, true)) + } + + val responseDefinitionBuilder: ResponseDefinitionBuilder = aResponse() + .withStatus(response.status) + if (response.body != null) { + responseDefinitionBuilder.withBody(mapper.writeValueAsBytes(response.body)) + .withHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + } + + mappingBuilder.willReturn(responseDefinitionBuilder) + + mockServer.stubFor(mappingBuilder) + } + return mockServer + } + + private fun setPropertiesForMockServer(service: ServiceDefinition, mockServer: WireMockServer) { + val selector = service.selector + val httpPort = mockServer.port() + val properties = mapOf( + "blueprintsprocessor.restclient.$selector.type" to "basic-auth", + "blueprintsprocessor.restclient.$selector.url" to "http://localhost:$httpPort/", + // TODO credentials should be validated + "blueprintsprocessor.restclient.$selector.username" to "admin", + "blueprintsprocessor.restclient.$selector.password" to "Kp8bJ4SXszM0WXlhak3eHlcse2gAw84vaoGGmJvUy2U" + ) + setProperties(properties) + } + + /** + * Borrowed from com.github.tomakehurst.wiremock.junit.WireMockRule.checkForUnmatchedRequests + */ + private fun WireMockServer.checkForUnmatchedRequests() { + val unmatchedRequests = findAllUnmatchedRequests() + if (unmatchedRequests.isNotEmpty()) { + val nearMisses = findNearMissesForAllUnmatchedRequests() + if (nearMisses.isEmpty()) { + throw VerificationException.forUnmatchedRequests(unmatchedRequests) + } else { + throw VerificationException.forUnmatchedNearMisses(nearMisses) + } + } + } + + private fun yamlToJson(yaml: String): String { + val map: Map = Yaml().load(yaml) + return mapper.writeValueAsString(map) + } +} \ No newline at end of file diff --git a/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/uat/WorkingFoldersInitializer.kt b/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/uat/WorkingFoldersInitializer.kt new file mode 100644 index 000000000..ab9ae31a0 --- /dev/null +++ b/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/uat/WorkingFoldersInitializer.kt @@ -0,0 +1,48 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2019 Nordix Foundation. + * ================================================================================ + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ +package org.onap.ccsdk.cds.blueprintsprocessor.uat + +import org.springframework.beans.factory.support.BeanDefinitionBuilder +import org.springframework.beans.factory.support.BeanDefinitionRegistry +import org.springframework.context.ApplicationContextInitializer +import org.springframework.context.ConfigurableApplicationContext +import org.springframework.stereotype.Component +import org.springframework.test.context.support.TestPropertySourceUtils + +@Component +class WorkingFoldersInitializer : ApplicationContextInitializer { + + override fun initialize(context: ConfigurableApplicationContext) { + val tempFolder = ExtendedTemporaryFolder() + val properties = listOf("Deploy", "Archive", "Working") + .map { "blueprintsprocessor.blueprint${it}Path=${tempFolder.newFolder(it)}" } + .toTypedArray() + TestPropertySourceUtils.addInlinedPropertiesToEnvironment(context, *properties) + // Expose tempFolder as a bean so it can be accessed via DI + registerSingleton(context, "tempFolder", ExtendedTemporaryFolder::class.java, tempFolder) + } + + @Suppress("SameParameterValue") + private fun registerSingleton(context: ConfigurableApplicationContext, + beanName: String, beanClass: Class, instance: T) { + val builder = BeanDefinitionBuilder.genericBeanDefinition(beanClass) { instance } + (context.beanFactory as BeanDefinitionRegistry).registerBeanDefinition(beanName, builder.beanDefinition) + } +} -- cgit 1.2.3-korg