2 * ============LICENSE_START=======================================================
3 * Copyright (C) 2019 Nordix Foundation.
4 * ================================================================================
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
17 * SPDX-License-Identifier: Apache-2.0
18 * ============LICENSE_END=========================================================
20 package org.onap.ccsdk.cds.blueprintsprocessor.uat
22 import com.fasterxml.jackson.databind.ObjectMapper
23 import com.github.tomakehurst.wiremock.WireMockServer
24 import com.github.tomakehurst.wiremock.client.MappingBuilder
25 import com.github.tomakehurst.wiremock.client.ResponseDefinitionBuilder
26 import com.github.tomakehurst.wiremock.client.VerificationException
27 import com.github.tomakehurst.wiremock.client.WireMock.aResponse
28 import com.github.tomakehurst.wiremock.client.WireMock.equalTo
29 import com.github.tomakehurst.wiremock.client.WireMock.equalToJson
30 import com.github.tomakehurst.wiremock.client.WireMock.request
31 import com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo
32 import com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig
33 import org.apache.http.HttpStatus
34 import org.apache.http.client.methods.HttpPost
35 import org.apache.http.entity.ContentType
36 import org.apache.http.entity.mime.HttpMultipartMode
37 import org.apache.http.entity.mime.MultipartEntityBuilder
38 import org.apache.http.impl.client.CloseableHttpClient
39 import org.apache.http.impl.client.HttpClientBuilder
40 import org.apache.http.message.BasicHeader
41 import org.hamcrest.CoreMatchers
42 import org.hamcrest.MatcherAssert.assertThat
43 import org.hamcrest.Matchers.equalToIgnoringCase
44 import org.jetbrains.kotlin.konan.util.prefixIfNot
45 import org.onap.ccsdk.cds.blueprintsprocessor.uat.logging.LogColor.COLOR_WIREMOCK
46 import org.onap.ccsdk.cds.blueprintsprocessor.uat.logging.LogColor.markerOf
47 import org.onap.ccsdk.cds.blueprintsprocessor.uat.utils.MarkedSlf4jNotifier
48 import org.onap.ccsdk.cds.blueprintsprocessor.uat.utils.ServiceDefinition
49 import org.onap.ccsdk.cds.blueprintsprocessor.uat.utils.TestSecuritySettings
50 import org.onap.ccsdk.cds.blueprintsprocessor.uat.utils.UatDefinition
51 import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintConstants.UAT_SPECIFICATION_FILE
52 import org.onap.ccsdk.cds.controllerblueprints.core.utils.BluePrintArchiveUtils.Companion.compressToBytes
53 import org.skyscreamer.jsonassert.JSONAssert
54 import org.skyscreamer.jsonassert.JSONCompareMode
55 import org.springframework.beans.factory.annotation.Autowired
56 import org.springframework.boot.web.server.LocalServerPort
57 import org.springframework.core.env.ConfigurableEnvironment
58 import org.springframework.core.env.MapPropertySource
59 import org.springframework.http.HttpHeaders
60 import org.springframework.http.MediaType
61 import org.springframework.test.context.ActiveProfiles
62 import org.springframework.test.context.support.TestPropertySourceUtils.INLINED_PROPERTIES_PROPERTY_SOURCE_NAME
63 import org.yaml.snakeyaml.Yaml
64 import java.nio.file.Paths
65 import kotlin.test.AfterTest
66 import kotlin.test.BeforeTest
67 import kotlin.test.Test
68 import kotlin.test.assertNotNull
70 @ActiveProfiles("uat")
71 @Suppress("MemberVisibilityCanBePrivate")
72 class UatServicesTest : BaseUatTest() {
75 private const val BLUEPRINT_NAME = "pnf_config"
76 private val BLUEPRINT_BASE_DIR = Paths.get(UAT_BLUEPRINTS_BASE_DIR, BLUEPRINT_NAME)
77 private val UAT_PATH = BLUEPRINT_BASE_DIR.resolve(UAT_SPECIFICATION_FILE)
78 private val wireMockMarker = markerOf(COLOR_WIREMOCK)
82 lateinit var mapper: ObjectMapper
85 lateinit var environment: ConfigurableEnvironment
87 private val ephemeralProperties = mutableSetOf<String>()
88 private val startedMockServers = mutableListOf<WireMockServer>()
90 private fun setProperties(properties: Map<String, String>) {
91 inlinedPropertySource().putAll(properties)
92 ephemeralProperties += properties.keys
96 fun resetProperties() {
97 val source = inlinedPropertySource()
98 ephemeralProperties.forEach { key -> source.remove(key) }
99 ephemeralProperties.clear()
103 fun stopMockServers() {
104 startedMockServers.forEach { mockServer ->
106 mockServer.checkForUnmatchedRequests()
111 startedMockServers.clear()
114 private fun inlinedPropertySource(): MutableMap<String, Any> =
115 (environment.propertySources[INLINED_PROPERTIES_PROPERTY_SOURCE_NAME] as MapPropertySource).source
118 var localServerPort: Int = 0
120 // use lazy evaluation to postpone until localServerPort is injected by Spring
121 val baseUrl: String by lazy {
122 "http://127.0.0.1:$localServerPort"
125 lateinit var httpClient: CloseableHttpClient
128 fun setupHttpClient() {
129 val defaultHeaders = listOf(
131 org.apache.http.HttpHeaders.AUTHORIZATION,
132 TestSecuritySettings.clientAuthToken()
135 httpClient = HttpClientBuilder.create()
136 .setDefaultHeaders(defaultHeaders)
141 fun `verify service validates candidate UAT`() {
143 val cbaBytes = compressToBytes(BLUEPRINT_BASE_DIR)
144 val multipartEntity = MultipartEntityBuilder.create()
145 .setMode(HttpMultipartMode.BROWSER_COMPATIBLE)
146 .addBinaryBody("cba", cbaBytes, ContentType.DEFAULT_BINARY, "cba.zip")
148 val request = HttpPost("$baseUrl/api/v1/uat/verify").apply {
149 entity = multipartEntity
153 httpClient.execute(request) { response ->
156 val statusLine = response.statusLine
157 assertThat(statusLine.statusCode, CoreMatchers.equalTo(HttpStatus.SC_OK))
162 fun `spy service generates complete UAT from bare UAT`() {
164 val uatSpec = UAT_PATH.toFile().readText()
165 val fullUat = UatDefinition.load(mapper, uatSpec)
166 val expectedJson = mapper.writeValueAsString(fullUat)
168 val bareUatBytes = fullUat.toBare().dump(mapper).toByteArray()
170 fullUat.externalServices.forEach { service ->
171 val mockServer = createMockServer(service)
173 startedMockServers += mockServer
174 setPropertiesForMockServer(service, mockServer)
177 val cbaBytes = compressToBytes(BLUEPRINT_BASE_DIR)
178 val multipartEntity = MultipartEntityBuilder.create()
179 .setMode(HttpMultipartMode.BROWSER_COMPATIBLE)
180 .addBinaryBody("cba", cbaBytes, ContentType.DEFAULT_BINARY, "cba.zip")
181 .addBinaryBody("uat", bareUatBytes, ContentType.DEFAULT_BINARY, "uat.yaml")
183 val request = HttpPost("$baseUrl/api/v1/uat/spy").apply {
184 entity = multipartEntity
188 httpClient.execute(request) { response ->
191 val statusLine = response.statusLine
192 assertThat(statusLine.statusCode, CoreMatchers.equalTo(HttpStatus.SC_OK))
193 val entity = response.entity
194 assertNotNull(entity)
195 val contentType = ContentType.get(entity)
196 assertThat(contentType.mimeType, equalToIgnoringCase("text/vnd.yaml"))
197 val yamlResponse = entity.content.bufferedReader().readText()
198 val jsonResponse = yamlToJson(yamlResponse)
199 JSONAssert.assertEquals(expectedJson, jsonResponse, JSONCompareMode.LENIENT)
203 private fun createMockServer(service: ServiceDefinition): WireMockServer {
204 val mockServer = WireMockServer(
207 .notifier(MarkedSlf4jNotifier(wireMockMarker))
209 service.expectations.forEach { expectation ->
211 val request = expectation.request
212 val response = expectation.response
213 // WebTestClient always use absolute path, prefixing with "/" if necessary
214 val urlPattern = urlEqualTo(request.path.prefixIfNot("/"))
215 val mappingBuilder: MappingBuilder = request(request.method, urlPattern)
216 request.headers.forEach { (key, value) ->
217 mappingBuilder.withHeader(key, equalTo(value))
219 if (request.body != null) {
220 mappingBuilder.withRequestBody(equalToJson(mapper.writeValueAsString(request.body), true, true))
223 val responseDefinitionBuilder: ResponseDefinitionBuilder = aResponse()
224 .withStatus(response.status)
225 if (response.body != null) {
226 responseDefinitionBuilder.withBody(mapper.writeValueAsBytes(response.body))
227 .withHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
230 mappingBuilder.willReturn(responseDefinitionBuilder)
232 mockServer.stubFor(mappingBuilder)
237 private fun setPropertiesForMockServer(service: ServiceDefinition, mockServer: WireMockServer) {
238 val selector = service.selector
239 val httpPort = mockServer.port()
240 val properties = mapOf(
241 "blueprintsprocessor.restclient.$selector.type" to "basic-auth",
242 "blueprintsprocessor.restclient.$selector.url" to "http://localhost:$httpPort/",
243 // TODO credentials should be validated
244 "blueprintsprocessor.restclient.$selector.username" to "admin",
245 "blueprintsprocessor.restclient.$selector.password" to "Kp8bJ4SXszM0WXlhak3eHlcse2gAw84vaoGGmJvUy2U"
247 setProperties(properties)
251 * Borrowed from com.github.tomakehurst.wiremock.junit.WireMockRule.checkForUnmatchedRequests
253 private fun WireMockServer.checkForUnmatchedRequests() {
254 val unmatchedRequests = findAllUnmatchedRequests()
255 if (unmatchedRequests.isNotEmpty()) {
256 val nearMisses = findNearMissesForAllUnmatchedRequests()
257 if (nearMisses.isEmpty()) {
258 throw VerificationException.forUnmatchedRequests(unmatchedRequests)
260 throw VerificationException.forUnmatchedNearMisses(nearMisses)
265 private fun yamlToJson(yaml: String): String {
266 val map: Map<String, Any> = Yaml().load(yaml)
267 return mapper.writeValueAsString(map)