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.onap.ccsdk.cds.blueprintsprocessor.uat.logging.LogColor.COLOR_WIREMOCK
45 import org.onap.ccsdk.cds.blueprintsprocessor.uat.logging.LogColor.markerOf
46 import org.onap.ccsdk.cds.blueprintsprocessor.uat.utils.MarkedSlf4jNotifier
47 import org.onap.ccsdk.cds.blueprintsprocessor.uat.utils.ServiceDefinition
48 import org.onap.ccsdk.cds.blueprintsprocessor.uat.utils.TestSecuritySettings
49 import org.onap.ccsdk.cds.blueprintsprocessor.uat.utils.UatDefinition
50 import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintConstants.UAT_SPECIFICATION_FILE
51 import org.onap.ccsdk.cds.controllerblueprints.core.utils.BluePrintArchiveUtils.Companion.compressToBytes
52 import org.skyscreamer.jsonassert.JSONAssert
53 import org.skyscreamer.jsonassert.JSONCompareMode
54 import org.springframework.beans.factory.annotation.Autowired
55 import org.springframework.boot.web.server.LocalServerPort
56 import org.springframework.core.env.ConfigurableEnvironment
57 import org.springframework.core.env.MapPropertySource
58 import org.springframework.http.HttpHeaders
59 import org.springframework.http.MediaType
60 import org.springframework.test.context.ActiveProfiles
61 import org.springframework.test.context.support.TestPropertySourceUtils.INLINED_PROPERTIES_PROPERTY_SOURCE_NAME
62 import org.yaml.snakeyaml.Yaml
63 import java.nio.file.Paths
64 import kotlin.test.AfterTest
65 import kotlin.test.BeforeTest
66 import kotlin.test.Test
67 import kotlin.test.assertNotNull
69 fun String.prefixIfNot(prefix: String) =
70 if (this.startsWith(prefix)) this else "$prefix$this"
72 @ActiveProfiles("uat")
73 @Suppress("MemberVisibilityCanBePrivate")
74 class UatServicesTest : BaseUatTest() {
77 private const val BLUEPRINT_NAME = "pnf_config"
78 private val BLUEPRINT_BASE_DIR = Paths.get(UAT_BLUEPRINTS_BASE_DIR, BLUEPRINT_NAME)
79 private val UAT_PATH = BLUEPRINT_BASE_DIR.resolve(UAT_SPECIFICATION_FILE)
80 private val wireMockMarker = markerOf(COLOR_WIREMOCK)
84 lateinit var mapper: ObjectMapper
87 lateinit var environment: ConfigurableEnvironment
89 private val ephemeralProperties = mutableSetOf<String>()
90 private val startedMockServers = mutableListOf<WireMockServer>()
92 private fun setProperties(properties: Map<String, String>) {
93 inlinedPropertySource().putAll(properties)
94 ephemeralProperties += properties.keys
98 fun resetProperties() {
99 val source = inlinedPropertySource()
100 ephemeralProperties.forEach { key -> source.remove(key) }
101 ephemeralProperties.clear()
105 fun stopMockServers() {
106 startedMockServers.forEach { mockServer ->
108 mockServer.checkForUnmatchedRequests()
113 startedMockServers.clear()
116 private fun inlinedPropertySource(): MutableMap<String, Any> =
117 (environment.propertySources[INLINED_PROPERTIES_PROPERTY_SOURCE_NAME] as MapPropertySource).source
120 var localServerPort: Int = 0
122 // use lazy evaluation to postpone until localServerPort is injected by Spring
123 val baseUrl: String by lazy {
124 "http://127.0.0.1:$localServerPort"
127 lateinit var httpClient: CloseableHttpClient
130 fun setupHttpClient() {
131 val defaultHeaders = listOf(
133 org.apache.http.HttpHeaders.AUTHORIZATION,
134 TestSecuritySettings.clientAuthToken()
137 httpClient = HttpClientBuilder.create()
138 .setDefaultHeaders(defaultHeaders)
143 fun `verify service validates candidate UAT`() {
145 val cbaBytes = compressToBytes(BLUEPRINT_BASE_DIR)
146 val multipartEntity = MultipartEntityBuilder.create()
147 .setMode(HttpMultipartMode.BROWSER_COMPATIBLE)
148 .addBinaryBody("cba", cbaBytes, ContentType.DEFAULT_BINARY, "cba.zip")
150 val request = HttpPost("$baseUrl/api/v1/uat/verify").apply {
151 entity = multipartEntity
155 httpClient.execute(request) { response ->
158 val statusLine = response.statusLine
159 assertThat(statusLine.statusCode, CoreMatchers.equalTo(HttpStatus.SC_OK))
164 fun `spy service generates complete UAT from bare UAT`() {
166 val uatSpec = UAT_PATH.toFile().readText()
167 val fullUat = UatDefinition.load(mapper, uatSpec)
168 val expectedJson = mapper.writeValueAsString(fullUat)
170 val bareUatBytes = fullUat.toBare().dump(mapper).toByteArray()
172 fullUat.externalServices.forEach { service ->
173 val mockServer = createMockServer(service)
175 startedMockServers += mockServer
176 setPropertiesForMockServer(service, mockServer)
179 val cbaBytes = compressToBytes(BLUEPRINT_BASE_DIR)
180 val multipartEntity = MultipartEntityBuilder.create()
181 .setMode(HttpMultipartMode.BROWSER_COMPATIBLE)
182 .addBinaryBody("cba", cbaBytes, ContentType.DEFAULT_BINARY, "cba.zip")
183 .addBinaryBody("uat", bareUatBytes, ContentType.DEFAULT_BINARY, "uat.yaml")
185 val request = HttpPost("$baseUrl/api/v1/uat/spy").apply {
186 entity = multipartEntity
190 httpClient.execute(request) { response ->
193 val statusLine = response.statusLine
194 assertThat(statusLine.statusCode, CoreMatchers.equalTo(HttpStatus.SC_OK))
195 val entity = response.entity
196 assertNotNull(entity)
197 val contentType = ContentType.get(entity)
198 assertThat(contentType.mimeType, equalToIgnoringCase("text/vnd.yaml"))
199 val yamlResponse = entity.content.bufferedReader().readText()
200 val jsonResponse = yamlToJson(yamlResponse)
201 JSONAssert.assertEquals(expectedJson, jsonResponse, JSONCompareMode.LENIENT)
205 private fun createMockServer(service: ServiceDefinition): WireMockServer {
206 val mockServer = WireMockServer(
209 .notifier(MarkedSlf4jNotifier(wireMockMarker))
211 service.expectations.forEach { expectation ->
213 val request = expectation.request
214 val response = expectation.response
215 // WebTestClient always use absolute path, prefixing with "/" if necessary
216 val urlPattern = urlEqualTo(request.path.prefixIfNot("/"))
217 val mappingBuilder: MappingBuilder = request(request.method, urlPattern)
218 request.headers.forEach { (key, value) ->
219 mappingBuilder.withHeader(key, equalTo(value))
221 if (request.body != null) {
222 mappingBuilder.withRequestBody(equalToJson(mapper.writeValueAsString(request.body), true, true))
225 val responseDefinitionBuilder: ResponseDefinitionBuilder = aResponse()
226 .withStatus(response.status)
227 if (response.body != null) {
228 responseDefinitionBuilder.withBody(mapper.writeValueAsBytes(response.body))
229 .withHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
232 mappingBuilder.willReturn(responseDefinitionBuilder)
234 mockServer.stubFor(mappingBuilder)
239 private fun setPropertiesForMockServer(service: ServiceDefinition, mockServer: WireMockServer) {
240 val selector = service.selector
241 val httpPort = mockServer.port()
242 val properties = mapOf(
243 "blueprintsprocessor.restclient.$selector.type" to "basic-auth",
244 "blueprintsprocessor.restclient.$selector.url" to "http://localhost:$httpPort/",
245 // TODO credentials should be validated
246 "blueprintsprocessor.restclient.$selector.username" to "admin",
247 "blueprintsprocessor.restclient.$selector.password" to "Kp8bJ4SXszM0WXlhak3eHlcse2gAw84vaoGGmJvUy2U"
249 setProperties(properties)
253 * Borrowed from com.github.tomakehurst.wiremock.junit.WireMockRule.checkForUnmatchedRequests
255 private fun WireMockServer.checkForUnmatchedRequests() {
256 val unmatchedRequests = findAllUnmatchedRequests()
257 if (unmatchedRequests.isNotEmpty()) {
258 val nearMisses = findNearMissesForAllUnmatchedRequests()
259 if (nearMisses.isEmpty()) {
260 throw VerificationException.forUnmatchedRequests(unmatchedRequests)
262 throw VerificationException.forUnmatchedNearMisses(nearMisses)
267 private fun yamlToJson(yaml: String): String {
268 val map: Map<String, Any> = Yaml().load(yaml)
269 return mapper.writeValueAsString(map)