summaryrefslogtreecommitdiffstats
path: root/ms/blueprintsprocessor/application
diff options
context:
space:
mode:
Diffstat (limited to 'ms/blueprintsprocessor/application')
-rw-r--r--ms/blueprintsprocessor/application/src/main/resources/application-local.yml4
-rw-r--r--ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/uat/UatServicesTest.kt23
-rw-r--r--ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/uat/utils/InvalidUatDefinition.kt22
-rw-r--r--ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/uat/utils/UatDefinition.kt24
-rw-r--r--ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/uat/utils/UatExecutor.kt159
5 files changed, 149 insertions, 83 deletions
diff --git a/ms/blueprintsprocessor/application/src/main/resources/application-local.yml b/ms/blueprintsprocessor/application/src/main/resources/application-local.yml
index de2cf4e52..f2843322c 100644
--- a/ms/blueprintsprocessor/application/src/main/resources/application-local.yml
+++ b/ms/blueprintsprocessor/application/src/main/resources/application-local.yml
@@ -38,10 +38,10 @@ blueprintsprocessor:
remoteScriptCommand:
enabled: true
restclient:
- sdncodl:
+ sdnc:
password: Kp8bJ4SXszM0WXlhak3eHlcse2gAw84vaoGGmJvUy2U
type: basic-auth
- url: http://localhost:8282/
+ url: http://localhost:8282
username: admin
restconfEnabled: true
controllerblueprints:
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
index aebda8c07..4e7d4ce40 100644
--- 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
@@ -30,6 +30,8 @@ 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 com.github.tomakehurst.wiremock.http.HttpHeader
+import com.github.tomakehurst.wiremock.http.HttpHeaders
import org.apache.http.HttpStatus
import org.apache.http.client.methods.HttpPost
import org.apache.http.entity.ContentType
@@ -55,8 +57,6 @@ 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
@@ -211,7 +211,6 @@ class UatServicesTest : BaseUatTest() {
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)
@@ -222,15 +221,19 @@ class UatServicesTest : BaseUatTest() {
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)
+ for (response in expectation.responses) {
+ val responseDefinitionBuilder: ResponseDefinitionBuilder = aResponse()
+ .withStatus(response.status)
+ if (response.body != null) {
+ responseDefinitionBuilder.withBody(mapper.writeValueAsBytes(response.body))
+ .withHeaders(HttpHeaders(
+ response.headers.entries.map { e -> HttpHeader(e.key, e.value) }))
+ }
+
+ // TODO: MockServer verification for multiple responses should be done using Wiremock scenarios
+ mappingBuilder.willReturn(responseDefinitionBuilder)
}
- mappingBuilder.willReturn(responseDefinitionBuilder)
-
mockServer.stubFor(mappingBuilder)
}
return mockServer
diff --git a/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/uat/utils/InvalidUatDefinition.kt b/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/uat/utils/InvalidUatDefinition.kt
new file mode 100644
index 000000000..4a756411f
--- /dev/null
+++ b/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/uat/utils/InvalidUatDefinition.kt
@@ -0,0 +1,22 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2020 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.utils
+
+class InvalidUatDefinition(message: String) : RuntimeException(message)
diff --git a/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/uat/utils/UatDefinition.kt b/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/uat/utils/UatDefinition.kt
index c45ac45c6..17b79f588 100644
--- a/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/uat/utils/UatDefinition.kt
+++ b/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/uat/utils/UatDefinition.kt
@@ -47,18 +47,30 @@ data class RequestDefinition(
)
@JsonInclude(JsonInclude.Include.NON_EMPTY)
-data class ResponseDefinition(val status: Int = 200, val body: JsonNode? = null) {
+data class ResponseDefinition(val status: Int = 200, val body: JsonNode? = null, val headers: Map<String, String> = mapOf("Content-Type" to "application/json")) {
companion object {
- val DEFAULT_RESPONSE = ResponseDefinition()
+ val DEFAULT_RESPONSES = listOf(ResponseDefinition())
}
}
@JsonInclude(JsonInclude.Include.NON_EMPTY)
-data class ExpectationDefinition(
+class ExpectationDefinition(
val request: RequestDefinition,
- val response: ResponseDefinition = ResponseDefinition.DEFAULT_RESPONSE
-)
+ response: ResponseDefinition?,
+ responses: List<ResponseDefinition>? = null,
+ val times: String = ">= 1"
+) {
+ val responses: List<ResponseDefinition> = resolveOneOrMany(response, responses, ResponseDefinition.DEFAULT_RESPONSES)
+
+ companion object {
+ fun <T> resolveOneOrMany(one: T?, many: List<T>?, defaultMany: List<T>): List<T> = when {
+ many != null -> many
+ one != null -> listOf(one)
+ else -> defaultMany
+ }
+ }
+}
@JsonInclude(JsonInclude.Include.NON_EMPTY)
data class ServiceDefinition(val selector: String, val expectations: List<ExpectationDefinition>)
@@ -97,6 +109,6 @@ data class UatDefinition(
companion object {
fun load(mapper: ObjectMapper, spec: String): UatDefinition =
- mapper.convertValue(Yaml().load(spec), UatDefinition::class.java)
+ mapper.convertValue(Yaml().load(spec), UatDefinition::class.java)
}
}
diff --git a/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/uat/utils/UatExecutor.kt b/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/uat/utils/UatExecutor.kt
index a904fa9b6..d120e71d6 100644
--- a/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/uat/utils/UatExecutor.kt
+++ b/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/uat/utils/UatExecutor.kt
@@ -24,9 +24,10 @@ import com.fasterxml.jackson.databind.ObjectMapper
import com.nhaarman.mockitokotlin2.any
import com.nhaarman.mockitokotlin2.argThat
import com.nhaarman.mockitokotlin2.atLeast
-import com.nhaarman.mockitokotlin2.atLeastOnce
+import com.nhaarman.mockitokotlin2.atMost
import com.nhaarman.mockitokotlin2.eq
import com.nhaarman.mockitokotlin2.mock
+import com.nhaarman.mockitokotlin2.times
import com.nhaarman.mockitokotlin2.verify
import com.nhaarman.mockitokotlin2.verifyNoMoreInteractions
import com.nhaarman.mockitokotlin2.whenever
@@ -44,6 +45,7 @@ import org.hamcrest.CoreMatchers.equalTo
import org.hamcrest.CoreMatchers.notNullValue
import org.hamcrest.MatcherAssert.assertThat
import org.mockito.Answers
+import org.mockito.verification.VerificationMode
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
@@ -77,7 +79,8 @@ class UatExecutor(
companion object {
private const val NOOP_PASSWORD_PREFIX = "{noop}"
-
+ private const val PROPERTY_IN_UAT = "IN_UAT"
+ private val TIMES_SPEC_REGEX = "([<>]=?)?\\s*(\\d+)".toRegex()
private val log: Logger = LoggerFactory.getLogger(UatExecutor::class.java)
private val mockLoggingListener = MockInvocationLogger(markerOf(COLOR_MOCKITO))
}
@@ -103,23 +106,24 @@ class UatExecutor(
fun execute(uat: UatDefinition, cbaBytes: ByteArray): UatDefinition {
val defaultHeaders = listOf(BasicHeader(HttpHeaders.AUTHORIZATION, clientAuthToken()))
val httpClient = HttpClientBuilder.create()
- .setDefaultHeaders(defaultHeaders)
- .build()
+ .setDefaultHeaders(defaultHeaders)
+ .build()
// Only if externalServices are defined
val mockInterceptor = MockPreInterceptor()
// Always defined and used, whatever the case
val spyInterceptor = SpyPostInterceptor(mapper)
restClientFactory.setInterceptors(mockInterceptor, spyInterceptor)
try {
- // Configure mocked external services and save their expected requests for further validation
- val requestsPerClient = uat.externalServices.associateBy(
- { service ->
- createRestClientMock(service.expectations).also { restClient ->
- // side-effect: register restClient to override real instance
- mockInterceptor.registerMock(service.selector, restClient)
- }
- },
- { service -> service.expectations.map { it.request } }
+ markUatBegin()
+ // Configure mocked external services and save their expectations for further validation
+ val expectationsPerClient = uat.externalServices.associateBy(
+ { service ->
+ createRestClientMock(service.expectations).also { restClient ->
+ // side-effect: register restClient to override real instance
+ mockInterceptor.registerMock(service.selector, restClient)
+ }
+ },
+ { service -> service.expectations }
)
val newProcesses = httpClient.use { client ->
@@ -130,26 +134,27 @@ class UatExecutor(
log.info("Executing process '${process.name}'")
val responseNormalizer = JsonNormalizer.getNormalizer(mapper, process.responseNormalizerSpec)
val actualResponse = processBlueprint(
- client, process.request,
- process.expectedResponse, responseNormalizer
+ client, process.request,
+ process.expectedResponse, responseNormalizer
)
ProcessDefinition(
- process.name,
- process.request,
- actualResponse,
- process.responseNormalizerSpec
+ process.name,
+ process.request,
+ actualResponse,
+ 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 { assertJsonEquals(request.body, this) },
- argThat(RequiredMapEntriesMatcher(request.headers))
+ for ((mockClient, expectations) in expectationsPerClient) {
+ expectations.forEach { expectation ->
+ val request = expectation.request
+ verify(mockClient, evalVerificationMode(expectation.times)).exchangeResource(
+ eq(request.method),
+ eq(request.path),
+ argThat { assertJsonEquals(request.body, this) },
+ argThat(RequiredMapEntriesMatcher(request.headers))
)
}
// Don't mind the invocations to the overloaded exchangeResource(String, String, String)
@@ -158,40 +163,51 @@ class UatExecutor(
}
val newExternalServices = spyInterceptor.getSpies()
- .map(SpyService::asServiceDefinition)
+ .map(SpyService::asServiceDefinition)
return UatDefinition(newProcesses, newExternalServices)
} finally {
restClientFactory.clearInterceptors()
+ markUatEnd()
}
}
+ private fun markUatBegin() {
+ System.setProperty(PROPERTY_IN_UAT, "1")
+ }
+
+ private fun markUatEnd() {
+ System.clearProperty(PROPERTY_IN_UAT)
+ }
+
private fun createRestClientMock(restExpectations: List<ExpectationDefinition>):
BlueprintWebClientService {
val restClient = mock<BlueprintWebClientService>(
- defaultAnswer = Answers.RETURNS_SMART_NULLS,
- // our custom verboseLogging handler
- invocationListeners = arrayOf(mockLoggingListener)
+ defaultAnswer = Answers.RETURNS_SMART_NULLS,
+ // our custom verboseLogging handler
+ invocationListeners = arrayOf(mockLoggingListener)
)
// Delegates to overloaded exchangeResource(String, String, String, Map<String, String>)
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())
- }
+ .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()
- )
+ var stubbing = whenever(
+ restClient.exchangeResource(
+ eq(expectation.request.method),
+ eq(expectation.request.path),
+ any(),
+ any()
+ )
)
- .thenReturn(WebClientResponse(expectation.response.status, expectation.response.body.toString()))
+ for (response in expectation.responses) {
+ stubbing = stubbing.thenReturn(WebClientResponse(response.status, response.body.toString()))
+ }
}
return restClient
}
@@ -199,9 +215,9 @@ class UatExecutor(
@Throws(AssertionError::class)
private fun uploadBlueprint(client: HttpClient, cbaBytes: ByteArray) {
val multipartEntity = MultipartEntityBuilder.create()
- .setMode(HttpMultipartMode.BROWSER_COMPATIBLE)
- .addBinaryBody("file", cbaBytes, ContentType.DEFAULT_BINARY, "cba.zip")
- .build()
+ .setMode(HttpMultipartMode.BROWSER_COMPATIBLE)
+ .addBinaryBody("file", cbaBytes, ContentType.DEFAULT_BINARY, "cba.zip")
+ .build()
val request = HttpPost("$baseUrl/api/v1/blueprint-model/publish").apply {
entity = multipartEntity
}
@@ -236,6 +252,19 @@ class UatExecutor(
return mapper.readTree(actualResponse)!!
}
+ private fun evalVerificationMode(times: String): VerificationMode {
+ val matchResult = TIMES_SPEC_REGEX.matchEntire(times) ?: throw InvalidUatDefinition(
+ "Time specification '$times' does not follow expected format $TIMES_SPEC_REGEX")
+ val counter = matchResult.groups[2]!!.value.toInt()
+ return when (matchResult.groups[1]?.value) {
+ ">=" -> atLeast(counter)
+ ">" -> atLeast(counter + 1)
+ "<=" -> atMost(counter)
+ "<" -> atMost(counter - 1)
+ else -> times(counter)
+ }
+ }
+
@Throws(AssertionError::class)
private fun assertJsonEquals(expected: JsonNode?, actual: String): Boolean {
// special case
@@ -249,15 +278,15 @@ class UatExecutor(
}
private fun localServerPort(): Int =
- (environment.getProperty("local.server.port")
- ?: environment.getRequiredProperty("blueprint.httpPort")).toInt()
+ (environment.getProperty("local.server.port")
+ ?: environment.getRequiredProperty("blueprint.httpPort")).toInt()
private fun clientAuthToken(): String {
val username = environment.getRequiredProperty("security.user.name")
val password = environment.getRequiredProperty("security.user.password")
val plainPassword = when {
password.startsWith(NOOP_PASSWORD_PREFIX) -> password.substring(
- NOOP_PASSWORD_PREFIX.length)
+ NOOP_PASSWORD_PREFIX.length)
else -> username
}
return "Basic " + Base64Utils.encodeToString("$username:$plainPassword".toByteArray())
@@ -271,7 +300,7 @@ class UatExecutor(
}
override fun getInstance(selector: String): BlueprintWebClientService? =
- mocks[selector]
+ mocks[selector]
fun registerMock(selector: String, client: BlueprintWebClientService) {
mocks[selector] = client
@@ -293,7 +322,7 @@ class UatExecutor(
}
fun getSpies(): List<SpyService> =
- spies.values.toList()
+ spies.values.toList()
}
private class SpyService(
@@ -301,14 +330,14 @@ class UatExecutor(
val selector: String,
private val realService: BlueprintWebClientService
) :
- BlueprintWebClientService by realService {
+ BlueprintWebClientService by realService {
private val expectations: MutableList<ExpectationDefinition> = mutableListOf()
override fun exchangeResource(methodType: String, path: String, request: String): WebClientResponse<String> =
- exchangeResource(methodType, path, request,
- DEFAULT_HEADERS
- )
+ exchangeResource(methodType, path, request,
+ DEFAULT_HEADERS
+ )
override fun exchangeResource(
methodType: String,
@@ -317,7 +346,7 @@ class UatExecutor(
headers: Map<String, String>
): WebClientResponse<String> {
val requestDefinition =
- RequestDefinition(methodType, path, headers, toJson(request))
+ RequestDefinition(methodType, path, headers, toJson(request))
val realAnswer = realService.exchangeResource(methodType, path, request, headers)
val responseBody = when {
// TODO: confirm if we need to normalize the response here
@@ -325,12 +354,12 @@ class UatExecutor(
else -> null
}
val responseDefinition =
- ResponseDefinition(realAnswer.status, responseBody)
+ ResponseDefinition(realAnswer.status, responseBody)
expectations.add(
- ExpectationDefinition(
- requestDefinition,
- responseDefinition
- )
+ ExpectationDefinition(
+ requestDefinition,
+ responseDefinition
+ )
)
return realAnswer
}
@@ -340,7 +369,7 @@ class UatExecutor(
}
fun asServiceDefinition() =
- ServiceDefinition(selector, expectations)
+ ServiceDefinition(selector, expectations)
private fun toJson(str: String): JsonNode? {
return when {
@@ -351,8 +380,8 @@ class UatExecutor(
companion object {
private val DEFAULT_HEADERS = mapOf(
- HttpHeaders.CONTENT_TYPE to MediaType.APPLICATION_JSON_VALUE,
- HttpHeaders.ACCEPT to MediaType.APPLICATION_JSON_VALUE
+ HttpHeaders.CONTENT_TYPE to MediaType.APPLICATION_JSON_VALUE,
+ HttpHeaders.ACCEPT to MediaType.APPLICATION_JSON_VALUE
)
}
}