Add optional observed timestamp in the cps data api
[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
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()
65     }
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'
74             scenario           | xpath
75             'no xpath'         | ''
76             'root level xpath' | '/'
77     }
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)
86     }
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)
95     }
96     def 'Update data node leaves.'() {
97         given: 'a cm Handle and a cps path'
98             def xpath = '/xpath'
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)
104     }
105     def 'Replace data node tree.'() {
106         given: 'a cm Handle and a cps path'
107             def xpath = '/xpath'
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)
113     }
114
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)
136
137         where:
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
143
144     }
145
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)
159     }
160
161     def 'Get resource data for pass-through operational from dmi.'() {
162         given: 'xpath'
163             def xpath = "/dmi-registry/cm-handles[@id='testCmHandle']"
164         and: 'data node'
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',
172             'testResourceId',
173             'testAcceptParam',
174             'testFieldQuery',
175             5)
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',
181                     'testCmHandle',
182                     'testResourceId',
183                     'testFieldQuery',
184                     5,
185                     'testAcceptParam',
186             '{"operation":"read","cmHandleProperties":{"testName":"testValue"}}') >>
187                 new ResponseEntity<>('result-json', HttpStatus.OK)
188         and: 'dmi returns ok response'
189             response == 'result-json'
190     }
191     def 'Get resource data for pass-through operational from dmi threw parsing exception.'() {
192         given: 'xpath'
193             def xpath = "/dmi-registry/cm-handles[@id='testCmHandle']"
194         and: 'data node'
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',
208                     'testResourceId',
209                     'testAcceptParam',
210                     'testFieldQuery',
211                     5)
212         then: 'exception is thrown'
213             thrown(NcmpException.class)
214     }
215     def 'Get resource data for pass-through operational from dmi return NOK response.'() {
216         given: 'xpath'
217             def xpath = "/dmi-registry/cm-handles[@id='testCmHandle']"
218         and: 'data node'
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',
228                     'testCmHandle',
229                     'testResourceId',
230                     'testFieldQuery',
231                     5,
232                     'testAcceptParam',
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',
237                     'testResourceId',
238                     'testAcceptParam',
239                     'testFieldQuery',
240                     5)
241         then: 'exception is thrown'
242             thrown(NcmpException.class)
243     }
244     def 'Get resource data for pass-through running from dmi.'() {
245         given: 'xpath'
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',
258                     'testCmHandle',
259                     'testResourceId',
260                     'testFieldQuery',
261                     5,
262                     'testAcceptParam',
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',
266                     'testResourceId',
267                     'testAcceptParam',
268                     'testFieldQuery',
269                     5)
270         then: 'get resource data returns expected response'
271             response == '{result-json}'
272     }
273     def 'Get resource data for pass-through running from dmi threw parsing exception.'() {
274         given: 'xpath'
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',
291                     'testResourceId',
292                     'testAcceptParam',
293                     'testFieldQuery',
294                     5)
295         then: 'exception is thrown'
296             thrown(NcmpException.class)
297     }
298     def 'Get resource data for pass-through running from dmi return NOK response.'() {
299         given: 'xpath'
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',
312                     'testCmHandle',
313                     'testResourceId',
314                     'testFieldQuery',
315                     5,
316                     'testAcceptParam',
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',
321                     'testResourceId',
322                     'testAcceptParam',
323                     'testFieldQuery',
324                     5)
325         then: 'exception is thrown'
326             thrown(NcmpException.class)
327     }
328 }