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 mockCpsDataService = Mock(CpsDataService)
45 def mockCpsQueryService = Mock(CpsQueryService)
46 def mockDmiOperations = Mock(DmiOperations)
47 def objectUnderTest = new NetworkCmProxyDataServiceImpl(mockDmiOperations, mockCpsDataService, mockCpsQueryService, new ObjectMapper())
49 def cmHandle = 'some handle'
51 def expectedDataspaceName = 'NFP-Operational'
52 def 'Query data nodes by cps path with #fetchDescendantsOption.'() {
53 given: 'a cm Handle and a cps path'
54 def cpsPath = '/cps-path'
55 when: 'queryDataNodes is invoked'
56 objectUnderTest.queryDataNodes(cmHandle, cpsPath, fetchDescendantsOption)
57 then: 'the persistence service is called once with the correct parameters'
58 1 * mockCpsQueryService.queryDataNodes(expectedDataspaceName, cmHandle, cpsPath, fetchDescendantsOption)
59 where: 'all fetch descendants options are supported'
60 fetchDescendantsOption << FetchDescendantsOption.values()
62 def 'Create full data node: #scenario.'() {
63 given: 'a cm handle and root xpath'
64 def jsonData = 'some json'
65 when: 'createDataNode is invoked'
66 objectUnderTest.createDataNode(cmHandle, xpath, jsonData)
67 then: 'the CPS service method is invoked once with the expected parameters'
68 1 * mockCpsDataService.saveData(expectedDataspaceName, cmHandle, jsonData)
69 where: 'following parameters were used'
72 'root level xpath' | '/'
74 def 'Create child data node.'() {
75 given: 'a cm handle and parent node xpath'
76 def jsonData = 'some json'
77 def xpath = '/test-node'
78 when: 'createDataNode is invoked'
79 objectUnderTest.createDataNode(cmHandle, xpath, jsonData)
80 then: 'the CPS service method is invoked once with the expected parameters'
81 1 * mockCpsDataService.saveData(expectedDataspaceName, cmHandle, xpath, jsonData)
83 def 'Add list-node elements.'() {
84 given: 'a cm handle and parent node xpath'
85 def jsonData = 'some json'
86 def xpath = '/test-node'
87 when: 'addListNodeElements is invoked'
88 objectUnderTest.addListNodeElements(cmHandle, xpath, jsonData)
89 then: 'the CPS service method is invoked once with the expected parameters'
90 1 * mockCpsDataService.saveListNodeData(expectedDataspaceName, cmHandle, xpath, jsonData)
92 def 'Update data node leaves.'() {
93 given: 'a cm Handle and a cps path'
95 def jsonData = 'some json'
96 when: 'updateNodeLeaves is invoked'
97 objectUnderTest.updateNodeLeaves(cmHandle, xpath, jsonData)
98 then: 'the persistence service is called once with the correct parameters'
99 1 * mockCpsDataService.updateNodeLeaves(expectedDataspaceName, cmHandle, xpath, jsonData)
101 def 'Replace data node tree.'() {
102 given: 'a cm Handle and a cps path'
104 def jsonData = 'some json'
105 when: 'replaceNodeTree is invoked'
106 objectUnderTest.replaceNodeTree(cmHandle, xpath, jsonData)
107 then: 'the persistence service is called once with the correct parameters'
108 1 * mockCpsDataService.replaceNodeTree(expectedDataspaceName, cmHandle, xpath, jsonData)
111 def 'Register or re-register a DMI Plugin with #scenario cm handles.'() {
112 given: 'a registration '
113 def dmiRegistryAnchor = 'ncmp-dmi-registry'
114 def dmiPluginRegistration = new DmiPluginRegistration()
115 dmiPluginRegistration.dmiPlugin = 'my-server'
116 persistenceCmHandle.cmHandleID = '123'
117 persistenceCmHandle.cmHandleProperties = [name1: 'value1', name2: 'value2']
118 dmiPluginRegistration.createdCmHandles = createdCmHandles
119 dmiPluginRegistration.updatedCmHandles = updatedCmHandles
120 def expectedJsonData = '{"cm-handles":[{"id":"123","dmi-service-name":"my-server","additional-properties":[{"name":"name1","value":"value1"},{"name":"name2","value":"value2"}]}]}'
121 when: 'registration is updated'
122 objectUnderTest.updateDmiPluginRegistration(dmiPluginRegistration)
123 then: 'the CPS save list node data is invoked with the expected parameters'
124 expectedCallsToSaveNode * mockCpsDataService.saveListNodeData('NCMP-Admin', 'ncmp-dmi-registry', '/dmi-registry', expectedJsonData)
125 and: 'update Node and Child Data Nodes is invoked with correct parameter'
126 expectedCallsToUpdateNode * mockCpsDataService.updateNodeLeavesAndExistingDescendantLeaves('NCMP-Admin', dmiRegistryAnchor, '/dmi-registry', expectedJsonData)
128 scenario | createdCmHandles | updatedCmHandles || expectedCallsToSaveNode | expectedCallsToUpdateNode
129 'create' | [persistenceCmHandle ] | [] || 1 | 0
130 'update' | [] | [persistenceCmHandle ] || 0 | 1
131 'create and update' | [persistenceCmHandle ] | [persistenceCmHandle ] || 1 | 1
135 def 'Register a DMI Plugin for the given cmHandle without additional properties.'() {
136 given: 'a registration without cmHandle properties '
137 def dmiPluginRegistration = new DmiPluginRegistration()
138 dmiPluginRegistration.dmiPlugin = 'my-server'
139 persistenceCmHandle.cmHandleID = '123'
140 persistenceCmHandle.cmHandleProperties = null
141 dmiPluginRegistration.createdCmHandles = [persistenceCmHandle ]
142 def expectedJsonData = '{"cm-handles":[{"id":"123","dmi-service-name":"my-server","additional-properties":[]}]}'
143 when: 'registration is updated'
144 objectUnderTest.updateDmiPluginRegistration(dmiPluginRegistration)
145 then: 'the CPS save list node data is invoked with the expected parameters'
146 1 * mockCpsDataService.saveListNodeData('NCMP-Admin', 'ncmp-dmi-registry', '/dmi-registry', expectedJsonData)
149 def 'Get resource data for pass-through operational from dmi.'() {
151 def xpath = "/dmi-registry/cm-handles[@id='testCmHandle']"
153 def dataNode = new DataNode()
154 dataNode.leaves = ['dmi-service-name':'testDmiService']
155 def childDataNode = new DataNode()
156 childDataNode.leaves = ['name':'testName','value':'testValue']
157 dataNode.childDataNodes = [childDataNode]
158 when: 'get resource data is called'
159 def response = objectUnderTest.getResourceDataOperationalForCmHandle('testCmHandle',
164 then: 'cps data service is being called once to get data node'
165 1 * mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
166 xpath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> dataNode
167 and: 'dmi operation is being called to get resource data'
168 1 * mockDmiOperations.getResouceDataOperationalFromDmi('testDmiService',
174 '{"operation":"read","cmHandleProperties":{"testName":"testValue"}}') >> new ResponseEntity<>('result-json', HttpStatus.OK)
175 and: 'dmi returns ok response'
176 response == 'result-json'
178 def 'Get resource data for pass-through operational from dmi threw parsing exception.'() {
180 def xpath = "/dmi-registry/cm-handles[@id='testCmHandle']"
182 def dataNode = new DataNode()
183 dataNode.leaves = ['dmi-service-name':'testDmiService']
184 def childDataNode = new DataNode()
185 childDataNode.leaves = ['name':'testName','value':'testValue']
186 dataNode.childDataNodes = [childDataNode]
187 mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
188 xpath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> dataNode
189 and: 'objectMapper not able to parse object'
190 def mockObjectMapper = Mock(ObjectMapper)
191 objectUnderTest.objectMapper = mockObjectMapper
192 mockObjectMapper.writeValueAsString(_) >> { throw new JsonProcessingException("testException") }
193 when: 'get resource data is called'
194 def response = objectUnderTest.getResourceDataOperationalForCmHandle('testCmHandle',
199 then: 'exception is thrown'
200 thrown(NcmpException.class)
202 def 'Get resource data for pass-through operational from dmi return NOK response.'() {
204 def xpath = "/dmi-registry/cm-handles[@id='testCmHandle']"
206 def dataNode = new DataNode()
207 dataNode.leaves = ['dmi-service-name':'testDmiService']
208 def childDataNode = new DataNode()
209 childDataNode.leaves = ['name':'testName','value':'testValue']
210 dataNode.childDataNodes = [childDataNode]
211 mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
212 xpath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> dataNode
213 and: 'dmi returns NOK response'
214 mockDmiOperations.getResouceDataOperationalFromDmi('testDmiService',
220 '{"operation":"read","cmHandleProperties":{"testName":"testValue"}}')
221 >> new ResponseEntity<>('NOK-json', HttpStatus.NOT_FOUND)
222 when: 'get resource data is called'
223 def response = objectUnderTest.getResourceDataOperationalForCmHandle('testCmHandle',
228 then: 'exception is thrown'
229 thrown(NcmpException.class)
231 def 'Get resource data for pass-through running from dmi.'() {
233 def xpath = "/dmi-registry/cm-handles[@id='testCmHandle']"
234 and: 'data node representing cmhandle and its properties'
235 def dataNode = new DataNode()
236 dataNode.leaves = ['dmi-service-name':'testDmiService']
237 def childDataNode = new DataNode()
238 childDataNode.leaves = ['name':'testName','value':'testValue']
239 dataNode.childDataNodes = [childDataNode]
240 and: 'cpsDataService returns valid dataNode'
241 mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
242 xpath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> dataNode
243 and: 'dmi returns valid response and data'
244 mockDmiOperations.getResouceDataPassThroughRunningFromDmi('testDmiService',
250 '{"operation":"read","cmHandleProperties":{"testName":"testValue"}}') >> new ResponseEntity<>('{result-json}', HttpStatus.OK)
251 when: 'get resource data is called'
252 def response = objectUnderTest.getResourceDataPassThroughRunningForCmHandle('testCmHandle',
257 then: 'get resource data returns expected response'
258 response == '{result-json}'
260 def 'Get resource data for pass-through running from dmi threw parsing exception.'() {
262 def xpath = "/dmi-registry/cm-handles[@id='testCmHandle']"
263 and: 'data node representing cmhandle and its properties'
264 def dataNode = new DataNode()
265 dataNode.leaves = ['dmi-service-name':'testDmiService']
266 def childDataNode = new DataNode()
267 childDataNode.leaves = ['name':'testName','value':'testValue']
268 dataNode.childDataNodes = [childDataNode]
269 and: 'cpsDataService returns valid dataNode'
270 mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
271 xpath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> dataNode
272 and: 'objectMapper not able to parse object'
273 def mockObjectMapper = Mock(ObjectMapper)
274 objectUnderTest.objectMapper = mockObjectMapper
275 mockObjectMapper.writeValueAsString(_) >> { throw new JsonProcessingException("testException") }
276 when: 'get resource data is called'
277 def response = objectUnderTest.getResourceDataPassThroughRunningForCmHandle('testCmHandle',
282 then: 'exception is thrown'
283 thrown(NcmpException.class)
285 def 'Get resource data for pass-through running from dmi return NOK response.'() {
287 def xpath = "/dmi-registry/cm-handles[@id='testCmHandle']"
288 and: 'data node representing cmhandle and its properties'
289 def dataNode = new DataNode()
290 dataNode.leaves = ['dmi-service-name':'testDmiService']
291 def childDataNode = new DataNode()
292 childDataNode.leaves = ['name':'testName','value':'testValue']
293 dataNode.childDataNodes = [childDataNode]
294 and: 'cpsDataService returns valid dataNode'
295 mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
296 xpath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> dataNode
297 and: 'dmi returns NOK response'
298 mockDmiOperations.getResouceDataPassThroughRunningFromDmi('testDmiService',
304 '{"operation":"read","cmHandleProperties":{"testName":"testValue"}}')
305 >> new ResponseEntity<>('NOK-json', HttpStatus.NOT_FOUND)
306 when: 'get resource data is called'
307 def response = objectUnderTest.getResourceDataPassThroughRunningForCmHandle('testCmHandle',
312 then: 'exception is thrown'
313 thrown(NcmpException.class)