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.controllerblueprints.core.BluePrintConstants.UAT_SPECIFICATION_FILE
48 import org.onap.ccsdk.cds.controllerblueprints.core.utils.BluePrintArchiveUtils.Companion.compressToBytes
49 import org.skyscreamer.jsonassert.JSONAssert
50 import org.skyscreamer.jsonassert.JSONCompareMode
51 import org.springframework.beans.factory.annotation.Autowired
52 import org.springframework.boot.web.server.LocalServerPort
53 import org.springframework.core.env.ConfigurableEnvironment
54 import org.springframework.core.env.MapPropertySource
55 import org.springframework.http.HttpHeaders
56 import org.springframework.http.MediaType
57 import org.springframework.test.context.ActiveProfiles
58 import org.springframework.test.context.support.TestPropertySourceUtils.INLINED_PROPERTIES_PROPERTY_SOURCE_NAME
59 import org.yaml.snakeyaml.Yaml
60 import java.nio.file.Paths
61 import kotlin.test.AfterTest
62 import kotlin.test.BeforeTest
63 import kotlin.test.Test
64 import kotlin.test.assertNotNull
66 @ActiveProfiles("uat")
67 @Suppress("MemberVisibilityCanBePrivate")
68 class UatServicesTest : BaseUatTest() {
71 private const val BLUEPRINT_NAME = "pnf_config"
72 private val BLUEPRINT_BASE_DIR = Paths.get(UAT_BLUEPRINTS_BASE_DIR, BLUEPRINT_NAME)
73 private val UAT_PATH = BLUEPRINT_BASE_DIR.resolve(UAT_SPECIFICATION_FILE)
74 private val wireMockMarker = markerOf(COLOR_WIREMOCK)
78 lateinit var mapper: ObjectMapper
81 lateinit var environment: ConfigurableEnvironment
83 private val ephemeralProperties = mutableSetOf<String>()
84 private val startedMockServers = mutableListOf<WireMockServer>()
86 private fun setProperties(properties: Map<String, String>) {
87 inlinedPropertySource().putAll(properties)
88 ephemeralProperties += properties.keys
92 fun resetProperties() {
93 val source = inlinedPropertySource()
94 ephemeralProperties.forEach { key -> source.remove(key) }
95 ephemeralProperties.clear()
99 fun stopMockServers() {
100 startedMockServers.forEach { mockServer ->
102 mockServer.checkForUnmatchedRequests()
107 startedMockServers.clear()
110 private fun inlinedPropertySource(): MutableMap<String, Any> =
111 (environment.propertySources[INLINED_PROPERTIES_PROPERTY_SOURCE_NAME] as MapPropertySource).source
114 var localServerPort: Int = 0
116 // use lazy evaluation to postpone until localServerPort is injected by Spring
117 val baseUrl: String by lazy {
118 "http://127.0.0.1:$localServerPort"
121 lateinit var httpClient: CloseableHttpClient
124 fun setupHttpClient() {
125 val defaultHeaders = listOf(BasicHeader(org.apache.http.HttpHeaders.AUTHORIZATION,
126 TestSecuritySettings.clientAuthToken()))
127 httpClient = HttpClientBuilder.create()
128 .setDefaultHeaders(defaultHeaders)
133 fun `verify service validates candidate UAT`() {
135 val cbaBytes = compressToBytes(BLUEPRINT_BASE_DIR)
136 val multipartEntity = MultipartEntityBuilder.create()
137 .setMode(HttpMultipartMode.BROWSER_COMPATIBLE)
138 .addBinaryBody("cba", cbaBytes, ContentType.DEFAULT_BINARY, "cba.zip")
140 val request = HttpPost("$baseUrl/api/v1/uat/verify").apply {
141 entity = multipartEntity
145 httpClient.execute(request) { response ->
148 val statusLine = response.statusLine
149 assertThat(statusLine.statusCode, CoreMatchers.equalTo(HttpStatus.SC_OK))
154 fun `spy service generates complete UAT from bare UAT`() {
156 val uatSpec = UAT_PATH.toFile().readText()
157 val fullUat = UatDefinition.load(mapper, uatSpec)
158 val expectedJson = mapper.writeValueAsString(fullUat)
160 val bareUatBytes = fullUat.toBare().dump(mapper).toByteArray()
162 fullUat.externalServices.forEach { service ->
163 val mockServer = createMockServer(service)
165 startedMockServers += mockServer
166 setPropertiesForMockServer(service, mockServer)
169 val cbaBytes = compressToBytes(BLUEPRINT_BASE_DIR)
170 val multipartEntity = MultipartEntityBuilder.create()
171 .setMode(HttpMultipartMode.BROWSER_COMPATIBLE)
172 .addBinaryBody("cba", cbaBytes, ContentType.DEFAULT_BINARY, "cba.zip")
173 .addBinaryBody("uat", bareUatBytes, ContentType.DEFAULT_BINARY, "uat.yaml")
175 val request = HttpPost("$baseUrl/api/v1/uat/spy").apply {
176 entity = multipartEntity
180 httpClient.execute(request) { response ->
183 val statusLine = response.statusLine
184 assertThat(statusLine.statusCode, CoreMatchers.equalTo(HttpStatus.SC_OK))
185 val entity = response.entity
186 assertNotNull(entity)
187 val contentType = ContentType.get(entity)
188 assertThat(contentType.mimeType, equalToIgnoringCase("text/vnd.yaml"))
189 val yamlResponse = entity.content.bufferedReader().readText()
190 val jsonResponse = yamlToJson(yamlResponse)
191 JSONAssert.assertEquals(expectedJson, jsonResponse, JSONCompareMode.LENIENT)
195 private fun createMockServer(service: ServiceDefinition): WireMockServer {
196 val mockServer = WireMockServer(wireMockConfig()
198 .notifier(MarkedSlf4jNotifier(wireMockMarker))
200 service.expectations.forEach { expectation ->
202 val request = expectation.request
203 val response = expectation.response
204 // WebTestClient always use absolute path, prefixing with "/" if necessary
205 val urlPattern = urlEqualTo(request.path.prefixIfNot("/"))
206 val mappingBuilder: MappingBuilder = request(request.method, urlPattern)
207 request.headers.forEach { (key, value) ->
208 mappingBuilder.withHeader(key, equalTo(value))
210 if (request.body != null) {
211 mappingBuilder.withRequestBody(equalToJson(mapper.writeValueAsString(request.body), true, true))
214 val responseDefinitionBuilder: ResponseDefinitionBuilder = aResponse()
215 .withStatus(response.status)
216 if (response.body != null) {
217 responseDefinitionBuilder.withBody(mapper.writeValueAsBytes(response.body))
218 .withHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
221 mappingBuilder.willReturn(responseDefinitionBuilder)
223 mockServer.stubFor(mappingBuilder)
228 private fun setPropertiesForMockServer(service: ServiceDefinition, mockServer: WireMockServer) {
229 val selector = service.selector
230 val httpPort = mockServer.port()
231 val properties = mapOf(
232 "blueprintsprocessor.restclient.$selector.type" to "basic-auth",
233 "blueprintsprocessor.restclient.$selector.url" to "http://localhost:$httpPort/",
234 // TODO credentials should be validated
235 "blueprintsprocessor.restclient.$selector.username" to "admin",
236 "blueprintsprocessor.restclient.$selector.password" to "Kp8bJ4SXszM0WXlhak3eHlcse2gAw84vaoGGmJvUy2U"
238 setProperties(properties)
242 * Borrowed from com.github.tomakehurst.wiremock.junit.WireMockRule.checkForUnmatchedRequests
244 private fun WireMockServer.checkForUnmatchedRequests() {
245 val unmatchedRequests = findAllUnmatchedRequests()
246 if (unmatchedRequests.isNotEmpty()) {
247 val nearMisses = findNearMissesForAllUnmatchedRequests()
248 if (nearMisses.isEmpty()) {
249 throw VerificationException.forUnmatchedRequests(unmatchedRequests)
251 throw VerificationException.forUnmatchedNearMisses(nearMisses)
256 private fun yamlToJson(yaml: String): String {
257 val map: Map<String, Any> = Yaml().load(yaml)
258 return mapper.writeValueAsString(map)