Formatting Code base with ktlint
[ccsdk/cds.git] / ms / blueprintsprocessor / application / src / test / kotlin / org / onap / ccsdk / cds / blueprintsprocessor / uat / UatServicesTest.kt
1 /*-
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
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
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.
16  *
17  * SPDX-License-Identifier: Apache-2.0
18  * ============LICENSE_END=========================================================
19  */
20 package org.onap.ccsdk.cds.blueprintsprocessor.uat
21
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
65
66 @ActiveProfiles("uat")
67 @Suppress("MemberVisibilityCanBePrivate")
68 class UatServicesTest : BaseUatTest() {
69
70     companion object {
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)
75     }
76
77     @Autowired
78     lateinit var mapper: ObjectMapper
79
80     @Autowired
81     lateinit var environment: ConfigurableEnvironment
82
83     private val ephemeralProperties = mutableSetOf<String>()
84     private val startedMockServers = mutableListOf<WireMockServer>()
85
86     private fun setProperties(properties: Map<String, String>) {
87         inlinedPropertySource().putAll(properties)
88         ephemeralProperties += properties.keys
89     }
90
91     @AfterTest
92     fun resetProperties() {
93         val source = inlinedPropertySource()
94         ephemeralProperties.forEach { key -> source.remove(key) }
95         ephemeralProperties.clear()
96     }
97
98     @AfterTest
99     fun stopMockServers() {
100         startedMockServers.forEach { mockServer ->
101             try {
102                 mockServer.checkForUnmatchedRequests()
103             } finally {
104                 mockServer.stop()
105             }
106         }
107         startedMockServers.clear()
108     }
109
110     private fun inlinedPropertySource(): MutableMap<String, Any> =
111         (environment.propertySources[INLINED_PROPERTIES_PROPERTY_SOURCE_NAME] as MapPropertySource).source
112
113     @LocalServerPort
114     var localServerPort: Int = 0
115
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"
119     }
120
121     lateinit var httpClient: CloseableHttpClient
122
123     @BeforeTest
124     fun setupHttpClient() {
125         val defaultHeaders = listOf(
126             BasicHeader(
127                 org.apache.http.HttpHeaders.AUTHORIZATION,
128                 TestSecuritySettings.clientAuthToken()
129             )
130         )
131         httpClient = HttpClientBuilder.create()
132             .setDefaultHeaders(defaultHeaders)
133             .build()
134     }
135
136     @Test
137     fun `verify service validates candidate UAT`() {
138         // GIVEN
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")
143             .build()
144         val request = HttpPost("$baseUrl/api/v1/uat/verify").apply {
145             entity = multipartEntity
146         }
147
148         // WHEN
149         httpClient.execute(request) { response ->
150
151             // THEN
152             val statusLine = response.statusLine
153             assertThat(statusLine.statusCode, CoreMatchers.equalTo(HttpStatus.SC_OK))
154         }
155     }
156
157     @Test
158     fun `spy service generates complete UAT from bare UAT`() {
159         // GIVEN
160         val uatSpec = UAT_PATH.toFile().readText()
161         val fullUat = UatDefinition.load(mapper, uatSpec)
162         val expectedJson = mapper.writeValueAsString(fullUat)
163
164         val bareUatBytes = fullUat.toBare().dump(mapper).toByteArray()
165
166         fullUat.externalServices.forEach { service ->
167             val mockServer = createMockServer(service)
168             mockServer.start()
169             startedMockServers += mockServer
170             setPropertiesForMockServer(service, mockServer)
171         }
172
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")
178             .build()
179         val request = HttpPost("$baseUrl/api/v1/uat/spy").apply {
180             entity = multipartEntity
181         }
182
183         // WHEN
184         httpClient.execute(request) { response ->
185
186             // THEN
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)
196         }
197     }
198
199     private fun createMockServer(service: ServiceDefinition): WireMockServer {
200         val mockServer = WireMockServer(
201             wireMockConfig()
202                 .dynamicPort()
203                 .notifier(MarkedSlf4jNotifier(wireMockMarker))
204         )
205         service.expectations.forEach { expectation ->
206
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))
214             }
215             if (request.body != null) {
216                 mappingBuilder.withRequestBody(equalToJson(mapper.writeValueAsString(request.body), true, true))
217             }
218
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)
224             }
225
226             mappingBuilder.willReturn(responseDefinitionBuilder)
227
228             mockServer.stubFor(mappingBuilder)
229         }
230         return mockServer
231     }
232
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"
242         )
243         setProperties(properties)
244     }
245
246     /**
247      * Borrowed from com.github.tomakehurst.wiremock.junit.WireMockRule.checkForUnmatchedRequests
248      */
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)
255             } else {
256                 throw VerificationException.forUnmatchedNearMisses(nearMisses)
257             }
258         }
259     }
260
261     private fun yamlToJson(yaml: String): String {
262         val map: Map<String, Any> = Yaml().load(yaml)
263         return mapper.writeValueAsString(map)
264     }
265 }