88277d3e974753ddecb22e5de6eb15594510d722
[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.CpsAdminService
28 import org.onap.cps.api.CpsDataService
29 import org.onap.cps.api.CpsModuleService
30 import org.onap.cps.api.CpsQueryService
31 import org.onap.cps.ncmp.api.impl.config.NcmpConfiguration
32 import org.onap.cps.ncmp.api.impl.exception.NcmpException
33 import org.onap.cps.ncmp.api.impl.operation.DmiOperations
34 import org.onap.cps.ncmp.api.models.CmHandle
35 import org.onap.cps.ncmp.api.models.DmiPluginRegistration
36 import org.onap.cps.ncmp.api.models.PersistenceCmHandle
37 import org.onap.cps.ncmp.utils.TestUtils
38 import org.onap.cps.spi.FetchDescendantsOption
39 import org.onap.cps.spi.exceptions.DataNodeNotFoundException
40 import org.onap.cps.spi.exceptions.DataValidationException
41 import org.onap.cps.spi.model.DataNode
42 import org.onap.cps.spi.model.ModuleReference
43 import org.springframework.http.HttpStatus
44 import org.springframework.http.ResponseEntity
45 import spock.lang.Shared
46 import spock.lang.Specification
47
48 class NetworkCmProxyDataServiceImplSpec extends Specification {
49
50     @Shared
51     def persistenceCmHandle = new CmHandle()
52     @Shared
53     def cmHandlesArray = ['cmHandle001']
54
55     def mockCpsDataService = Mock(CpsDataService)
56     def mockCpsQueryService = Mock(CpsQueryService)
57     def mockDmiOperations = Mock(DmiOperations)
58     def mockCpsModuleService = Mock(CpsModuleService)
59     def mockCpsAdminService = Mock(CpsAdminService)
60     def mockDmiProperties = Mock(NcmpConfiguration.DmiProperties)
61     def spyObjectMapper = Spy(ObjectMapper)
62
63     def objectUnderTest = new NetworkCmProxyDataServiceImpl(mockDmiOperations, mockCpsModuleService,
64             mockCpsDataService, mockCpsQueryService, mockCpsAdminService, spyObjectMapper)
65
66     def cmHandle = 'some handle'
67     def noTimestamp = null
68     def cmHandleXPath = "/dmi-registry/cm-handles[@id='testCmHandle']"
69     def expectedDataspaceName = 'NFP-Operational'
70
71
72     def 'Get data node.'() {
73         when: 'queryDataNodes is invoked'
74             objectUnderTest.getDataNode(cmHandle, 'some xpath', fetchDescendantsOption)
75         then: 'the persistence data service is called once with the correct parameters'
76             1 * mockCpsDataService.getDataNode(expectedDataspaceName, cmHandle, 'some xpath', fetchDescendantsOption)
77         where: 'all fetch descendants options are supported'
78             fetchDescendantsOption << FetchDescendantsOption.values()
79     }
80
81     def 'Query data nodes by cps path with #fetchDescendantsOption.'() {
82         given: 'a cm Handle and a cps path'
83             def cpsPath = '/cps-path'
84         when: 'queryDataNodes is invoked'
85             objectUnderTest.queryDataNodes(cmHandle, cpsPath, fetchDescendantsOption)
86         then: 'the persistence query service is called once with the correct parameters'
87             1 * mockCpsQueryService.queryDataNodes(expectedDataspaceName, cmHandle, cpsPath, fetchDescendantsOption)
88         where: 'all fetch descendants options are supported'
89             fetchDescendantsOption << FetchDescendantsOption.values()
90     }
91
92     def 'Create full data node: #scenario.'() {
93         given: 'a cm handle and root xpath'
94             def jsonData = 'some json'
95         when: 'createDataNode is invoked'
96             objectUnderTest.createDataNode(cmHandle, xpath, jsonData)
97         then: 'the CPS service method is invoked once with the expected parameters'
98             1 * mockCpsDataService.saveData(expectedDataspaceName, cmHandle, jsonData, noTimestamp)
99         where: 'following parameters were used'
100             scenario           | xpath
101             'no xpath'         | ''
102             'root level xpath' | '/'
103     }
104
105     def 'Create child data node.'() {
106         given: 'a cm handle and parent node xpath'
107             def jsonData = 'some json'
108             def xpath = '/test-node'
109         when: 'createDataNode is invoked'
110             objectUnderTest.createDataNode(cmHandle, xpath, jsonData)
111         then: 'the CPS service method is invoked once with the expected parameters'
112             1 * mockCpsDataService.saveData(expectedDataspaceName, cmHandle, xpath, jsonData, noTimestamp)
113     }
114
115     def 'Add list-node elements.'() {
116         given: 'a cm handle and parent node xpath'
117             def jsonData = 'some json'
118             def xpath = '/test-node'
119         when: 'addListNodeElements is invoked'
120             objectUnderTest.addListNodeElements(cmHandle, xpath, jsonData)
121         then: 'the CPS service method is invoked once with the expected parameters'
122             1 * mockCpsDataService.saveListElements(expectedDataspaceName, cmHandle, xpath, jsonData, noTimestamp)
123     }
124
125     def 'Update data node leaves.'() {
126         given: 'a cm Handle and a cps path'
127             def xpath = '/xpath'
128             def jsonData = 'some json'
129         when: 'updateNodeLeaves is invoked'
130             objectUnderTest.updateNodeLeaves(cmHandle, xpath, jsonData)
131         then: 'the persistence service is called once with the correct parameters'
132             1 * mockCpsDataService.updateNodeLeaves(expectedDataspaceName, cmHandle, xpath, jsonData, noTimestamp)
133     }
134
135     def 'Replace data node tree.'() {
136         given: 'a cm Handle and a cps path'
137             def xpath = '/xpath'
138             def jsonData = 'some json'
139         when: 'replaceNodeTree is invoked'
140             objectUnderTest.replaceNodeTree(cmHandle, xpath, jsonData)
141         then: 'the persistence service is called once with the correct parameters'
142             1 * mockCpsDataService.replaceNodeTree(expectedDataspaceName, cmHandle, xpath, jsonData, noTimestamp)
143     }
144
145     def 'Register or re-register a DMI Plugin with #scenario cm handles.'() {
146         given: 'a registration '
147             def objectUnderTest = getObjectUnderTestWithModelSyncDisabled()
148             def dmiPluginRegistration = new DmiPluginRegistration()
149             dmiPluginRegistration.dmiPlugin = 'my-server'
150             persistenceCmHandle.cmHandleID = '123'
151             persistenceCmHandle.cmHandleProperties = [name1: 'value1', name2: 'value2']
152             dmiPluginRegistration.createdCmHandles = createdCmHandles
153             dmiPluginRegistration.updatedCmHandles = updatedCmHandles
154             dmiPluginRegistration.removedCmHandles = removedCmHandles
155             def expectedJsonData = '{"cm-handles":[{"id":"123","dmi-service-name":"my-server","additional-properties":[{"name":"name1","value":"value1"},{"name":"name2","value":"value2"}]}]}'
156         when: 'registration is updated'
157             objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
158         then: 'cps save list elements is invoked with the expected parameters'
159             expectedCallsToSaveNode * mockCpsDataService.saveListElements('NCMP-Admin', 'ncmp-dmi-registry',
160                 '/dmi-registry', expectedJsonData, noTimestamp)
161         and: 'update node and child data nodes is invoked with correct parameters'
162             expectedCallsToUpdateNode * mockCpsDataService.updateNodeLeavesAndExistingDescendantLeaves('NCMP-Admin',
163                 'ncmp-dmi-registry', '/dmi-registry', expectedJsonData, noTimestamp)
164         and : 'delete list or list element is invoked with the correct parameters'
165             expectedCallsToDeleteListElement * mockCpsDataService.deleteListOrListElement('NCMP-Admin',
166                 'ncmp-dmi-registry', "/dmi-registry/cm-handles[@id='cmHandle001']", noTimestamp)
167
168         where:
169             scenario                        | createdCmHandles      | updatedCmHandles      | removedCmHandles || expectedCallsToSaveNode   | expectedCallsToUpdateNode | expectedCallsToDeleteListElement
170             'create'                        | [persistenceCmHandle] | []                    | []               || 1                         | 0                         | 0
171             'update'                        | []                    | [persistenceCmHandle] | []               || 0                         | 1                         | 0
172             'delete'                        | []                    | []                    | cmHandlesArray   || 0                         | 0                         | 1
173             'create, update and delete'     | [persistenceCmHandle] | [persistenceCmHandle] | cmHandlesArray   || 1                         | 1                         | 1
174             'no valid data'                 | null                  | null                  |  null            || 0                         | 0                         | 0
175     }
176
177     def 'Register a DMI Plugin for the given cmHandle without additional properties.'() {
178         given: 'a registration without cmHandle properties '
179             NetworkCmProxyDataServiceImpl objectUnderTest = getObjectUnderTestWithModelSyncDisabled()
180             def dmiPluginRegistration = new DmiPluginRegistration()
181             dmiPluginRegistration.dmiPlugin = 'my-server'
182             persistenceCmHandle.cmHandleID = '123'
183             persistenceCmHandle.cmHandleProperties = null
184             dmiPluginRegistration.createdCmHandles = [persistenceCmHandle]
185             def expectedJsonData = '{"cm-handles":[{"id":"123","dmi-service-name":"my-server","additional-properties":[]}]}'
186         when: 'registration is updated'
187             objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
188         then: 'the cps save list element is invoked with the expected parameters'
189             1 * mockCpsDataService.saveListElements('NCMP-Admin', 'ncmp-dmi-registry',
190                 '/dmi-registry', expectedJsonData, noTimestamp)
191     }
192
193     def 'Register a DMI Plugin with JSON processing errors during #scenario.'() {
194         given: 'a registration without cmHandle properties '
195             NetworkCmProxyDataServiceImpl objectUnderTest = getObjectUnderTestWithModelSyncDisabled()
196             def dmiPluginRegistration = new DmiPluginRegistration()
197             dmiPluginRegistration.createdCmHandles = createdCmHandles
198             dmiPluginRegistration.updatedCmHandles = updatedCmHandles
199         and: 'an JSON processing exception occurs'
200             spyObjectMapper.writeValueAsString(_) >> { throw (new JsonProcessingException('')) }
201         when: 'registration is updated'
202             objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
203         then: 'a data validation exception is thrown'
204             thrown(DataValidationException)
205         where:
206             scenario | createdCmHandles      | updatedCmHandles
207             'create' | [persistenceCmHandle] | []
208             'update' | []                    | [persistenceCmHandle]
209     }
210
211     def 'Register a DMI Plugin with no data found during delete.'() {
212         given: 'a registration without cmHandle properties '
213             NetworkCmProxyDataServiceImpl objectUnderTest = getObjectUnderTestWithModelSyncDisabled()
214             def dmiPluginRegistration = new DmiPluginRegistration()
215             dmiPluginRegistration.removedCmHandles = ['some cm handle']
216         and: 'an JSON processing exception occurs'
217             mockCpsDataService.deleteListOrListElement(*_) >>  { throw (new DataNodeNotFoundException('','')) }
218         when: 'registration is updated'
219             objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
220         then: 'no exception is thrown'
221             noExceptionThrown()
222     }
223
224     def 'Get resource data for pass-through operational from dmi.'() {
225         given: 'data node representing cmHandle and its properties'
226             def cmHandleDataNode = getCmHandleDataNodeForTest(true)
227         and: 'data node is got from data service'
228             mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
229                 cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> cmHandleDataNode
230         and: 'resource data is got from DMI'
231             mockDmiOperations.getResourceDataOperationalFromDmi('testDmiService',
232                 'testCmHandle',
233                 'testResourceId',
234                 '(a=1,b=2)',
235                 'testAcceptParam',
236                 '{"operation":"read","cmHandleProperties":{"testName":"testValue"}}') >> new ResponseEntity<>('result-json', HttpStatus.OK)
237         when: 'get resource data is called'
238             def response = objectUnderTest.getResourceDataOperationalForCmHandle('testCmHandle',
239             'testResourceId',
240             'testAcceptParam',
241             '(a=1,b=2)')
242         then: 'dmi returns ok response'
243             response == 'result-json'
244     }
245
246     def 'Get resource data for pass-through operational from dmi threw parsing exception.'() {
247         given: 'data node representing cmHandle and its properties'
248             def cmHandleDataNode = getCmHandleDataNodeForTest(true)
249         and: 'cps data service returns valid cmHandle data node'
250             mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
251                     cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> cmHandleDataNode
252         and: 'objectMapper not able to parse object'
253             def mockObjectMapper = Mock(ObjectMapper)
254             objectUnderTest.objectMapper = mockObjectMapper
255             mockObjectMapper.writeValueAsString(_) >> { throw new JsonProcessingException('testException') }
256         when: 'get resource data is called'
257             def response = objectUnderTest.getResourceDataOperationalForCmHandle('testCmHandle',
258                     'testResourceId',
259                     'testAcceptParam',
260                     '(a=1,b=2)')
261         then: 'exception is thrown with the expected details'
262             def exceptionThrown = thrown(NcmpException.class)
263             exceptionThrown.details == 'testException'
264     }
265
266     def 'Get resource data for pass-through operational from dmi return NOK response.'() {
267         given: 'data node representing cmHandle and its properties'
268             def cmHandleDataNode = getCmHandleDataNodeForTest(true)
269         and: 'cps data service returns valid cmHandle data node'
270             mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
271                     cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> cmHandleDataNode
272         and: 'dmi returns NOK response'
273             mockDmiOperations.getResourceDataOperationalFromDmi('testDmiService',
274                     'testCmHandle',
275                     'testResourceId',
276                     '(a=1,b=2)',
277                     'testAcceptParam',
278                     '{"operation":"read","cmHandleProperties":{"testName":"testValue"}}')
279                     >> new ResponseEntity<>('NOK-json', HttpStatus.NOT_FOUND)
280         when: 'get resource data is called'
281             def response = objectUnderTest.getResourceDataOperationalForCmHandle('testCmHandle',
282                     'testResourceId',
283                     'testAcceptParam',
284                     '(a=1,b=2)')
285         then: 'exception is thrown'
286             def exceptionThrown = thrown(NcmpException.class)
287         and: 'details contains the original response'
288             exceptionThrown.details.contains('NOK-json')
289     }
290
291     def 'Get resource data for pass-through running from dmi.'() {
292         given: 'data node representing cmHandle and its properties'
293             def cmHandleDataNode = getCmHandleDataNodeForTest(true)
294         and: 'cpsDataService returns valid dataNode'
295             mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
296                     cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> cmHandleDataNode
297         and: 'dmi returns valid response and data'
298             mockDmiOperations.getResourceDataPassThroughRunningFromDmi('testDmiService',
299                     'testCmHandle',
300                     'testResourceId',
301                     '(a=1,b=2)',
302                     'testAcceptParam',
303                     '{"operation":"read","cmHandleProperties":{"testName":"testValue"}}') >> new ResponseEntity<>('{result-json}', HttpStatus.OK)
304         when: 'get resource data is called'
305             def response = objectUnderTest.getResourceDataPassThroughRunningForCmHandle('testCmHandle',
306                     'testResourceId',
307                     'testAcceptParam',
308                     '(a=1,b=2)')
309         then: 'get resource data returns expected response'
310             response == '{result-json}'
311     }
312
313     def 'Get resource data for pass-through running from dmi threw parsing exception.'() {
314         given: 'data node representing cmHandle and its properties'
315             def cmHandleDataNode = getCmHandleDataNodeForTest(true)
316         and: 'cpsDataService returns valid dataNode'
317             mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
318                     cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> cmHandleDataNode
319         and: 'objectMapper not able to parse object'
320             def mockObjectMapper = Mock(ObjectMapper)
321             objectUnderTest.objectMapper = mockObjectMapper
322             mockObjectMapper.writeValueAsString(_) >> { throw new JsonProcessingException('testException') }
323         when: 'get resource data is called'
324             def response = objectUnderTest.getResourceDataPassThroughRunningForCmHandle('testCmHandle',
325                     'testResourceId',
326                     'testAcceptParam',
327                     '(a=1,b=2)')
328         then: 'exception is thrown with the expected details'
329             def exceptionThrown = thrown(NcmpException.class)
330             exceptionThrown.details == 'testException'
331     }
332
333     def 'Get resource data for pass-through running from dmi return NOK response.'() {
334         given: 'data node representing cmHandle and its properties'
335             def cmHandleDataNode = getCmHandleDataNodeForTest(true)
336         and: 'cpsDataService returns valid dataNode'
337             mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
338                     cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> cmHandleDataNode
339         and: 'dmi returns NOK response'
340             mockDmiOperations.getResourceDataPassThroughRunningFromDmi('testDmiService',
341                     'testCmHandle',
342                     'testResourceId',
343                     '(a=1,b=2)',
344                     'testAcceptParam',
345                     '{"operation":"read","cmHandleProperties":{"testName":"testValue"}}')
346                     >> new ResponseEntity<>('NOK-json', HttpStatus.NOT_FOUND)
347         when: 'get resource data is called'
348             def response = objectUnderTest.getResourceDataPassThroughRunningForCmHandle('testCmHandle',
349                     'testResourceId',
350                     'testAcceptParam',
351                     '(a=1,b=2)')
352         then: 'exception is thrown'
353             def exceptionThrown = thrown(NcmpException.class)
354         and: 'details contains the original response'
355             exceptionThrown.details.contains('NOK-json')
356     }
357
358     def 'Write resource data for pass-through running from dmi using POST #scenario cm handle properties.'() {
359         given: 'data node representing cmHandle #scenario cm handle properties'
360             def cmHandleDataNode = getCmHandleDataNodeForTest(includeCmHandleProperties)
361         and: 'cpsDataService returns valid cm-handle datanode'
362             mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
363                     cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> cmHandleDataNode
364         when: 'get resource data is called'
365             objectUnderTest.createResourceDataPassThroughRunningForCmHandle('testCmHandle',
366                     'testResourceId',
367                     '{some-json}', 'application/json')
368         then: 'dmi called with correct data'
369             1 * mockDmiOperations.createResourceDataPassThroughRunningFromDmi('testDmiService',
370                 'testCmHandle',
371                 'testResourceId',
372                 '{"operation":"create","dataType":"application/json","data":"{some-json}","cmHandleProperties":'
373                 + expectedJsonForCmhandleProperties+ '}')
374                 >> { new ResponseEntity<>(HttpStatus.CREATED) }
375         where:
376             scenario  | includeCmHandleProperties || expectedJsonForCmhandleProperties
377             'with'    | true                      || '{"testName":"testValue"}'
378             'without' | false                     || '{}'
379     }
380
381     def 'Write resource data for pass-through running from dmi using POST "not found" response (from DMI).'() {
382         given: 'data node representing cmHandle and its properties'
383             def cmHandleDataNode = getCmHandleDataNodeForTest(true)
384         and: 'cpsDataService returns valid dataNode'
385             mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
386                     cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> cmHandleDataNode
387         and: 'dmi throws exception'
388             mockDmiOperations.createResourceDataPassThroughRunningFromDmi(_ as String, _ as String, _ as String, _ as String)
389                     >> { new ResponseEntity<>(HttpStatus.NOT_FOUND) }
390         when: 'get resource data is called'
391             objectUnderTest.createResourceDataPassThroughRunningForCmHandle('testCmHandle',
392                     'testResourceId',
393                     '{some-json}', 'application/json')
394         then: 'exception is thrown'
395             def exceptionThrown = thrown(NcmpException.class)
396         and: 'details contains (not found) error code: 404'
397             exceptionThrown.details.contains('404')
398     }
399
400     def 'Sync model for a (new) cm handle with #scenario'() {
401         given: 'persistence cm handle is given'
402             def cmHandleForModelSync = new PersistenceCmHandle(id:'some cm handle', dmiServiceName: 'some service name')
403         and: 'additional properties are set as required'
404             if (additionalProperties!=null) {
405                 cmHandleForModelSync.setAdditionalProperties(additionalProperties)
406             }
407         and: 'dmi operations returns some module references'
408             def jsonData = TestUtils.getResourceFileContent('cmHandleModules.json')
409             def expectedJsonBody = '{"cmHandleProperties":' + expectedJsonForAdditionalProperties + '}'
410             mockDmiProperties.getAuthUsername() >> 'someUser'
411             mockDmiProperties.getAuthPassword() >> 'somePassword'
412             def moduleReferencesFromCmHandleAsJson = new ResponseEntity<String>(jsonData, HttpStatus.OK)
413             mockDmiOperations.getResourceFromDmiWithJsonData('some service name', expectedJsonBody, 'some cm handle', 'modules') >> moduleReferencesFromCmHandleAsJson
414         and: 'CPS-Core returns list of known modules'
415             mockCpsModuleService.getYangResourceModuleReferences(_) >> existingModuleResourcesInCps
416         and: 'DMI-Plugin returns resource(s) for "new" module(s)'
417             def moduleResources = new ResponseEntity<String>(sdncReponseBody, HttpStatus.OK)
418             def jsonDataToFetchYangResource = '{"data":{"modules":[{"name":"module1","revision":"1"}]},"cmHandleProperties":' + expectedJsonForAdditionalProperties + '}'
419             mockDmiOperations.getResourceFromDmiWithJsonData('some service name', jsonDataToFetchYangResource, 'some cm handle', 'moduleResources') >> moduleResources
420         when: 'module Sync is triggered'
421             objectUnderTest.syncModulesAndCreateAnchor(cmHandleForModelSync)
422         then: 'the CPS module service is called once with the correct parameters'
423             1 * mockCpsModuleService.createSchemaSetFromModules(expectedDataspaceName, cmHandleForModelSync.getId(), expectedYangResourceToContentMap, expectedKnownModules)
424         and: 'admin service create anchor method has been called with correct parameters'
425             1 * mockCpsAdminService.createAnchor(expectedDataspaceName, cmHandleForModelSync.getId(), cmHandleForModelSync.getId())
426         where: 'the following responses are received from SDNC'
427             scenario                         | additionalProperties | existingModuleResourcesInCps                                                  | sdncReponseBody                                                                     || expectedYangResourceToContentMap | expectedKnownModules                                                       | expectedJsonForAdditionalProperties
428             'one unknown module'             | ['name1':'value1']   | [new ModuleReference('module2', '2'), new ModuleReference('module3', '3')]    | '[{"moduleName" : "module1", "revision" : "1","yangSource": "[some yang source]"}]' || [module1: 'some yang source']    | [new ModuleReference('module2', '2')]                                      |'{"name1":"value1"}'
429             'no add. properties'             | [:]                  | [new ModuleReference('module2', '2'), new ModuleReference('module3', '3')]    | '[{"moduleName" : "module1", "revision" : "1","yangSource": "[some yang source]"}]' || [module1: 'some yang source']    | [new ModuleReference('module2', '2')]                                      |'{}'
430             'additional properties is null'  | null                 | [new ModuleReference('module2', '2'), new ModuleReference('module3', '3')]    | '[{"moduleName" : "module1", "revision" : "1","yangSource": "[some yang source]"}]' || [module1: 'some yang source']    | [new ModuleReference('module2', '2')]                                      |'{}'
431             'no unknown module'              | [:]                  | [new ModuleReference('module1', '1'),    new ModuleReference('module2', '2')] | '[]'                                                                                || [:]                              | [new ModuleReference('module1', '1'), new ModuleReference('module2', '2')] |'{}'
432     }
433
434     def 'Getting Yang Resources.'() {
435         when: 'yang resources is called'
436             objectUnderTest.getYangResourcesModuleReferences('some cm handle')
437         then: 'CPS module services is invoked for the correct dataspace and cm handle'
438             1 * mockCpsModuleService.getYangResourcesModuleReferences('NFP-Operational','some cm handle')
439     }
440
441     def 'Create the request body to get yang resources from DMI.'() {
442         given: 'the expected json request'
443             def expectedRequestBody = '{"data":{"modules":[{"name":"module1","revision":"1"},{"name":"module2","revision":"2"}]},"cmHandleProperties":{"name1":"value1"}}'
444         and: 'module references and cm handle properties'
445             def moduleReferences = [new ModuleReference('module1', '1'),new ModuleReference('module2', '2')]
446             def cmHandleProperties = ['name1':'value1']
447         when: 'get request body to fetch yang resources from DMI is called'
448             def result = objectUnderTest.getRequestBodyToFetchYangResourceFromDmi(moduleReferences, cmHandleProperties)
449         then: 'the result is the same as the expected request body'
450             result == expectedRequestBody
451     }
452
453     def 'Get cm handle identifiers for the given module names.'() {
454         when: 'execute a cm handle search for the given module names'
455             objectUnderTest.executeCmHandleHasAllModulesSearch(['some-module-name'])
456         then: 'get anchor identifiers is invoked  with the expected parameters'
457             1 * mockCpsAdminService.queryAnchorNames('NFP-Operational', ['some-module-name'])
458     }
459
460     def getObjectUnderTestWithModelSyncDisabled() {
461         def objectUnderTest = Spy(new NetworkCmProxyDataServiceImpl(mockDmiOperations, mockCpsModuleService,
462                 mockCpsDataService, mockCpsQueryService, mockCpsAdminService, spyObjectMapper))
463         objectUnderTest.syncModulesAndCreateAnchor(_) >> null
464         return objectUnderTest
465     }
466
467     def getCmHandleDataNodeForTest(boolean includeCmHandleProperties) {
468         def cmHandleDataNode = new DataNode()
469         cmHandleDataNode.leaves = ['dmi-service-name': 'testDmiService']
470         if (includeCmHandleProperties) {
471             def cmHandlePropertyDataNode = new DataNode()
472             cmHandlePropertyDataNode.leaves = ['name': 'testName', 'value': 'testValue']
473             cmHandleDataNode.childDataNodes = [cmHandlePropertyDataNode]
474         }
475         return cmHandleDataNode
476     }
477
478 }