Improve logging for ResourceAssingmentUtils
[ccsdk/cds.git] / ms / blueprintsprocessor / functions / resource-resolution / src / test / kotlin / org / onap / ccsdk / cds / blueprintsprocessor / functions / resource / resolution / utils / ResourceAssignmentUtilsTest.kt
1 /*-
2  * ============LICENSE_START=======================================================
3  *  Copyright (C) 2019 Nordix Foundation.
4  * Modifications Copyright (c) 2019 IBM, Bell Canada.
5  * ================================================================================
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *      http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  *
18  * SPDX-License-Identifier: Apache-2.0
19  * ============LICENSE_END=========================================================
20  */
21
22 package org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.utils
23
24 import com.fasterxml.jackson.databind.JsonNode
25 import com.fasterxml.jackson.databind.node.TextNode
26 import io.mockk.every
27 import io.mockk.spyk
28 import kotlinx.coroutines.runBlocking
29 import org.junit.Before
30 import org.junit.Test
31 import org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.ResourceAssignmentRuntimeService
32 import org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.ResourceResolutionConstants.METADATA_TRANSFORM_TEMPLATE
33 import org.onap.ccsdk.cds.controllerblueprints.core.asJsonPrimitive
34 import org.onap.ccsdk.cds.controllerblueprints.core.asJsonType
35 import org.onap.ccsdk.cds.controllerblueprints.core.data.DataType
36 import org.onap.ccsdk.cds.controllerblueprints.core.data.EntrySchema
37 import org.onap.ccsdk.cds.controllerblueprints.core.data.NodeTemplate
38 import org.onap.ccsdk.cds.controllerblueprints.core.data.PropertyDefinition
39 import org.onap.ccsdk.cds.controllerblueprints.core.utils.BluePrintMetadataUtils
40 import org.onap.ccsdk.cds.controllerblueprints.core.utils.JacksonUtils
41 import org.onap.ccsdk.cds.controllerblueprints.resource.dict.ResourceAssignment
42 import org.onap.ccsdk.cds.controllerblueprints.resource.dict.ResourceDefinition
43 import kotlin.test.assertEquals
44
45 data class IpAddress(val port: String, val ip: String)
46 data class Host(val name: String, val ipAddress: IpAddress)
47 data class ExpectedResponseIp(val ip: String)
48 data class ExpectedResponseIpAddress(val ipAddress: IpAddress)
49
50 class ResourceAssignmentUtilsTest {
51     private lateinit var resourceAssignmentRuntimeService: ResourceAssignmentRuntimeService
52     private lateinit var resourceAssignment: ResourceAssignment
53
54     private lateinit var inputMapToTestPrimitiveTypeWithValue: JsonNode
55     private lateinit var inputMapToTestPrimitiveTypeWithKeyValue: JsonNode
56     private lateinit var inputMapToTestCollectionOfPrimitiveType: JsonNode
57     private lateinit var inputMapToTestCollectionOfComplexTypeWithOneOutputKeyMapping: JsonNode
58     private lateinit var inputMapToTestCollectionOfComplexTypeWithAllOutputKeyMapping: JsonNode
59     private lateinit var inputMapToTestComplexTypeWithOneOutputKeyMapping: JsonNode
60     private lateinit var inputMapToTestComplexTypeWithAllOutputKeyMapping: JsonNode
61     private lateinit var expectedValueToTestPrimitiveType: JsonNode
62     private lateinit var expectedValueToTesCollectionOfPrimitiveType: JsonNode
63     private lateinit var expectedValueToTestCollectionOfComplexTypeWithOneOutputKeyMapping: JsonNode
64     private lateinit var expectedValueToTestComplexTypeWithOneOutputKeyMapping: JsonNode
65     private lateinit var expectedValueToTestComplexTypeWithAllOutputKeyMapping: JsonNode
66     private lateinit var expectedValueToTestCollectionOfComplexTypeWithAllOutputKeyMapping: JsonNode
67
68     @Before
69     fun setup() {
70
71         val bluePrintContext = runBlocking {
72             BluePrintMetadataUtils.getBluePrintContext(
73                 "./../../../../components/model-catalog/blueprint-model/test-blueprint/baseconfiguration"
74             )
75         }
76
77         resourceAssignmentRuntimeService = spyk(ResourceAssignmentRuntimeService("1234", bluePrintContext))
78
79         // Init input map and expected values for tests
80         initInputMapAndExpectedValuesForPrimitiveType()
81         initInputMapAndExpectedValuesForCollection()
82         initInputMapAndExpectedValuesForComplexType()
83
84         val propertiesDefinition1 = PropertyDefinition().apply {
85             type = "string"
86             id = "port"
87         }
88
89         val propertiesDefinition2 = PropertyDefinition().apply {
90             type = "string"
91             id = "ip"
92         }
93
94         val propertiesDefinition3 = PropertyDefinition().apply {
95             type = "string"
96             id = "name"
97         }
98
99         val propertiesDefinition4 = PropertyDefinition().apply {
100             type = "ip-address"
101             id = "ipAddress"
102         }
103
104         val mapOfPropertiesIpAddress = mutableMapOf<String, PropertyDefinition>()
105         mapOfPropertiesIpAddress["port"] = propertiesDefinition1
106         mapOfPropertiesIpAddress["ip"] = propertiesDefinition2
107
108         val mapOfPropertiesHost = mutableMapOf<String, PropertyDefinition>()
109         mapOfPropertiesHost["name"] = propertiesDefinition3
110         mapOfPropertiesHost["ipAddress"] = propertiesDefinition4
111
112         val myDataTypeIpaddress = DataType().apply {
113             id = "ip-address"
114             properties = mapOfPropertiesIpAddress
115         }
116
117         val myDataTypeHost = DataType().apply {
118             id = "host"
119             properties = mapOfPropertiesHost
120         }
121
122         every {
123             resourceAssignmentRuntimeService.bluePrintContext().dataTypeByName("ip-address")
124         } returns myDataTypeIpaddress
125
126         every { resourceAssignmentRuntimeService.bluePrintContext().dataTypeByName("host") } returns myDataTypeHost
127
128         every { resourceAssignmentRuntimeService.setNodeTemplateAttributeValue(any(), any(), any()) } returns Unit
129     }
130
131     @Test
132     fun `generateResourceDataForAssignments - positive test`() {
133         // given a valid resource assignment
134         val validResourceAssignment1 = createResourceAssignmentForTest("valid_value", "pnf-id")
135         val validResourceAssignment2 = createResourceAssignmentForTest("also_valid", "a1")
136
137         // and a list containing that resource assignment
138         val resourceAssignmentList = listOf<ResourceAssignment>(validResourceAssignment1, validResourceAssignment2)
139
140         // when the values of the resources are evaluated
141         val outcome = ResourceAssignmentUtils.generateResourceDataForAssignments(resourceAssignmentList)
142
143         // then the assignment should produce a valid result
144         val expected = """
145             {
146               "a1" : "also_valid",
147               "pnf-id" : "valid_value"
148             }
149         """.trimIndent()
150         assertEquals(expected, outcome.trimIndent(), "unexpected outcome generated")
151     }
152
153     @Test
154     fun `generateResourceDataForAssignments - resource without value is not resolved as null`() {
155         // given a valid resource assignment
156         val resourceAssignmentWithNullValue = createResourceAssignmentForTest(null)
157
158         // and a list containing that resource assignment
159         val resourceAssignmentList = listOf<ResourceAssignment>(resourceAssignmentWithNullValue)
160
161         // when the values of the resources are evaluated
162         val outcome = ResourceAssignmentUtils.generateResourceDataForAssignments(resourceAssignmentList)
163
164         // then the assignment should produce a valid result
165         val expected = "{\n" + "  \"pnf-id\" : \"\${pnf-id}\"\n" + "}"
166         assertEquals(expected, outcome.replace("\r\n", "\n"), "unexpected outcome generated")
167     }
168
169     @Test
170     fun generate() {
171         val resourceAssignment = createResourceAssignmentForTest(null)
172         val resourceDefinition = ResourceDefinition()
173         val nodeTemplate = NodeTemplate().apply {
174             properties = mutableMapOf("resolved-payload" to JacksonUtils.jsonNode("{\"mock\": true}"))
175         }
176         resourceDefinition.sources = mutableMapOf("input" to nodeTemplate)
177         resourceDefinition.property = PropertyDefinition().apply {
178             this.description = "pnf-id"
179             this.metadata = mutableMapOf("aai-path" to "//path/in/aai")
180         }
181
182         val result = ResourceAssignmentUtils.generateResolutionSummaryData(
183                 listOf(resourceAssignment), mapOf("pnf-id" to resourceDefinition))
184
185         assertEquals("""
186             {
187                 "resolution-summary":[
188                     {
189                         "name":"pnf-id",
190                         "value":"",
191                         "required":false,
192                         "type":"string",
193                         "key-identifiers":[],
194                         "dictionary-description":"pnf-id",
195                         "dictionary-metadata":[
196                             {"name":"aai-path","value":"//path/in/aai"}
197                         ],
198                         "dictionary-name":"pnf-id",
199                         "dictionary-source":"input",
200                         "request-payload":{"mock":true},
201                         "status":"",
202                         "message":""
203                     }
204                 ]
205             }
206         """.replace("\n|\\s".toRegex(), ""), result)
207     }
208
209     private fun createResourceAssignmentForTest(resourceValue: String?, resourceName: String = "pnf-id"): ResourceAssignment {
210         val valueForTest = if (resourceValue == null) null else TextNode(resourceValue)
211         val resourceAssignmentForTest = ResourceAssignment().apply {
212             name = resourceName
213             dictionaryName = "pnf-id"
214             dictionarySource = "input"
215             property = PropertyDefinition().apply {
216                 type = "string"
217                 value = valueForTest
218             }
219         }
220         return resourceAssignmentForTest
221     }
222
223     @Test
224     fun parseResponseNodeTestForPrimitivesTypes() {
225         var outcome = prepareResponseNodeForTest(
226             "sample-value", "string", "",
227             inputMapToTestPrimitiveTypeWithValue
228         )
229         assertEquals(
230             expectedValueToTestPrimitiveType,
231             outcome,
232             "Unexpected outcome returned for primitive type of simple String"
233         )
234         assertEquals(0, resourceAssignment.keyIdentifiers.size)
235
236         outcome = prepareResponseNodeForTest(
237             "sample-key-value", "string", "",
238             inputMapToTestPrimitiveTypeWithKeyValue
239         )
240         assertEquals(
241             expectedValueToTestPrimitiveType,
242             outcome,
243             "Unexpected outcome returned for primitive type of key-value String"
244         )
245         assertEquals(
246                 expectedValueToTestPrimitiveType,
247                 resourceAssignment.keyIdentifiers[0].value
248         )
249     }
250
251     @Test
252     fun parseResponseNodeTestForCollectionsOfString() {
253         var outcome = prepareResponseNodeForTest(
254             "listOfString", "list",
255             "string", inputMapToTestCollectionOfPrimitiveType
256         )
257         assertEquals(
258             expectedValueToTesCollectionOfPrimitiveType,
259             outcome,
260             "unexpected outcome returned for list of String"
261         )
262
263         val expectedKeyIdentifierValue = JacksonUtils.getJsonNode(outcome.map { it["ip"] })
264         assertEquals(
265                 expectedKeyIdentifierValue,
266                 resourceAssignment.keyIdentifiers[0].value
267         )
268
269         // FIXME("Map is not collection type, It is known complex type")
270         // outcome = prepareResponseNodeForTest(
271         //     "mapOfString", "map", "string",
272         //     inputMapToTestCollectionOfPrimitiveType
273         // )
274         // assertEquals(
275         //     expectedValueToTesCollectionOfPrimitiveType,
276         //     outcome,
277         //     "unexpected outcome returned for map of String"
278         // )
279     }
280
281     @Test
282     fun parseResponseNodeTestForCollectionsOfComplexType() {
283         var outcome = prepareResponseNodeForTest(
284             "listOfMyDataTypeWithOneOutputKeyMapping", "list",
285             "ip-address", inputMapToTestCollectionOfComplexTypeWithOneOutputKeyMapping
286         )
287         assertEquals(
288             expectedValueToTestCollectionOfComplexTypeWithOneOutputKeyMapping,
289             outcome,
290             "unexpected outcome returned for list of String"
291         )
292
293         outcome = prepareResponseNodeForTest(
294             "listOfMyDataTypeWithAllOutputKeyMapping", "list",
295             "ip-address", inputMapToTestCollectionOfComplexTypeWithAllOutputKeyMapping
296         )
297         assertEquals(
298             expectedValueToTestCollectionOfComplexTypeWithAllOutputKeyMapping,
299             outcome,
300             "unexpected outcome returned for list of String"
301         )
302     }
303
304     @Test
305     fun `parseResponseNodeTestForComplexType find one output key mapping`() {
306         val outcome = prepareResponseNodeForTest(
307             "complexTypeOneKeys", "host",
308             "", inputMapToTestComplexTypeWithOneOutputKeyMapping
309         )
310         assertEquals(
311             expectedValueToTestComplexTypeWithOneOutputKeyMapping,
312             outcome,
313             "Unexpected outcome returned for complex type"
314         )
315         assertEquals(
316                 expectedValueToTestComplexTypeWithOneOutputKeyMapping["host"],
317                 resourceAssignment.keyIdentifiers[0].value)
318     }
319
320     @Test
321     fun `parseResponseNodeTestForComplexType find all output key mapping`() {
322         val outcome = prepareResponseNodeForTest(
323             "complexTypeAllKeys", "host",
324             "", inputMapToTestComplexTypeWithAllOutputKeyMapping
325         )
326         assertEquals(
327             expectedValueToTestComplexTypeWithAllOutputKeyMapping,
328             outcome,
329             "Unexpected outcome returned for complex type"
330         )
331         assertEquals(2, resourceAssignment.keyIdentifiers.size)
332         assertEquals(
333                 expectedValueToTestComplexTypeWithAllOutputKeyMapping["name"],
334                 resourceAssignment.keyIdentifiers[0].value
335         )
336
337         assertEquals(
338                 expectedValueToTestComplexTypeWithAllOutputKeyMapping["ipAddress"],
339                 resourceAssignment.keyIdentifiers[1].value
340         )
341     }
342
343     @Test
344     fun `transform resolved value with inline template`() {
345         resourceAssignmentRuntimeService.putResolutionStore("vnf_name", "abc-vnf".asJsonType())
346         resourceAssignment = ResourceAssignment()
347         resourceAssignment.name = "int_pktgen_private_net_id"
348         resourceAssignment.property = PropertyDefinition()
349         resourceAssignment.property!!.type = "string"
350         val value = "".asJsonType()
351
352         // Enable transform template
353         resourceAssignment.property!!.metadata =
354                 mutableMapOf(METADATA_TRANSFORM_TEMPLATE to "\${vnf_name}_private2")
355
356         ResourceAssignmentUtils
357                 .setResourceDataValue(resourceAssignment, resourceAssignmentRuntimeService, value)
358
359         assertEquals("abc-vnf_private2",
360                 resourceAssignment.property!!.value!!.asText())
361     }
362
363     private fun initInputMapAndExpectedValuesForPrimitiveType() {
364         inputMapToTestPrimitiveTypeWithValue = "1.2.3.1".asJsonType()
365         val keyValue = mutableMapOf<String, String>()
366         keyValue["value"] = "1.2.3.1"
367         inputMapToTestPrimitiveTypeWithKeyValue = keyValue.asJsonType()
368         expectedValueToTestPrimitiveType = TextNode("1.2.3.1")
369     }
370
371     private fun initInputMapAndExpectedValuesForCollection() {
372         val listOfIps = arrayListOf("1.2.3.1", "1.2.3.2", "1.2.3.3")
373         val arrayNodeForList1 = JacksonUtils.objectMapper.createArrayNode()
374         listOfIps.forEach {
375             val arrayChildNode = JacksonUtils.objectMapper.createObjectNode()
376             arrayChildNode.set<JsonNode>("value", it.asJsonPrimitive())
377             arrayNodeForList1.add(arrayChildNode)
378         }
379         inputMapToTestCollectionOfPrimitiveType = arrayNodeForList1
380
381         expectedValueToTesCollectionOfPrimitiveType = arrayListOf(
382             ExpectedResponseIp("1.2.3.1"),
383             ExpectedResponseIp("1.2.3.2"), ExpectedResponseIp("1.2.3.3")
384         ).asJsonType()
385
386         val listOfIpAddresses = arrayListOf(
387             IpAddress("1111", "1.2.3.1").asJsonType(),
388             IpAddress("2222", "1.2.3.2").asJsonType(), IpAddress("3333", "1.2.3.3").asJsonType()
389         )
390         val arrayNodeForList2 = JacksonUtils.objectMapper.createArrayNode()
391         listOfIpAddresses.forEach {
392             val arrayChildNode = JacksonUtils.objectMapper.createObjectNode()
393             arrayChildNode.set<JsonNode>("value", it.asJsonType())
394             arrayNodeForList2.add(arrayChildNode)
395         }
396         inputMapToTestCollectionOfComplexTypeWithOneOutputKeyMapping = arrayNodeForList2
397
398         val arrayNodeForList3 = JacksonUtils.objectMapper.createArrayNode()
399         var childNode = JacksonUtils.objectMapper.createObjectNode()
400         childNode.set<JsonNode>("port", "1111".asJsonPrimitive())
401         childNode.set<JsonNode>("ip", "1.2.3.1".asJsonPrimitive())
402         arrayNodeForList3.add(childNode)
403         childNode = JacksonUtils.objectMapper.createObjectNode()
404         childNode.set<JsonNode>("port", "2222".asJsonPrimitive())
405         childNode.set<JsonNode>("ip", "1.2.3.2".asJsonPrimitive())
406         arrayNodeForList3.add(childNode)
407         childNode = JacksonUtils.objectMapper.createObjectNode()
408         childNode.set<JsonNode>("port", "3333".asJsonPrimitive())
409         childNode.set<JsonNode>("ip", "1.2.3.3".asJsonPrimitive())
410         arrayNodeForList3.add(childNode)
411         inputMapToTestCollectionOfComplexTypeWithAllOutputKeyMapping = arrayNodeForList3
412
413         expectedValueToTestCollectionOfComplexTypeWithOneOutputKeyMapping = arrayListOf(
414             ExpectedResponseIpAddress(IpAddress("1111", "1.2.3.1")),
415             ExpectedResponseIpAddress(IpAddress("2222", "1.2.3.2")), ExpectedResponseIpAddress(
416                 IpAddress("3333", "1.2.3.3")
417             )
418         ).asJsonType()
419         expectedValueToTestCollectionOfComplexTypeWithAllOutputKeyMapping = arrayListOf(
420             IpAddress("1111", "1.2.3.1"),
421             IpAddress("2222", "1.2.3.2"),
422             IpAddress("3333", "1.2.3.3")
423         ).asJsonType()
424     }
425
426     private fun initInputMapAndExpectedValuesForComplexType() {
427         val mapOfComplexType = mutableMapOf<String, JsonNode>()
428         mapOfComplexType["value"] = Host("my-ipAddress", IpAddress("1111", "1.2.3.1")).asJsonType()
429         mapOfComplexType["port"] = "8888".asJsonType()
430         mapOfComplexType["something"] = "1.2.3.2".asJsonType()
431         inputMapToTestComplexTypeWithOneOutputKeyMapping = mapOfComplexType.asJsonType()
432
433         val objectNode = JacksonUtils.objectMapper.createObjectNode()
434         expectedValueToTestComplexTypeWithOneOutputKeyMapping =
435             objectNode.set("host", Host("my-ipAddress", IpAddress("1111", "1.2.3.1")).asJsonType())
436
437         val childNode1 = JacksonUtils.objectMapper.createObjectNode()
438         childNode1.set<JsonNode>("name", "my-ipAddress".asJsonPrimitive())
439         childNode1.set<JsonNode>("ipAddress", IpAddress("1111", "1.2.3.1").asJsonType())
440         childNode1.set<JsonNode>("port", "8888".asJsonType())
441         childNode1.set<JsonNode>("something", IpAddress("2222", "1.2.3.1").asJsonType())
442         inputMapToTestComplexTypeWithAllOutputKeyMapping = childNode1
443
444         val childNode2 = JacksonUtils.objectMapper.createObjectNode()
445         childNode2.set<JsonNode>("name", "my-ipAddress".asJsonPrimitive())
446         childNode2.set<JsonNode>("ipAddress", IpAddress("1111", "1.2.3.1").asJsonType())
447         expectedValueToTestComplexTypeWithAllOutputKeyMapping = childNode2
448     }
449
450     private fun prepareResponseNodeForTest(
451         dictionary_source: String,
452         sourceType: String,
453         entrySchema: String,
454         response: Any
455     ): JsonNode {
456
457         resourceAssignment = when (sourceType) {
458             "list" -> {
459                 prepareRADataDictionaryCollection(dictionary_source, sourceType, entrySchema)
460             }
461             "string" -> {
462                 prepareRADataDictionaryOfPrimaryType(dictionary_source)
463             }
464             else -> {
465                 prepareRADataDictionaryComplexType(dictionary_source, sourceType, entrySchema)
466             }
467         }
468
469         val responseNode = checkNotNull(JacksonUtils.getJsonNode(response)) {
470             "Failed to get database query result into Json node."
471         }
472
473         val outputKeyMapping = prepareOutputKeyMapping(dictionary_source)
474
475         return ResourceAssignmentUtils.parseResponseNode(
476             responseNode,
477             resourceAssignment,
478             resourceAssignmentRuntimeService,
479             outputKeyMapping
480         )
481     }
482
483     private fun prepareRADataDictionaryOfPrimaryType(dictionary_source: String): ResourceAssignment {
484         return ResourceAssignment().apply {
485             name = "ipAddress"
486             dictionaryName = "sample-ip"
487             dictionarySource = "$dictionary_source"
488             property = PropertyDefinition().apply {
489                 type = "string"
490             }
491         }
492     }
493
494     private fun prepareRADataDictionaryCollection(
495         dictionary_source: String,
496         sourceType: String,
497         schema: String
498     ): ResourceAssignment {
499         return ResourceAssignment().apply {
500             name = "ipAddress-list"
501             dictionaryName = "sample-licenses"
502             dictionarySource = "$dictionary_source"
503             property = PropertyDefinition().apply {
504                 type = "$sourceType"
505                 entrySchema = EntrySchema().apply {
506                     type = "$schema"
507                 }
508             }
509         }
510     }
511
512     private fun prepareRADataDictionaryComplexType(
513         dictionary_source: String,
514         sourceType: String,
515         schema: String
516     ): ResourceAssignment {
517         return ResourceAssignment().apply {
518             name = "ipAddress-complexType"
519             dictionaryName = "sample-licenses"
520             dictionarySource = "$dictionary_source"
521             property = PropertyDefinition().apply {
522                 type = "$sourceType"
523             }
524         }
525     }
526
527     private fun prepareOutputKeyMapping(dictionary_source: String): MutableMap<String, String> {
528         val outputMapping = mutableMapOf<String, String>()
529
530         when (dictionary_source) {
531             "sample-key-value", "sample-value" -> {
532                 // Primary Type
533                 if (dictionary_source == "sample-key-value")
534                     outputMapping["sample-ip"] = "value"
535             }
536             "listOfString", "mapOfString" -> {
537                 // List of string
538                 outputMapping["ip"] = "value"
539             }
540             "listOfMyDataTypeWithOneOutputKeyMapping", "listOfMyDataTypeWithAllOutputKeyMapping" -> {
541                 // List or map of complex Type
542                 if (dictionary_source == "listOfMyDataTypeWithOneOutputKeyMapping")
543                     outputMapping["ipAddress"] = "value"
544                 else {
545                     outputMapping["port"] = "port"
546                     outputMapping["ip"] = "ip"
547                 }
548             }
549             else -> {
550                 // Complex Type
551                 if (dictionary_source == "complexTypeOneKeys")
552                     outputMapping["host"] = "value"
553                 else {
554                     outputMapping["name"] = "name"
555                     outputMapping["ipAddress"] = "ipAddress"
556                 }
557             }
558         }
559         return outputMapping
560     }
561 }