Fix exception when registration has no additionProperties
[cps.git] / cps-ncmp-service / src / test / groovy / org / onap / cps / ncmp / api / impl / NetworkCmProxyDataServiceImplSpec.groovy
1 /*
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
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.cps.ncmp.api.impl
23
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
38
39 class NetworkCmProxyDataServiceImplSpec extends Specification {
40
41     @Shared
42     def persistenceCmHandle = new CmHandle()
43
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())
48
49     def cmHandle = 'some handle'
50
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()
61     }
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'
70             scenario           | xpath
71             'no xpath'         | ''
72             'root level xpath' | '/'
73     }
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)
82     }
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)
91     }
92     def 'Update data node leaves.'() {
93         given: 'a cm Handle and a cps path'
94             def xpath = '/xpath'
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)
100     }
101     def 'Replace data node tree.'() {
102         given: 'a cm Handle and a cps path'
103             def xpath = '/xpath'
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)
109     }
110
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)
127         where:
128             scenario                | createdCmHandles       | updatedCmHandles       || expectedCallsToSaveNode   | expectedCallsToUpdateNode
129             'create'                | [persistenceCmHandle ] | []                     || 1                         | 0
130             'update'                | []                     | [persistenceCmHandle ] || 0                         | 1
131             'create and update'     | [persistenceCmHandle ] | [persistenceCmHandle ] || 1                         | 1
132
133     }
134
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)
147     }
148
149     def 'Get resource data for pass-through operational from dmi.'() {
150         given: 'xpath'
151             def xpath = "/dmi-registry/cm-handles[@id='testCmHandle']"
152         and: 'data node'
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',
160             'testResourceId',
161             'testAcceptParam',
162             'testFieldQuery',
163             5)
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',
169                     'testCmHandle',
170                     'testResourceId',
171                     'testFieldQuery',
172                     5,
173                     'testAcceptParam',
174             '{"operation":"read","cmHandleProperties":{"testName":"testValue"}}') >> new ResponseEntity<>('result-json', HttpStatus.OK)
175         and: 'dmi returns ok response'
176             response == 'result-json'
177     }
178     def 'Get resource data for pass-through operational from dmi threw parsing exception.'() {
179         given: 'xpath'
180             def xpath = "/dmi-registry/cm-handles[@id='testCmHandle']"
181         and: 'data node'
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',
195                     'testResourceId',
196                     'testAcceptParam',
197                     'testFieldQuery',
198                     5)
199         then: 'exception is thrown'
200             thrown(NcmpException.class)
201     }
202     def 'Get resource data for pass-through operational from dmi return NOK response.'() {
203         given: 'xpath'
204             def xpath = "/dmi-registry/cm-handles[@id='testCmHandle']"
205         and: 'data node'
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',
215                     'testCmHandle',
216                     'testResourceId',
217                     'testFieldQuery',
218                     5,
219                     'testAcceptParam',
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',
224                     'testResourceId',
225                     'testAcceptParam',
226                     'testFieldQuery',
227                     5)
228         then: 'exception is thrown'
229             thrown(NcmpException.class)
230     }
231     def 'Get resource data for pass-through running from dmi.'() {
232         given: 'xpath'
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',
245                     'testCmHandle',
246                     'testResourceId',
247                     'testFieldQuery',
248                     5,
249                     'testAcceptParam',
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',
253                     'testResourceId',
254                     'testAcceptParam',
255                     'testFieldQuery',
256                     5)
257         then: 'get resource data returns expected response'
258             response == '{result-json}'
259     }
260     def 'Get resource data for pass-through running from dmi threw parsing exception.'() {
261         given: 'xpath'
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',
278                     'testResourceId',
279                     'testAcceptParam',
280                     'testFieldQuery',
281                     5)
282         then: 'exception is thrown'
283             thrown(NcmpException.class)
284     }
285     def 'Get resource data for pass-through running from dmi return NOK response.'() {
286         given: 'xpath'
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',
299                     'testCmHandle',
300                     'testResourceId',
301                     'testFieldQuery',
302                     5,
303                     'testAcceptParam',
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',
308                     'testResourceId',
309                     'testAcceptParam',
310                     'testFieldQuery',
311                     5)
312         then: 'exception is thrown'
313             thrown(NcmpException.class)
314     }
315 }