aboutsummaryrefslogtreecommitdiffstats
path: root/ms/blueprintsprocessor/application
diff options
context:
space:
mode:
authorgummar <raj.gumma@est.tech>2020-02-18 18:54:44 +0000
committerKAPIL SINGAL <ks220y@att.com>2020-02-26 15:23:42 +0000
commit94ad509756f17e79c278e3cc2f87440009125cd1 (patch)
treeccd6a842eb0fdca66689b2569cd71cb78ec81cd2 /ms/blueprintsprocessor/application
parentac31d2159014a84de91b6c7baeb29adf90284c10 (diff)
Merge SW Upgrade Blueprint into PNF_AAI and create one UAT BP for PNF
UAT: Add support to multiple responses for a single request Set property IN_UAT=1 during UAT execution so blueprints can tune their settings to values more suitable for testing (like timeouts) Add 'times' field to specify expected number of invocations Add UAT blueprint script for PNF SW Upgrade UC Add current thread check for Hazelcast distributed lock Resolve URI before returning Issue-ID: CCSDK-2091 Change-Id: Id256bad043488f88f1b60015ebf9ade4be607fa2 Signed-off-by: gummar <raj.gumma@est.tech>
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
)
}
}