Post impl for passthrough running (Ncmp impl.)
[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  *  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
10  *
11  *        http://www.apache.org/licenses/LICENSE-2.0
12  *
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.
18  *
19  *  SPDX-License-Identifier: Apache-2.0
20  *  ============LICENSE_END=========================================================
21  */
22
23 package org.onap.cps.ncmp.api.impl
24
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
39
40 class NetworkCmProxyDataServiceImplSpec extends Specification {
41
42     @Shared
43     def persistenceCmHandle = new CmHandle()
44     @Shared
45     def cmHandlesArray = ['cmHandle001']
46
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())
51
52     def cmHandle = 'some handle'
53     def noTimestamp = null
54     def cmHandleXPath = "/dmi-registry/cm-handles[@id='testCmHandle']"
55
56     def expectedDataspaceName = 'NFP-Operational'
57     def 'Query data nodes by cps path with #fetchDescendantsOption.'() {
58         given: 'a cm Handle and a cps path'
59             def cpsPath = '/cps-path'
60         when: 'queryDataNodes is invoked'
61             objectUnderTest.queryDataNodes(cmHandle, cpsPath, fetchDescendantsOption)
62         then: 'the persistence service is called once with the correct parameters'
63             1 * mockCpsQueryService.queryDataNodes(expectedDataspaceName, cmHandle, cpsPath, fetchDescendantsOption)
64         where: 'all fetch descendants options are supported'
65             fetchDescendantsOption << FetchDescendantsOption.values()
66     }
67     def 'Create full data node: #scenario.'() {
68         given: 'a cm handle and root xpath'
69             def jsonData = 'some json'
70         when: 'createDataNode is invoked'
71             objectUnderTest.createDataNode(cmHandle, xpath, jsonData)
72         then: 'the CPS service method is invoked once with the expected parameters'
73             1 * mockCpsDataService.saveData(expectedDataspaceName, cmHandle, jsonData, noTimestamp)
74         where: 'following parameters were used'
75             scenario           | xpath
76             'no xpath'         | ''
77             'root level xpath' | '/'
78     }
79     def 'Create child data node.'() {
80         given: 'a cm handle and parent node xpath'
81             def jsonData = 'some json'
82             def xpath = '/test-node'
83         when: 'createDataNode is invoked'
84             objectUnderTest.createDataNode(cmHandle, xpath, jsonData)
85         then: 'the CPS service method is invoked once with the expected parameters'
86             1 * mockCpsDataService.saveData(expectedDataspaceName, cmHandle, xpath, jsonData, noTimestamp)
87     }
88     def 'Add list-node elements.'() {
89         given: 'a cm handle and parent node xpath'
90             def jsonData = 'some json'
91             def xpath = '/test-node'
92         when: 'addListNodeElements is invoked'
93             objectUnderTest.addListNodeElements(cmHandle, xpath, jsonData)
94         then: 'the CPS service method is invoked once with the expected parameters'
95             1 * mockCpsDataService.saveListNodeData(expectedDataspaceName, cmHandle, xpath, jsonData, noTimestamp)
96     }
97     def 'Update data node leaves.'() {
98         given: 'a cm Handle and a cps path'
99             def xpath = '/xpath'
100             def jsonData = 'some json'
101         when: 'updateNodeLeaves is invoked'
102             objectUnderTest.updateNodeLeaves(cmHandle, xpath, jsonData)
103         then: 'the persistence service is called once with the correct parameters'
104             1 * mockCpsDataService.updateNodeLeaves(expectedDataspaceName, cmHandle, xpath, jsonData, noTimestamp)
105     }
106     def 'Replace data node tree.'() {
107         given: 'a cm Handle and a cps path'
108             def xpath = '/xpath'
109             def jsonData = 'some json'
110         when: 'replaceNodeTree is invoked'
111             objectUnderTest.replaceNodeTree(cmHandle, xpath, jsonData)
112         then: 'the persistence service is called once with the correct parameters'
113             1 * mockCpsDataService.replaceNodeTree(expectedDataspaceName, cmHandle, xpath, jsonData, noTimestamp)
114     }
115
116     def 'Register or re-register a DMI Plugin with #scenario cm handles.'() {
117         given: 'a registration '
118             def dmiPluginRegistration = new DmiPluginRegistration()
119             dmiPluginRegistration.dmiPlugin = 'my-server'
120             persistenceCmHandle.cmHandleID = '123'
121             persistenceCmHandle.cmHandleProperties = [name1: 'value1', name2: 'value2']
122             dmiPluginRegistration.createdCmHandles = createdCmHandles
123             dmiPluginRegistration.updatedCmHandles = updatedCmHandles
124             dmiPluginRegistration.removedCmHandles = removedCmHandles
125             def expectedJsonData = '{"cm-handles":[{"id":"123","dmi-service-name":"my-server","additional-properties":[{"name":"name1","value":"value1"},{"name":"name2","value":"value2"}]}]}'
126         when: 'registration is updated'
127             objectUnderTest.updateDmiPluginRegistration(dmiPluginRegistration)
128         then: 'the CPS save list node data is invoked with the expected parameters'
129             expectedCallsToSaveNode * mockCpsDataService.saveListNodeData('NCMP-Admin', 'ncmp-dmi-registry',
130                 '/dmi-registry', expectedJsonData, noTimestamp)
131         and: 'update Node and Child Data Nodes is invoked with correct parameters'
132             expectedCallsToUpdateNode * mockCpsDataService.updateNodeLeavesAndExistingDescendantLeaves('NCMP-Admin',
133                 'ncmp-dmi-registry', '/dmi-registry', expectedJsonData, noTimestamp)
134         and : 'delete list data node is invoked with the correct parameters'
135             expectedCallsToDeleteListDataNode * mockCpsDataService.deleteListNodeData('NCMP-Admin',
136                 'ncmp-dmi-registry', "/dmi-registry/cm-handles[@id='cmHandle001']", noTimestamp)
137
138         where:
139             scenario                        | createdCmHandles       | updatedCmHandles       | removedCmHandles || expectedCallsToSaveNode   | expectedCallsToUpdateNode | expectedCallsToDeleteListDataNode
140             'create'                        | [persistenceCmHandle ] | []                     | []               || 1                         | 0                         | 0
141             'update'                        | []                     | [persistenceCmHandle ] | []               || 0                         | 1                         | 0
142             'delete'                        | []                     | []                     | cmHandlesArray   || 0                         | 0                         | 1
143             'create, update and delete'     | [persistenceCmHandle ] | [persistenceCmHandle ] | cmHandlesArray   || 1                         | 1                         | 1
144
145     }
146
147     def 'Register a DMI Plugin for the given cmHandle without additional properties.'() {
148         given: 'a registration without cmHandle properties '
149             def dmiPluginRegistration = new DmiPluginRegistration()
150             dmiPluginRegistration.dmiPlugin = 'my-server'
151             persistenceCmHandle.cmHandleID = '123'
152             persistenceCmHandle.cmHandleProperties = null
153             dmiPluginRegistration.createdCmHandles = [persistenceCmHandle ]
154             def expectedJsonData = '{"cm-handles":[{"id":"123","dmi-service-name":"my-server","additional-properties":[]}]}'
155         when: 'registration is updated'
156             objectUnderTest.updateDmiPluginRegistration(dmiPluginRegistration)
157         then: 'the CPS save list node data is invoked with the expected parameters'
158             1 * mockCpsDataService.saveListNodeData('NCMP-Admin', 'ncmp-dmi-registry',
159                 '/dmi-registry', expectedJsonData, noTimestamp)
160     }
161
162     def 'Get resource data for pass-through operational from dmi.'() {
163         given: 'data node representing cmHandle and its properties'
164             def cmHandleDataNode = prepareCmHandleDataNode()
165         when: 'get resource data is called'
166             def response = objectUnderTest.getResourceDataOperationalForCmHandle('testCmHandle',
167             'testResourceId',
168             'testAcceptParam',
169             'testFieldQuery',
170             5)
171         then: 'cps data service is being called once to get data node'
172             1 * mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
173                     cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> cmHandleDataNode
174         and: 'dmi operation is being called to get resource data'
175             1 * mockDmiOperations.getResourceDataOperationalFromDmi('testDmiService',
176                     'testCmHandle',
177                     'testResourceId',
178                     'testFieldQuery',
179                     5,
180                     'testAcceptParam',
181             '{"operation":"read","cmHandleProperties":{"testName":"testValue"}}') >>
182                 new ResponseEntity<>('result-json', HttpStatus.OK)
183         and: 'dmi returns ok response'
184             response == 'result-json'
185     }
186
187     def 'Get resource data for pass-through operational from dmi threw parsing exception.'() {
188         given: 'data node representing cmHandle and its properties'
189             def cmHandleDataNode = prepareCmHandleDataNode()
190         and: 'cps data service returns valid cmHandle data node'
191             mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
192                     cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> cmHandleDataNode
193         and: 'objectMapper not able to parse object'
194             def mockObjectMapper = Mock(ObjectMapper)
195             objectUnderTest.objectMapper = mockObjectMapper
196             mockObjectMapper.writeValueAsString(_) >> { throw new JsonProcessingException("testException") }
197         when: 'get resource data is called'
198             def response = objectUnderTest.getResourceDataOperationalForCmHandle('testCmHandle',
199                     'testResourceId',
200                     'testAcceptParam',
201                     'testFieldQuery',
202                     5)
203         then: 'exception is thrown'
204             thrown(NcmpException.class)
205     }
206
207     def 'Get resource data for pass-through operational from dmi return NOK response.'() {
208         given: 'data node representing cmHandle and its properties'
209             def cmHandleDataNode = prepareCmHandleDataNode()
210         and: 'cps data service returns valid cmHandle data node'
211             mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
212                     cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> cmHandleDataNode
213         and: 'dmi returns NOK response'
214             mockDmiOperations.getResourceDataOperationalFromDmi('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
232     def 'Get resource data for pass-through running from dmi.'() {
233         given: 'data node representing cmHandle and its properties'
234             def cmHandleDataNode = prepareCmHandleDataNode()
235         and: 'cpsDataService returns valid dataNode'
236             mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
237                     cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> cmHandleDataNode
238         and: 'dmi returns valid response and data'
239             mockDmiOperations.getResourceDataPassThroughRunningFromDmi('testDmiService',
240                     'testCmHandle',
241                     'testResourceId',
242                     'testFieldQuery',
243                     5,
244                     'testAcceptParam',
245                     '{"operation":"read","cmHandleProperties":{"testName":"testValue"}}') >> new ResponseEntity<>('{result-json}', HttpStatus.OK)
246         when: 'get resource data is called'
247             def response = objectUnderTest.getResourceDataPassThroughRunningForCmHandle('testCmHandle',
248                     'testResourceId',
249                     'testAcceptParam',
250                     'testFieldQuery',
251                     5)
252         then: 'get resource data returns expected response'
253             response == '{result-json}'
254     }
255
256     def 'Get resource data for pass-through running from dmi threw parsing exception.'() {
257         given: 'data node representing cmHandle and its properties'
258             def cmHandleDataNode = prepareCmHandleDataNode()
259         and: 'cpsDataService returns valid dataNode'
260             mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
261                     cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> cmHandleDataNode
262         and: 'objectMapper not able to parse object'
263             def mockObjectMapper = Mock(ObjectMapper)
264             objectUnderTest.objectMapper = mockObjectMapper
265             mockObjectMapper.writeValueAsString(_) >> { throw new JsonProcessingException("testException") }
266         when: 'get resource data is called'
267             def response = objectUnderTest.getResourceDataPassThroughRunningForCmHandle('testCmHandle',
268                     'testResourceId',
269                     'testAcceptParam',
270                     'testFieldQuery',
271                     5)
272         then: 'exception is thrown'
273             thrown(NcmpException.class)
274     }
275
276     def 'Get resource data for pass-through running from dmi return NOK response.'() {
277         given: 'data node representing cmHandle and its properties'
278             def cmHandleDataNode = prepareCmHandleDataNode()
279         and: 'cpsDataService returns valid dataNode'
280             mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
281                     cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> cmHandleDataNode
282         and: 'dmi returns NOK response'
283             mockDmiOperations.getResourceDataPassThroughRunningFromDmi('testDmiService',
284                     'testCmHandle',
285                     'testResourceId',
286                     'testFieldQuery',
287                     5,
288                     'testAcceptParam',
289                     '{"operation":"read","cmHandleProperties":{"testName":"testValue"}}')
290                     >> new ResponseEntity<>('NOK-json', HttpStatus.NOT_FOUND)
291         when: 'get resource data is called'
292             def response = objectUnderTest.getResourceDataPassThroughRunningForCmHandle('testCmHandle',
293                     'testResourceId',
294                     'testAcceptParam',
295                     'testFieldQuery',
296                     5)
297         then: 'exception is thrown'
298             thrown(NcmpException.class)
299     }
300
301     def 'Write resource data for pass-through running from dmi using POST.'() {
302         given: 'data node representing cmHandle and its properties'
303             def cmHandleDataNode = prepareCmHandleDataNode()
304         and: 'cpsDataService returns valid dataNode'
305             mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
306                     cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> cmHandleDataNode
307         when: 'get resource data is called'
308             objectUnderTest.createResourceDataPassThroughRunningForCmHandle('testCmHandle',
309                     'testResourceId',
310                     '{some-json}', 'application/json')
311         then: 'dmi called with correct data'
312             1 * mockDmiOperations.createResourceDataPassThroughRunningFromDmi('testDmiService',
313                 'testCmHandle',
314                 'testResourceId',
315                 '{"operation":"create","dataType":"application/json","data":"{some-json}","cmHandleProperties":{"testName":"testValue"}}')
316                 >> { new ResponseEntity<>(HttpStatus.CREATED) }
317     }
318
319     def 'Write resource data for pass-through running from dmi using POST "not found" response (from DMI).'() {
320         given: 'data node representing cmHandle and its properties'
321             def cmHandleDataNode = prepareCmHandleDataNode()
322         and: 'cpsDataService returns valid dataNode'
323             mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
324                     cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> cmHandleDataNode
325         and: 'dmi throws exception'
326             1 * mockDmiOperations.createResourceDataPassThroughRunningFromDmi(_ as String, _ as String, _ as String, _ as String)
327                     >> { new ResponseEntity<>(HttpStatus.NOT_FOUND) }
328         when: 'get resource data is called'
329             objectUnderTest.createResourceDataPassThroughRunningForCmHandle('testCmHandle',
330                     'testResourceId',
331                     '{some-json}', 'application/json')
332         then: 'exception is thrown'
333             thrown(NcmpException.class)
334     }
335
336     private DataNode prepareCmHandleDataNode() {
337         def cmHandleDataNode = new DataNode()
338         cmHandleDataNode.leaves = ['dmi-service-name': 'testDmiService']
339         def cmHandlePropertyDataNode = new DataNode()
340         cmHandlePropertyDataNode.leaves = ['name': 'testName', 'value': 'testValue']
341         cmHandleDataNode.childDataNodes = [cmHandlePropertyDataNode]
342         cmHandleDataNode
343     }
344 }