2 * ============LICENSE_START=======================================================
3 * Copyright (C) 2021 Nordix Foundation
4 * Modifications Copyright (C) 2021 Pantheon.tech
5 * Modifications Copyright (C) 2021 Bell Canada
6 * ================================================================================
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
19 * SPDX-License-Identifier: Apache-2.0
20 * ============LICENSE_END=========================================================
23 package org.onap.cps.ncmp.api.impl
25 import com.fasterxml.jackson.core.JsonProcessingException
26 import com.fasterxml.jackson.databind.ObjectMapper
27 import org.onap.cps.api.CpsDataService
28 import org.onap.cps.api.CpsQueryService
29 import org.onap.cps.ncmp.api.impl.exception.NcmpException
30 import org.onap.cps.ncmp.api.impl.operation.DmiOperations
31 import org.onap.cps.ncmp.api.models.CmHandle
32 import org.onap.cps.ncmp.api.models.DmiPluginRegistration
33 import org.onap.cps.spi.FetchDescendantsOption
34 import org.onap.cps.spi.model.DataNode
35 import org.springframework.http.HttpStatus
36 import org.springframework.http.ResponseEntity
37 import spock.lang.Shared
38 import spock.lang.Specification
40 class NetworkCmProxyDataServiceImplSpec extends Specification {
43 def persistenceCmHandle = new CmHandle()
45 def cmHandlesArray = ['cmHandle001']
47 def mockCpsDataService = Mock(CpsDataService)
48 def mockCpsQueryService = Mock(CpsQueryService)
49 def mockDmiOperations = Mock(DmiOperations)
50 def objectUnderTest = new NetworkCmProxyDataServiceImpl(mockDmiOperations, mockCpsDataService, mockCpsQueryService, new ObjectMapper())
52 def cmHandle = 'some handle'
53 def noTimestamp = null
55 def expectedDataspaceName = 'NFP-Operational'
56 def 'Query data nodes by cps path with #fetchDescendantsOption.'() {
57 given: 'a cm Handle and a cps path'
58 def cpsPath = '/cps-path'
59 when: 'queryDataNodes is invoked'
60 objectUnderTest.queryDataNodes(cmHandle, cpsPath, fetchDescendantsOption)
61 then: 'the persistence service is called once with the correct parameters'
62 1 * mockCpsQueryService.queryDataNodes(expectedDataspaceName, cmHandle, cpsPath, fetchDescendantsOption)
63 where: 'all fetch descendants options are supported'
64 fetchDescendantsOption << FetchDescendantsOption.values()
66 def 'Create full data node: #scenario.'() {
67 given: 'a cm handle and root xpath'
68 def jsonData = 'some json'
69 when: 'createDataNode is invoked'
70 objectUnderTest.createDataNode(cmHandle, xpath, jsonData)
71 then: 'the CPS service method is invoked once with the expected parameters'
72 1 * mockCpsDataService.saveData(expectedDataspaceName, cmHandle, jsonData, noTimestamp)
73 where: 'following parameters were used'
76 'root level xpath' | '/'
78 def 'Create child data node.'() {
79 given: 'a cm handle and parent node xpath'
80 def jsonData = 'some json'
81 def xpath = '/test-node'
82 when: 'createDataNode is invoked'
83 objectUnderTest.createDataNode(cmHandle, xpath, jsonData)
84 then: 'the CPS service method is invoked once with the expected parameters'
85 1 * mockCpsDataService.saveData(expectedDataspaceName, cmHandle, xpath, jsonData, noTimestamp)
87 def 'Add list-node elements.'() {
88 given: 'a cm handle and parent node xpath'
89 def jsonData = 'some json'
90 def xpath = '/test-node'
91 when: 'addListNodeElements is invoked'
92 objectUnderTest.addListNodeElements(cmHandle, xpath, jsonData)
93 then: 'the CPS service method is invoked once with the expected parameters'
94 1 * mockCpsDataService.saveListNodeData(expectedDataspaceName, cmHandle, xpath, jsonData, noTimestamp)
96 def 'Update data node leaves.'() {
97 given: 'a cm Handle and a cps path'
99 def jsonData = 'some json'
100 when: 'updateNodeLeaves is invoked'
101 objectUnderTest.updateNodeLeaves(cmHandle, xpath, jsonData)
102 then: 'the persistence service is called once with the correct parameters'
103 1 * mockCpsDataService.updateNodeLeaves(expectedDataspaceName, cmHandle, xpath, jsonData, noTimestamp)
105 def 'Replace data node tree.'() {
106 given: 'a cm Handle and a cps path'
108 def jsonData = 'some json'
109 when: 'replaceNodeTree is invoked'
110 objectUnderTest.replaceNodeTree(cmHandle, xpath, jsonData)
111 then: 'the persistence service is called once with the correct parameters'
112 1 * mockCpsDataService.replaceNodeTree(expectedDataspaceName, cmHandle, xpath, jsonData, noTimestamp)
115 def 'Register or re-register a DMI Plugin with #scenario cm handles.'() {
116 given: 'a registration '
117 def dmiPluginRegistration = new DmiPluginRegistration()
118 dmiPluginRegistration.dmiPlugin = 'my-server'
119 persistenceCmHandle.cmHandleID = '123'
120 persistenceCmHandle.cmHandleProperties = [name1: 'value1', name2: 'value2']
121 dmiPluginRegistration.createdCmHandles = createdCmHandles
122 dmiPluginRegistration.updatedCmHandles = updatedCmHandles
123 dmiPluginRegistration.removedCmHandles = removedCmHandles
124 def expectedJsonData = '{"cm-handles":[{"id":"123","dmi-service-name":"my-server","additional-properties":[{"name":"name1","value":"value1"},{"name":"name2","value":"value2"}]}]}'
125 when: 'registration is updated'
126 objectUnderTest.updateDmiPluginRegistration(dmiPluginRegistration)
127 then: 'the CPS save list node data is invoked with the expected parameters'
128 expectedCallsToSaveNode * mockCpsDataService.saveListNodeData('NCMP-Admin', 'ncmp-dmi-registry',
129 '/dmi-registry', expectedJsonData, noTimestamp)
130 and: 'update Node and Child Data Nodes is invoked with correct parameters'
131 expectedCallsToUpdateNode * mockCpsDataService.updateNodeLeavesAndExistingDescendantLeaves('NCMP-Admin',
132 'ncmp-dmi-registry', '/dmi-registry', expectedJsonData, noTimestamp)
133 and : 'delete list data node is invoked with the correct parameters'
134 expectedCallsToDeleteListDataNode * mockCpsDataService.deleteListNodeData('NCMP-Admin',
135 'ncmp-dmi-registry', "/dmi-registry/cm-handles[@id='cmHandle001']", noTimestamp)
138 scenario | createdCmHandles | updatedCmHandles | removedCmHandles || expectedCallsToSaveNode | expectedCallsToUpdateNode | expectedCallsToDeleteListDataNode
139 'create' | [persistenceCmHandle ] | [] | [] || 1 | 0 | 0
140 'update' | [] | [persistenceCmHandle ] | [] || 0 | 1 | 0
141 'delete' | [] | [] | cmHandlesArray || 0 | 0 | 1
142 'create, update and delete' | [persistenceCmHandle ] | [persistenceCmHandle ] | cmHandlesArray || 1 | 1 | 1
146 def 'Register a DMI Plugin for the given cmHandle without additional properties.'() {
147 given: 'a registration without cmHandle properties '
148 def dmiPluginRegistration = new DmiPluginRegistration()
149 dmiPluginRegistration.dmiPlugin = 'my-server'
150 persistenceCmHandle.cmHandleID = '123'
151 persistenceCmHandle.cmHandleProperties = null
152 dmiPluginRegistration.createdCmHandles = [persistenceCmHandle ]
153 def expectedJsonData = '{"cm-handles":[{"id":"123","dmi-service-name":"my-server","additional-properties":[]}]}'
154 when: 'registration is updated'
155 objectUnderTest.updateDmiPluginRegistration(dmiPluginRegistration)
156 then: 'the CPS save list node data is invoked with the expected parameters'
157 1 * mockCpsDataService.saveListNodeData('NCMP-Admin', 'ncmp-dmi-registry',
158 '/dmi-registry', expectedJsonData, noTimestamp)
161 def 'Get resource data for pass-through operational from dmi.'() {
163 def xpath = "/dmi-registry/cm-handles[@id='testCmHandle']"
165 def dataNode = new DataNode()
166 dataNode.leaves = ['dmi-service-name':'testDmiService']
167 def childDataNode = new DataNode()
168 childDataNode.leaves = ['name':'testName','value':'testValue']
169 dataNode.childDataNodes = [childDataNode]
170 when: 'get resource data is called'
171 def response = objectUnderTest.getResourceDataOperationalForCmHandle('testCmHandle',
176 then: 'cps data service is being called once to get data node'
177 1 * mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
178 xpath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> dataNode
179 and: 'dmi operation is being called to get resource data'
180 1 * mockDmiOperations.getResouceDataOperationalFromDmi('testDmiService',
186 '{"operation":"read","cmHandleProperties":{"testName":"testValue"}}') >>
187 new ResponseEntity<>('result-json', HttpStatus.OK)
188 and: 'dmi returns ok response'
189 response == 'result-json'
191 def 'Get resource data for pass-through operational from dmi threw parsing exception.'() {
193 def xpath = "/dmi-registry/cm-handles[@id='testCmHandle']"
195 def dataNode = new DataNode()
196 dataNode.leaves = ['dmi-service-name':'testDmiService']
197 def childDataNode = new DataNode()
198 childDataNode.leaves = ['name':'testName','value':'testValue']
199 dataNode.childDataNodes = [childDataNode]
200 mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
201 xpath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> dataNode
202 and: 'objectMapper not able to parse object'
203 def mockObjectMapper = Mock(ObjectMapper)
204 objectUnderTest.objectMapper = mockObjectMapper
205 mockObjectMapper.writeValueAsString(_) >> { throw new JsonProcessingException("testException") }
206 when: 'get resource data is called'
207 def response = objectUnderTest.getResourceDataOperationalForCmHandle('testCmHandle',
212 then: 'exception is thrown'
213 thrown(NcmpException.class)
215 def 'Get resource data for pass-through operational from dmi return NOK response.'() {
217 def xpath = "/dmi-registry/cm-handles[@id='testCmHandle']"
219 def dataNode = new DataNode()
220 dataNode.leaves = ['dmi-service-name':'testDmiService']
221 def childDataNode = new DataNode()
222 childDataNode.leaves = ['name':'testName','value':'testValue']
223 dataNode.childDataNodes = [childDataNode]
224 mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
225 xpath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> dataNode
226 and: 'dmi returns NOK response'
227 mockDmiOperations.getResouceDataOperationalFromDmi('testDmiService',
233 '{"operation":"read","cmHandleProperties":{"testName":"testValue"}}')
234 >> new ResponseEntity<>('NOK-json', HttpStatus.NOT_FOUND)
235 when: 'get resource data is called'
236 def response = objectUnderTest.getResourceDataOperationalForCmHandle('testCmHandle',
241 then: 'exception is thrown'
242 thrown(NcmpException.class)
244 def 'Get resource data for pass-through running from dmi.'() {
246 def xpath = "/dmi-registry/cm-handles[@id='testCmHandle']"
247 and: 'data node representing cmhandle and its properties'
248 def dataNode = new DataNode()
249 dataNode.leaves = ['dmi-service-name':'testDmiService']
250 def childDataNode = new DataNode()
251 childDataNode.leaves = ['name':'testName','value':'testValue']
252 dataNode.childDataNodes = [childDataNode]
253 and: 'cpsDataService returns valid dataNode'
254 mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
255 xpath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> dataNode
256 and: 'dmi returns valid response and data'
257 mockDmiOperations.getResouceDataPassThroughRunningFromDmi('testDmiService',
263 '{"operation":"read","cmHandleProperties":{"testName":"testValue"}}') >> new ResponseEntity<>('{result-json}', HttpStatus.OK)
264 when: 'get resource data is called'
265 def response = objectUnderTest.getResourceDataPassThroughRunningForCmHandle('testCmHandle',
270 then: 'get resource data returns expected response'
271 response == '{result-json}'
273 def 'Get resource data for pass-through running from dmi threw parsing exception.'() {
275 def xpath = "/dmi-registry/cm-handles[@id='testCmHandle']"
276 and: 'data node representing cmhandle and its properties'
277 def dataNode = new DataNode()
278 dataNode.leaves = ['dmi-service-name':'testDmiService']
279 def childDataNode = new DataNode()
280 childDataNode.leaves = ['name':'testName','value':'testValue']
281 dataNode.childDataNodes = [childDataNode]
282 and: 'cpsDataService returns valid dataNode'
283 mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
284 xpath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> dataNode
285 and: 'objectMapper not able to parse object'
286 def mockObjectMapper = Mock(ObjectMapper)
287 objectUnderTest.objectMapper = mockObjectMapper
288 mockObjectMapper.writeValueAsString(_) >> { throw new JsonProcessingException("testException") }
289 when: 'get resource data is called'
290 def response = objectUnderTest.getResourceDataPassThroughRunningForCmHandle('testCmHandle',
295 then: 'exception is thrown'
296 thrown(NcmpException.class)
298 def 'Get resource data for pass-through running from dmi return NOK response.'() {
300 def xpath = "/dmi-registry/cm-handles[@id='testCmHandle']"
301 and: 'data node representing cmhandle and its properties'
302 def dataNode = new DataNode()
303 dataNode.leaves = ['dmi-service-name':'testDmiService']
304 def childDataNode = new DataNode()
305 childDataNode.leaves = ['name':'testName','value':'testValue']
306 dataNode.childDataNodes = [childDataNode]
307 and: 'cpsDataService returns valid dataNode'
308 mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
309 xpath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> dataNode
310 and: 'dmi returns NOK response'
311 mockDmiOperations.getResouceDataPassThroughRunningFromDmi('testDmiService',
317 '{"operation":"read","cmHandleProperties":{"testName":"testValue"}}')
318 >> new ResponseEntity<>('NOK-json', HttpStatus.NOT_FOUND)
319 when: 'get resource data is called'
320 def response = objectUnderTest.getResourceDataPassThroughRunningForCmHandle('testCmHandle',
325 then: 'exception is thrown'
326 thrown(NcmpException.class)