2 * ============LICENSE_START=======================================================
3 * Copyright (C) 2021 Nordix Foundation
4 * Modifications Copyright (C) 2021 Pantheon.tech
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
10 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 * SPDX-License-Identifier: Apache-2.0
19 * ============LICENSE_END=========================================================
22 package org.onap.cps.ncmp.api.impl
24 import com.fasterxml.jackson.core.JsonProcessingException
25 import com.fasterxml.jackson.databind.ObjectMapper
26 import org.onap.cps.api.CpsDataService
27 import org.onap.cps.api.CpsQueryService
28 import org.onap.cps.ncmp.api.impl.exception.NcmpException
29 import org.onap.cps.ncmp.api.impl.operation.DmiOperations
30 import org.onap.cps.ncmp.api.models.CmHandle
31 import org.onap.cps.ncmp.api.models.DmiPluginRegistration
32 import org.onap.cps.spi.FetchDescendantsOption
33 import org.onap.cps.spi.model.DataNode
34 import org.springframework.http.HttpStatus
35 import org.springframework.http.ResponseEntity
36 import spock.lang.Shared
37 import spock.lang.Specification
39 class NetworkCmProxyDataServiceImplSpec extends Specification {
42 def persistenceCmHandle = new CmHandle()
44 def cmHandlesArray = ['cmHandle001']
46 def mockCpsDataService = Mock(CpsDataService)
47 def mockCpsQueryService = Mock(CpsQueryService)
48 def mockDmiOperations = Mock(DmiOperations)
49 def objectUnderTest = new NetworkCmProxyDataServiceImpl(mockDmiOperations, mockCpsDataService, mockCpsQueryService, new ObjectMapper())
51 def cmHandle = 'some handle'
53 def expectedDataspaceName = 'NFP-Operational'
54 def 'Query data nodes by cps path with #fetchDescendantsOption.'() {
55 given: 'a cm Handle and a cps path'
56 def cpsPath = '/cps-path'
57 when: 'queryDataNodes is invoked'
58 objectUnderTest.queryDataNodes(cmHandle, cpsPath, fetchDescendantsOption)
59 then: 'the persistence service is called once with the correct parameters'
60 1 * mockCpsQueryService.queryDataNodes(expectedDataspaceName, cmHandle, cpsPath, fetchDescendantsOption)
61 where: 'all fetch descendants options are supported'
62 fetchDescendantsOption << FetchDescendantsOption.values()
64 def 'Create full data node: #scenario.'() {
65 given: 'a cm handle and root xpath'
66 def jsonData = 'some json'
67 when: 'createDataNode is invoked'
68 objectUnderTest.createDataNode(cmHandle, xpath, jsonData)
69 then: 'the CPS service method is invoked once with the expected parameters'
70 1 * mockCpsDataService.saveData(expectedDataspaceName, cmHandle, jsonData)
71 where: 'following parameters were used'
74 'root level xpath' | '/'
76 def 'Create child data node.'() {
77 given: 'a cm handle and parent node xpath'
78 def jsonData = 'some json'
79 def xpath = '/test-node'
80 when: 'createDataNode is invoked'
81 objectUnderTest.createDataNode(cmHandle, xpath, jsonData)
82 then: 'the CPS service method is invoked once with the expected parameters'
83 1 * mockCpsDataService.saveData(expectedDataspaceName, cmHandle, xpath, jsonData)
85 def 'Add list-node elements.'() {
86 given: 'a cm handle and parent node xpath'
87 def jsonData = 'some json'
88 def xpath = '/test-node'
89 when: 'addListNodeElements is invoked'
90 objectUnderTest.addListNodeElements(cmHandle, xpath, jsonData)
91 then: 'the CPS service method is invoked once with the expected parameters'
92 1 * mockCpsDataService.saveListNodeData(expectedDataspaceName, cmHandle, xpath, jsonData)
94 def 'Update data node leaves.'() {
95 given: 'a cm Handle and a cps path'
97 def jsonData = 'some json'
98 when: 'updateNodeLeaves is invoked'
99 objectUnderTest.updateNodeLeaves(cmHandle, xpath, jsonData)
100 then: 'the persistence service is called once with the correct parameters'
101 1 * mockCpsDataService.updateNodeLeaves(expectedDataspaceName, cmHandle, xpath, jsonData)
103 def 'Replace data node tree.'() {
104 given: 'a cm Handle and a cps path'
106 def jsonData = 'some json'
107 when: 'replaceNodeTree is invoked'
108 objectUnderTest.replaceNodeTree(cmHandle, xpath, jsonData)
109 then: 'the persistence service is called once with the correct parameters'
110 1 * mockCpsDataService.replaceNodeTree(expectedDataspaceName, cmHandle, xpath, jsonData)
113 def 'Register or re-register a DMI Plugin with #scenario cm handles.'() {
114 given: 'a registration '
115 def dmiPluginRegistration = new DmiPluginRegistration()
116 dmiPluginRegistration.dmiPlugin = 'my-server'
117 persistenceCmHandle.cmHandleID = '123'
118 persistenceCmHandle.cmHandleProperties = [name1: 'value1', name2: 'value2']
119 dmiPluginRegistration.createdCmHandles = createdCmHandles
120 dmiPluginRegistration.updatedCmHandles = updatedCmHandles
121 dmiPluginRegistration.removedCmHandles = removedCmHandles
122 def expectedJsonData = '{"cm-handles":[{"id":"123","dmi-service-name":"my-server","additional-properties":[{"name":"name1","value":"value1"},{"name":"name2","value":"value2"}]}]}'
123 when: 'registration is updated'
124 objectUnderTest.updateDmiPluginRegistration(dmiPluginRegistration)
125 then: 'the CPS save list node data is invoked with the expected parameters'
126 expectedCallsToSaveNode * mockCpsDataService.saveListNodeData('NCMP-Admin', 'ncmp-dmi-registry', '/dmi-registry', expectedJsonData)
127 and: 'update Node and Child Data Nodes is invoked with correct parameters'
128 expectedCallsToUpdateNode * mockCpsDataService.updateNodeLeavesAndExistingDescendantLeaves('NCMP-Admin', 'ncmp-dmi-registry', '/dmi-registry', expectedJsonData)
129 and : 'delete list data node is invoked with the correct parameters'
130 expectedCallsToDeleteListDataNode * mockCpsDataService.deleteListNodeData('NCMP-Admin', 'ncmp-dmi-registry', "/dmi-registry/cm-handles[@id='cmHandle001']")
132 scenario | createdCmHandles | updatedCmHandles | removedCmHandles || expectedCallsToSaveNode | expectedCallsToUpdateNode | expectedCallsToDeleteListDataNode
133 'create' | [persistenceCmHandle ] | [] | [] || 1 | 0 | 0
134 'update' | [] | [persistenceCmHandle ] | [] || 0 | 1 | 0
135 'delete' | [] | [] | cmHandlesArray || 0 | 0 | 1
136 'create, update and delete' | [persistenceCmHandle ] | [persistenceCmHandle ] | cmHandlesArray || 1 | 1 | 1
140 def 'Register a DMI Plugin for the given cmHandle without additional properties.'() {
141 given: 'a registration without cmHandle properties '
142 def dmiPluginRegistration = new DmiPluginRegistration()
143 dmiPluginRegistration.dmiPlugin = 'my-server'
144 persistenceCmHandle.cmHandleID = '123'
145 persistenceCmHandle.cmHandleProperties = null
146 dmiPluginRegistration.createdCmHandles = [persistenceCmHandle ]
147 def expectedJsonData = '{"cm-handles":[{"id":"123","dmi-service-name":"my-server","additional-properties":[]}]}'
148 when: 'registration is updated'
149 objectUnderTest.updateDmiPluginRegistration(dmiPluginRegistration)
150 then: 'the CPS save list node data is invoked with the expected parameters'
151 1 * mockCpsDataService.saveListNodeData('NCMP-Admin', 'ncmp-dmi-registry', '/dmi-registry', expectedJsonData)
154 def 'Get resource data for pass-through operational from dmi.'() {
156 def xpath = "/dmi-registry/cm-handles[@id='testCmHandle']"
158 def dataNode = new DataNode()
159 dataNode.leaves = ['dmi-service-name':'testDmiService']
160 def childDataNode = new DataNode()
161 childDataNode.leaves = ['name':'testName','value':'testValue']
162 dataNode.childDataNodes = [childDataNode]
163 when: 'get resource data is called'
164 def response = objectUnderTest.getResourceDataOperationalForCmHandle('testCmHandle',
169 then: 'cps data service is being called once to get data node'
170 1 * mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
171 xpath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> dataNode
172 and: 'dmi operation is being called to get resource data'
173 1 * mockDmiOperations.getResouceDataOperationalFromDmi('testDmiService',
179 '{"operation":"read","cmHandleProperties":{"testName":"testValue"}}') >> new ResponseEntity<>('result-json', HttpStatus.OK)
180 and: 'dmi returns ok response'
181 response == 'result-json'
183 def 'Get resource data for pass-through operational from dmi threw parsing exception.'() {
185 def xpath = "/dmi-registry/cm-handles[@id='testCmHandle']"
187 def dataNode = new DataNode()
188 dataNode.leaves = ['dmi-service-name':'testDmiService']
189 def childDataNode = new DataNode()
190 childDataNode.leaves = ['name':'testName','value':'testValue']
191 dataNode.childDataNodes = [childDataNode]
192 mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
193 xpath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> dataNode
194 and: 'objectMapper not able to parse object'
195 def mockObjectMapper = Mock(ObjectMapper)
196 objectUnderTest.objectMapper = mockObjectMapper
197 mockObjectMapper.writeValueAsString(_) >> { throw new JsonProcessingException("testException") }
198 when: 'get resource data is called'
199 def response = objectUnderTest.getResourceDataOperationalForCmHandle('testCmHandle',
204 then: 'exception is thrown'
205 thrown(NcmpException.class)
207 def 'Get resource data for pass-through operational from dmi return NOK response.'() {
209 def xpath = "/dmi-registry/cm-handles[@id='testCmHandle']"
211 def dataNode = new DataNode()
212 dataNode.leaves = ['dmi-service-name':'testDmiService']
213 def childDataNode = new DataNode()
214 childDataNode.leaves = ['name':'testName','value':'testValue']
215 dataNode.childDataNodes = [childDataNode]
216 mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
217 xpath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> dataNode
218 and: 'dmi returns NOK response'
219 mockDmiOperations.getResouceDataOperationalFromDmi('testDmiService',
225 '{"operation":"read","cmHandleProperties":{"testName":"testValue"}}')
226 >> new ResponseEntity<>('NOK-json', HttpStatus.NOT_FOUND)
227 when: 'get resource data is called'
228 def response = objectUnderTest.getResourceDataOperationalForCmHandle('testCmHandle',
233 then: 'exception is thrown'
234 thrown(NcmpException.class)
236 def 'Get resource data for pass-through running from dmi.'() {
238 def xpath = "/dmi-registry/cm-handles[@id='testCmHandle']"
239 and: 'data node representing cmhandle and its properties'
240 def dataNode = new DataNode()
241 dataNode.leaves = ['dmi-service-name':'testDmiService']
242 def childDataNode = new DataNode()
243 childDataNode.leaves = ['name':'testName','value':'testValue']
244 dataNode.childDataNodes = [childDataNode]
245 and: 'cpsDataService returns valid dataNode'
246 mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
247 xpath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> dataNode
248 and: 'dmi returns valid response and data'
249 mockDmiOperations.getResouceDataPassThroughRunningFromDmi('testDmiService',
255 '{"operation":"read","cmHandleProperties":{"testName":"testValue"}}') >> new ResponseEntity<>('{result-json}', HttpStatus.OK)
256 when: 'get resource data is called'
257 def response = objectUnderTest.getResourceDataPassThroughRunningForCmHandle('testCmHandle',
262 then: 'get resource data returns expected response'
263 response == '{result-json}'
265 def 'Get resource data for pass-through running from dmi threw parsing exception.'() {
267 def xpath = "/dmi-registry/cm-handles[@id='testCmHandle']"
268 and: 'data node representing cmhandle and its properties'
269 def dataNode = new DataNode()
270 dataNode.leaves = ['dmi-service-name':'testDmiService']
271 def childDataNode = new DataNode()
272 childDataNode.leaves = ['name':'testName','value':'testValue']
273 dataNode.childDataNodes = [childDataNode]
274 and: 'cpsDataService returns valid dataNode'
275 mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
276 xpath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> dataNode
277 and: 'objectMapper not able to parse object'
278 def mockObjectMapper = Mock(ObjectMapper)
279 objectUnderTest.objectMapper = mockObjectMapper
280 mockObjectMapper.writeValueAsString(_) >> { throw new JsonProcessingException("testException") }
281 when: 'get resource data is called'
282 def response = objectUnderTest.getResourceDataPassThroughRunningForCmHandle('testCmHandle',
287 then: 'exception is thrown'
288 thrown(NcmpException.class)
290 def 'Get resource data for pass-through running from dmi return NOK response.'() {
292 def xpath = "/dmi-registry/cm-handles[@id='testCmHandle']"
293 and: 'data node representing cmhandle and its properties'
294 def dataNode = new DataNode()
295 dataNode.leaves = ['dmi-service-name':'testDmiService']
296 def childDataNode = new DataNode()
297 childDataNode.leaves = ['name':'testName','value':'testValue']
298 dataNode.childDataNodes = [childDataNode]
299 and: 'cpsDataService returns valid dataNode'
300 mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
301 xpath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> dataNode
302 and: 'dmi returns NOK response'
303 mockDmiOperations.getResouceDataPassThroughRunningFromDmi('testDmiService',
309 '{"operation":"read","cmHandleProperties":{"testName":"testValue"}}')
310 >> new ResponseEntity<>('NOK-json', HttpStatus.NOT_FOUND)
311 when: 'get resource data is called'
312 def response = objectUnderTest.getResourceDataPassThroughRunningForCmHandle('testCmHandle',
317 then: 'exception is thrown'
318 thrown(NcmpException.class)