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(
127 org.apache.http.HttpHeaders.AUTHORIZATION,
128 TestSecuritySettings.clientAuthToken()
131 httpClient = HttpClientBuilder.create()
132 .setDefaultHeaders(defaultHeaders)
137 fun `verify service validates candidate UAT`() {
139 val cbaBytes = compressToBytes(BLUEPRINT_BASE_DIR)
140 val multipartEntity = MultipartEntityBuilder.create()
141 .setMode(HttpMultipartMode.BROWSER_COMPATIBLE)
142 .addBinaryBody("cba", cbaBytes, ContentType.DEFAULT_BINARY, "cba.zip")
144 val request = HttpPost("$baseUrl/api/v1/uat/verify").apply {
145 entity = multipartEntity
149 httpClient.execute(request) { response ->
152 val statusLine = response.statusLine
153 assertThat(statusLine.statusCode, CoreMatchers.equalTo(HttpStatus.SC_OK))
158 fun `spy service generates complete UAT from bare UAT`() {
160 val uatSpec = UAT_PATH.toFile().readText()
161 val fullUat = UatDefinition.load(mapper, uatSpec)
162 val expectedJson = mapper.writeValueAsString(fullUat)
164 val bareUatBytes = fullUat.toBare().dump(mapper).toByteArray()
166 fullUat.externalServices.forEach { service ->
167 val mockServer = createMockServer(service)
169 startedMockServers += mockServer
170 setPropertiesForMockServer(service, mockServer)
173 val cbaBytes = compressToBytes(BLUEPRINT_BASE_DIR)
174 val multipartEntity = MultipartEntityBuilder.create()
175 .setMode(HttpMultipartMode.BROWSER_COMPATIBLE)
176 .addBinaryBody("cba", cbaBytes, ContentType.DEFAULT_BINARY, "cba.zip")
177 .addBinaryBody("uat", bareUatBytes, ContentType.DEFAULT_BINARY, "uat.yaml")
179 val request = HttpPost("$baseUrl/api/v1/uat/spy").apply {
180 entity = multipartEntity
184 httpClient.execute(request) { response ->
187 val statusLine = response.statusLine
188 assertThat(statusLine.statusCode, CoreMatchers.equalTo(HttpStatus.SC_OK))
189 val entity = response.entity
190 assertNotNull(entity)
191 val contentType = ContentType.get(entity)
192 assertThat(contentType.mimeType, equalToIgnoringCase("text/vnd.yaml"))
193 val yamlResponse = entity.content.bufferedReader().readText()
194 val jsonResponse = yamlToJson(yamlResponse)
195 JSONAssert.assertEquals(expectedJson, jsonResponse, JSONCompareMode.LENIENT)
199 private fun createMockServer(service: ServiceDefinition): WireMockServer {
200 val mockServer = WireMockServer(
203 .notifier(MarkedSlf4jNotifier(wireMockMarker))
205 service.expectations.forEach { expectation ->
207 val request = expectation.request
208 val response = expectation.response
209 // WebTestClient always use absolute path, prefixing with "/" if necessary
210 val urlPattern = urlEqualTo(request.path.prefixIfNot("/"))
211 val mappingBuilder: MappingBuilder = request(request.method, urlPattern)
212 request.headers.forEach { (key, value) ->
213 mappingBuilder.withHeader(key, equalTo(value))
215 if (request.body != null) {
216 mappingBuilder.withRequestBody(equalToJson(mapper.writeValueAsString(request.body), true, true))
219 val responseDefinitionBuilder: ResponseDefinitionBuilder = aResponse()
220 .withStatus(response.status)
221 if (response.body != null) {
222 responseDefinitionBuilder.withBody(mapper.writeValueAsBytes(response.body))
223 .withHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
226 mappingBuilder.willReturn(responseDefinitionBuilder)
228 mockServer.stubFor(mappingBuilder)
233 private fun setPropertiesForMockServer(service: ServiceDefinition, mockServer: WireMockServer) {
234 val selector = service.selector
235 val httpPort = mockServer.port()
236 val properties = mapOf(
237 "blueprintsprocessor.restclient.$selector.type" to "basic-auth",
238 "blueprintsprocessor.restclient.$selector.url" to "http://localhost:$httpPort/",
239 // TODO credentials should be validated
240 "blueprintsprocessor.restclient.$selector.username" to "admin",
241 "blueprintsprocessor.restclient.$selector.password" to "Kp8bJ4SXszM0WXlhak3eHlcse2gAw84vaoGGmJvUy2U"
243 setProperties(properties)
247 * Borrowed from com.github.tomakehurst.wiremock.junit.WireMockRule.checkForUnmatchedRequests
249 private fun WireMockServer.checkForUnmatchedRequests() {
250 val unmatchedRequests = findAllUnmatchedRequests()
251 if (unmatchedRequests.isNotEmpty()) {
252 val nearMisses = findNearMissesForAllUnmatchedRequests()
253 if (nearMisses.isEmpty()) {
254 throw VerificationException.forUnmatchedRequests(unmatchedRequests)
256 throw VerificationException.forUnmatchedNearMisses(nearMisses)
261 private fun yamlToJson(yaml: String): String {
262 val map: Map<String, Any> = Yaml().load(yaml)
263 return mapper.writeValueAsString(map)