aboutsummaryrefslogtreecommitdiffstats
path: root/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplSpec.groovy
blob: 1bad8ce0bac33921d59901d759271c3166d8974a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
/*
 * ============LICENSE_START=======================================================
 *  Copyright (C) 2021 Nordix Foundation
 *  Modifications Copyright (C) 2021 Pantheon.tech
 *  Modifications Copyright (C) 2021 Bell Canada
 *  ================================================================================
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 *  SPDX-License-Identifier: Apache-2.0
 *  ============LICENSE_END=========================================================
 */

package org.onap.cps.ncmp.api.impl

import com.fasterxml.jackson.core.JsonProcessingException
import com.fasterxml.jackson.databind.ObjectMapper
import org.onap.cps.api.CpsAdminService
import org.onap.cps.api.CpsDataService
import org.onap.cps.api.CpsModuleService
import org.onap.cps.api.CpsQueryService
import org.onap.cps.ncmp.api.impl.config.NcmpConfiguration
import org.onap.cps.ncmp.api.impl.exception.NcmpException
import org.onap.cps.ncmp.api.impl.operation.DmiOperations
import org.onap.cps.ncmp.api.models.CmHandle
import org.onap.cps.ncmp.api.models.DmiPluginRegistration
import org.onap.cps.ncmp.api.models.PersistenceCmHandle
import org.onap.cps.ncmp.utils.TestUtils
import org.onap.cps.spi.FetchDescendantsOption
import org.onap.cps.spi.exceptions.DataNodeNotFoundException
import org.onap.cps.spi.exceptions.DataValidationException
import org.onap.cps.spi.model.DataNode
import org.onap.cps.spi.model.ModuleReference
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import spock.lang.Shared
import spock.lang.Specification

class NetworkCmProxyDataServiceImplSpec extends Specification {

    @Shared
    def persistenceCmHandle = new CmHandle()
    @Shared
    def cmHandlesArray = ['cmHandle001']

    def mockCpsDataService = Mock(CpsDataService)
    def mockCpsQueryService = Mock(CpsQueryService)
    def mockDmiOperations = Mock(DmiOperations)
    def mockCpsModuleService = Mock(CpsModuleService)
    def mockCpsAdminService = Mock(CpsAdminService)
    def mockDmiProperties = Mock(NcmpConfiguration.DmiProperties)
    def spyObjectMapper = Spy(ObjectMapper)

    def objectUnderTest = new NetworkCmProxyDataServiceImpl(mockDmiOperations, mockCpsModuleService,
            mockCpsDataService, mockCpsQueryService, mockCpsAdminService, spyObjectMapper)

    def cmHandle = 'some handle'
    def noTimestamp = null
    def cmHandleXPath = "/dmi-registry/cm-handles[@id='testCmHandle']"
    def expectedDataspaceName = 'NFP-Operational'


    def 'Get data node.'() {
        when: 'queryDataNodes is invoked'
            objectUnderTest.getDataNode(cmHandle, 'some xpath', fetchDescendantsOption)
        then: 'the persistence data service is called once with the correct parameters'
            1 * mockCpsDataService.getDataNode(expectedDataspaceName, cmHandle, 'some xpath', fetchDescendantsOption)
        where: 'all fetch descendants options are supported'
            fetchDescendantsOption << FetchDescendantsOption.values()
    }

    def 'Query data nodes by cps path with #fetchDescendantsOption.'() {
        given: 'a cm Handle and a cps path'
            def cpsPath = '/cps-path'
        when: 'queryDataNodes is invoked'
            objectUnderTest.queryDataNodes(cmHandle, cpsPath, fetchDescendantsOption)
        then: 'the persistence query service is called once with the correct parameters'
            1 * mockCpsQueryService.queryDataNodes(expectedDataspaceName, cmHandle, cpsPath, fetchDescendantsOption)
        where: 'all fetch descendants options are supported'
            fetchDescendantsOption << FetchDescendantsOption.values()
    }

    def 'Create full data node: #scenario.'() {
        given: 'a cm handle and root xpath'
            def jsonData = 'some json'
        when: 'createDataNode is invoked'
            objectUnderTest.createDataNode(cmHandle, xpath, jsonData)
        then: 'the CPS service method is invoked once with the expected parameters'
            1 * mockCpsDataService.saveData(expectedDataspaceName, cmHandle, jsonData, noTimestamp)
        where: 'following parameters were used'
            scenario           | xpath
            'no xpath'         | ''
            'root level xpath' | '/'
    }

    def 'Create child data node.'() {
        given: 'a cm handle and parent node xpath'
            def jsonData = 'some json'
            def xpath = '/test-node'
        when: 'createDataNode is invoked'
            objectUnderTest.createDataNode(cmHandle, xpath, jsonData)
        then: 'the CPS service method is invoked once with the expected parameters'
            1 * mockCpsDataService.saveData(expectedDataspaceName, cmHandle, xpath, jsonData, noTimestamp)
    }

    def 'Add list-node elements.'() {
        given: 'a cm handle and parent node xpath'
            def jsonData = 'some json'
            def xpath = '/test-node'
        when: 'addListNodeElements is invoked'
            objectUnderTest.addListNodeElements(cmHandle, xpath, jsonData)
        then: 'the CPS service method is invoked once with the expected parameters'
            1 * mockCpsDataService.saveListNodeData(expectedDataspaceName, cmHandle, xpath, jsonData, noTimestamp)
    }

    def 'Update data node leaves.'() {
        given: 'a cm Handle and a cps path'
            def xpath = '/xpath'
            def jsonData = 'some json'
        when: 'updateNodeLeaves is invoked'
            objectUnderTest.updateNodeLeaves(cmHandle, xpath, jsonData)
        then: 'the persistence service is called once with the correct parameters'
            1 * mockCpsDataService.updateNodeLeaves(expectedDataspaceName, cmHandle, xpath, jsonData, noTimestamp)
    }

    def 'Replace data node tree.'() {
        given: 'a cm Handle and a cps path'
            def xpath = '/xpath'
            def jsonData = 'some json'
        when: 'replaceNodeTree is invoked'
            objectUnderTest.replaceNodeTree(cmHandle, xpath, jsonData)
        then: 'the persistence service is called once with the correct parameters'
            1 * mockCpsDataService.replaceNodeTree(expectedDataspaceName, cmHandle, xpath, jsonData, noTimestamp)
    }

    def 'Register or re-register a DMI Plugin with #scenario cm handles.'() {
        given: 'a registration '
            def objectUnderTest = getObjectUnderTestWithModelSyncDisabled()
            def dmiPluginRegistration = new DmiPluginRegistration()
            dmiPluginRegistration.dmiPlugin = 'my-server'
            persistenceCmHandle.cmHandleID = '123'
            persistenceCmHandle.cmHandleProperties = [name1: 'value1', name2: 'value2']
            dmiPluginRegistration.createdCmHandles = createdCmHandles
            dmiPluginRegistration.updatedCmHandles = updatedCmHandles
            dmiPluginRegistration.removedCmHandles = removedCmHandles
            def expectedJsonData = '{"cm-handles":[{"id":"123","dmi-service-name":"my-server","additional-properties":[{"name":"name1","value":"value1"},{"name":"name2","value":"value2"}]}]}'
        when: 'registration is updated'
            objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
        then: 'the CPS save list node data is invoked with the expected parameters'
            expectedCallsToSaveNode * mockCpsDataService.saveListNodeData('NCMP-Admin', 'ncmp-dmi-registry',
                '/dmi-registry', expectedJsonData, noTimestamp)
        and: 'update Node and Child Data Nodes is invoked with correct parameters'
            expectedCallsToUpdateNode * mockCpsDataService.updateNodeLeavesAndExistingDescendantLeaves('NCMP-Admin',
                'ncmp-dmi-registry', '/dmi-registry', expectedJsonData, noTimestamp)
        and : 'delete list data node is invoked with the correct parameters'
            expectedCallsToDeleteListDataNode * mockCpsDataService.deleteListNodeData('NCMP-Admin',
                'ncmp-dmi-registry', "/dmi-registry/cm-handles[@id='cmHandle001']", noTimestamp)

        where:
            scenario                        | createdCmHandles      | updatedCmHandles      | removedCmHandles || expectedCallsToSaveNode   | expectedCallsToUpdateNode | expectedCallsToDeleteListDataNode
            'create'                        | [persistenceCmHandle] | []                    | []               || 1                         | 0                         | 0
            'update'                        | []                    | [persistenceCmHandle] | []               || 0                         | 1                         | 0
            'delete'                        | []                    | []                    | cmHandlesArray   || 0                         | 0                         | 1
            'create, update and delete'     | [persistenceCmHandle] | [persistenceCmHandle] | cmHandlesArray   || 1                         | 1                         | 1
            'no valid data'                 | null                  | null                  |  null            || 0                         | 0                         | 0
    }

    def 'Register a DMI Plugin for the given cmHandle without additional properties.'() {
        given: 'a registration without cmHandle properties '
            NetworkCmProxyDataServiceImpl objectUnderTest = getObjectUnderTestWithModelSyncDisabled()
            def dmiPluginRegistration = new DmiPluginRegistration()
            dmiPluginRegistration.dmiPlugin = 'my-server'
            persistenceCmHandle.cmHandleID = '123'
            persistenceCmHandle.cmHandleProperties = null
            dmiPluginRegistration.createdCmHandles = [persistenceCmHandle]
            def expectedJsonData = '{"cm-handles":[{"id":"123","dmi-service-name":"my-server","additional-properties":[]}]}'
        when: 'registration is updated'
            objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
        then: 'the CPS save list node data is invoked with the expected parameters'
            1 * mockCpsDataService.saveListNodeData('NCMP-Admin', 'ncmp-dmi-registry',
                '/dmi-registry', expectedJsonData, noTimestamp)
    }

    def 'Register a DMI Plugin with JSON processing errors during #scenario.'() {
        given: 'a registration without cmHandle properties '
            NetworkCmProxyDataServiceImpl objectUnderTest = getObjectUnderTestWithModelSyncDisabled()
            def dmiPluginRegistration = new DmiPluginRegistration()
            dmiPluginRegistration.createdCmHandles = createdCmHandles
            dmiPluginRegistration.updatedCmHandles = updatedCmHandles
        and: 'an JSON processing exception occurs'
            spyObjectMapper.writeValueAsString(_) >> { throw (new JsonProcessingException('')) }
        when: 'registration is updated'
            objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
        then: 'a data validation exception is thrown'
            thrown(DataValidationException)
        where:
            scenario | createdCmHandles      | updatedCmHandles
            'create' | [persistenceCmHandle] | []
            'update' | []                    | [persistenceCmHandle]
    }

    def 'Register a DMI Plugin with no data found during delete.'() {
        given: 'a registration without cmHandle properties '
            NetworkCmProxyDataServiceImpl objectUnderTest = getObjectUnderTestWithModelSyncDisabled()
            def dmiPluginRegistration = new DmiPluginRegistration()
            dmiPluginRegistration.removedCmHandles = ['some cm handle']
        and: 'an JSON processing exception occurs'
            mockCpsDataService.deleteListNodeData(*_) >>  { throw (new DataNodeNotFoundException('','')) }
        when: 'registration is updated'
            objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
        then: 'no exception is thrown'
            noExceptionThrown()
    }

    def 'Get resource data for pass-through operational from dmi.'() {
        given: 'data node representing cmHandle and its properties'
            def cmHandleDataNode = getCmHandleDataNodeForTest(true)
        and: 'data node is got from data service'
            mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
                cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> cmHandleDataNode
        and: 'resource data is got from DMI'
            mockDmiOperations.getResourceDataOperationalFromDmi('testDmiService',
                'testCmHandle',
                'testResourceId',
                '(a=1,b=2)',
                'testAcceptParam',
                '{"operation":"read","cmHandleProperties":{"testName":"testValue"}}') >> new ResponseEntity<>('result-json', HttpStatus.OK)
        when: 'get resource data is called'
            def response = objectUnderTest.getResourceDataOperationalForCmHandle('testCmHandle',
            'testResourceId',
            'testAcceptParam',
            '(a=1,b=2)')
        then: 'dmi returns ok response'
            response == 'result-json'
    }

    def 'Get resource data for pass-through operational from dmi threw parsing exception.'() {
        given: 'data node representing cmHandle and its properties'
            def cmHandleDataNode = getCmHandleDataNodeForTest(true)
        and: 'cps data service returns valid cmHandle data node'
            mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
                    cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> cmHandleDataNode
        and: 'objectMapper not able to parse object'
            def mockObjectMapper = Mock(ObjectMapper)
            objectUnderTest.objectMapper = mockObjectMapper
            mockObjectMapper.writeValueAsString(_) >> { throw new JsonProcessingException('testException') }
        when: 'get resource data is called'
            def response = objectUnderTest.getResourceDataOperationalForCmHandle('testCmHandle',
                    'testResourceId',
                    'testAcceptParam',
                    '(a=1,b=2)')
        then: 'exception is thrown with the expected details'
            def exceptionThrown = thrown(NcmpException.class)
            exceptionThrown.details == 'testException'
    }

    def 'Get resource data for pass-through operational from dmi return NOK response.'() {
        given: 'data node representing cmHandle and its properties'
            def cmHandleDataNode = getCmHandleDataNodeForTest(true)
        and: 'cps data service returns valid cmHandle data node'
            mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
                    cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> cmHandleDataNode
        and: 'dmi returns NOK response'
            mockDmiOperations.getResourceDataOperationalFromDmi('testDmiService',
                    'testCmHandle',
                    'testResourceId',
                    '(a=1,b=2)',
                    'testAcceptParam',
                    '{"operation":"read","cmHandleProperties":{"testName":"testValue"}}')
                    >> new ResponseEntity<>('NOK-json', HttpStatus.NOT_FOUND)
        when: 'get resource data is called'
            def response = objectUnderTest.getResourceDataOperationalForCmHandle('testCmHandle',
                    'testResourceId',
                    'testAcceptParam',
                    '(a=1,b=2)')
        then: 'exception is thrown'
            def exceptionThrown = thrown(NcmpException.class)
        and: 'details contains the original response'
            exceptionThrown.details.contains('NOK-json')
    }

    def 'Get resource data for pass-through running from dmi.'() {
        given: 'data node representing cmHandle and its properties'
            def cmHandleDataNode = getCmHandleDataNodeForTest(true)
        and: 'cpsDataService returns valid dataNode'
            mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
                    cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> cmHandleDataNode
        and: 'dmi returns valid response and data'
            mockDmiOperations.getResourceDataPassThroughRunningFromDmi('testDmiService',
                    'testCmHandle',
                    'testResourceId',
                    '(a=1,b=2)',
                    'testAcceptParam',
                    '{"operation":"read","cmHandleProperties":{"testName":"testValue"}}') >> new ResponseEntity<>('{result-json}', HttpStatus.OK)
        when: 'get resource data is called'
            def response = objectUnderTest.getResourceDataPassThroughRunningForCmHandle('testCmHandle',
                    'testResourceId',
                    'testAcceptParam',
                    '(a=1,b=2)')
        then: 'get resource data returns expected response'
            response == '{result-json}'
    }

    def 'Get resource data for pass-through running from dmi threw parsing exception.'() {
        given: 'data node representing cmHandle and its properties'
            def cmHandleDataNode = getCmHandleDataNodeForTest(true)
        and: 'cpsDataService returns valid dataNode'
            mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
                    cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> cmHandleDataNode
        and: 'objectMapper not able to parse object'
            def mockObjectMapper = Mock(ObjectMapper)
            objectUnderTest.objectMapper = mockObjectMapper
            mockObjectMapper.writeValueAsString(_) >> { throw new JsonProcessingException('testException') }
        when: 'get resource data is called'
            def response = objectUnderTest.getResourceDataPassThroughRunningForCmHandle('testCmHandle',
                    'testResourceId',
                    'testAcceptParam',
                    '(a=1,b=2)')
        then: 'exception is thrown with the expected details'
            def exceptionThrown = thrown(NcmpException.class)
            exceptionThrown.details == 'testException'
    }

    def 'Get resource data for pass-through running from dmi return NOK response.'() {
        given: 'data node representing cmHandle and its properties'
            def cmHandleDataNode = getCmHandleDataNodeForTest(true)
        and: 'cpsDataService returns valid dataNode'
            mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
                    cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> cmHandleDataNode
        and: 'dmi returns NOK response'
            mockDmiOperations.getResourceDataPassThroughRunningFromDmi('testDmiService',
                    'testCmHandle',
                    'testResourceId',
                    '(a=1,b=2)',
                    'testAcceptParam',
                    '{"operation":"read","cmHandleProperties":{"testName":"testValue"}}')
                    >> new ResponseEntity<>('NOK-json', HttpStatus.NOT_FOUND)
        when: 'get resource data is called'
            def response = objectUnderTest.getResourceDataPassThroughRunningForCmHandle('testCmHandle',
                    'testResourceId',
                    'testAcceptParam',
                    '(a=1,b=2)')
        then: 'exception is thrown'
            def exceptionThrown = thrown(NcmpException.class)
        and: 'details contains the original response'
            exceptionThrown.details.contains('NOK-json')
    }

    def 'Write resource data for pass-through running from dmi using POST #scenario cm handle properties.'() {
        given: 'data node representing cmHandle #scenario cm handle properties'
            def cmHandleDataNode = getCmHandleDataNodeForTest(includeCmHandleProperties)
        and: 'cpsDataService returns valid cm-handle datanode'
            mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
                    cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> cmHandleDataNode
        when: 'get resource data is called'
            objectUnderTest.createResourceDataPassThroughRunningForCmHandle('testCmHandle',
                    'testResourceId',
                    '{some-json}', 'application/json')
        then: 'dmi called with correct data'
            1 * mockDmiOperations.createResourceDataPassThroughRunningFromDmi('testDmiService',
                'testCmHandle',
                'testResourceId',
                '{"operation":"create","dataType":"application/json","data":"{some-json}","cmHandleProperties":'
                + expectedJsonForCmhandleProperties+ '}')
                >> { new ResponseEntity<>(HttpStatus.CREATED) }
        where:
            scenario  | includeCmHandleProperties || expectedJsonForCmhandleProperties
            'with'    | true                      || '{"testName":"testValue"}'
            'without' | false                     || '{}'
    }

    def 'Write resource data for pass-through running from dmi using POST "not found" response (from DMI).'() {
        given: 'data node representing cmHandle and its properties'
            def cmHandleDataNode = getCmHandleDataNodeForTest(true)
        and: 'cpsDataService returns valid dataNode'
            mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
                    cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> cmHandleDataNode
        and: 'dmi throws exception'
            mockDmiOperations.createResourceDataPassThroughRunningFromDmi(_ as String, _ as String, _ as String, _ as String)
                    >> { new ResponseEntity<>(HttpStatus.NOT_FOUND) }
        when: 'get resource data is called'
            objectUnderTest.createResourceDataPassThroughRunningForCmHandle('testCmHandle',
                    'testResourceId',
                    '{some-json}', 'application/json')
        then: 'exception is thrown'
            def exceptionThrown = thrown(NcmpException.class)
        and: 'details contains (not found) error code: 404'
            exceptionThrown.details.contains('404')
    }

    def 'Sync model for a (new) cm handle with #scenario'() {
        given: 'persistence cm handle is given'
            def cmHandleForModelSync = new PersistenceCmHandle(id:'some cm handle', dmiServiceName: 'some service name')
        and: 'additional properties are set as required'
            if (additionalProperties!=null) {
                cmHandleForModelSync.setAdditionalProperties(additionalProperties)
            }
        and: 'dmi operations returns some module references'
            def jsonData = TestUtils.getResourceFileContent('cmHandleModules.json')
            def expectedJsonBody = '{"cmHandleProperties":' + expectedJsonForAdditionalProperties + '}'
            mockDmiProperties.getAuthUsername() >> 'someUser'
            mockDmiProperties.getAuthPassword() >> 'somePassword'
            def moduleReferencesFromCmHandleAsJson = new ResponseEntity<String>(jsonData, HttpStatus.OK)
            mockDmiOperations.getResourceFromDmiWithJsonData('some service name', expectedJsonBody, 'some cm handle', 'modules') >> moduleReferencesFromCmHandleAsJson
        and: 'CPS-Core returns list of known modules'
            mockCpsModuleService.getYangResourceModuleReferences(_) >> existingModuleResourcesInCps
        and: 'DMI-Plugin returns resource(s) for "new" module(s)'
            def moduleResources = new ResponseEntity<String>(sdncReponseBody, HttpStatus.OK)
            def jsonDataToFetchYangResource = '{"data":{"modules":[{"name":"module1","revision":"1"}]},"cmHandleProperties":' + expectedJsonForAdditionalProperties + '}'
            mockDmiOperations.getResourceFromDmiWithJsonData('some service name', jsonDataToFetchYangResource, 'some cm handle', 'moduleResources') >> moduleResources
        when: 'module Sync is triggered'
            objectUnderTest.syncModulesAndCreateAnchor(cmHandleForModelSync)
        then: 'the CPS module service is called once with the correct parameters'
            1 * mockCpsModuleService.createSchemaSetFromModules(expectedDataspaceName, cmHandleForModelSync.getId(), expectedYangResourceToContentMap, expectedKnownModules)
        and: 'admin service create anchor method has been called with correct parameters'
            1 * mockCpsAdminService.createAnchor(expectedDataspaceName, cmHandleForModelSync.getId(), cmHandleForModelSync.getId())
        where: 'the following responses are received from SDNC'
            scenario                         | additionalProperties | existingModuleResourcesInCps                                                  | sdncReponseBody                                                                     || expectedYangResourceToContentMap | expectedKnownModules                                                       | expectedJsonForAdditionalProperties
            '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"}'
            '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')]                                      |'{}'
            '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')]                                      |'{}'
            'no unknown module'              | [:]                  | [new ModuleReference('module1', '1'),    new ModuleReference('module2', '2')] | '[]'                                                                                || [:]                              | [new ModuleReference('module1', '1'), new ModuleReference('module2', '2')] |'{}'
    }

    def 'Getting Yang Resources.'() {
        when: 'yang resources is called'
            objectUnderTest.getYangResourcesModuleReferences('some cm handle')
        then: 'CPS module services is invoked for the correct dataspace and cm handle'
            1 * mockCpsModuleService.getYangResourcesModuleReferences('NFP-Operational','some cm handle')
    }

    def 'Create the request body to get yang resources from DMI.'() {
        given: 'the expected json request'
            def expectedRequestBody = '{"data":{"modules":[{"name":"module1","revision":"1"},{"name":"module2","revision":"2"}]},"cmHandleProperties":{"name1":"value1"}}'
        and: 'module references and cm handle properties'
            def moduleReferences = [new ModuleReference('module1', '1'),new ModuleReference('module2', '2')]
            def cmHandleProperties = ['name1':'value1']
        when: 'get request body to fetch yang resources from DMI is called'
            def result = objectUnderTest.getRequestBodyToFetchYangResourceFromDmi(moduleReferences, cmHandleProperties)
        then: 'the result is the same as the expected request body'
            result == expectedRequestBody
    }

    def getObjectUnderTestWithModelSyncDisabled() {
        def objectUnderTest = Spy(new NetworkCmProxyDataServiceImpl(mockDmiOperations, mockCpsModuleService,
                mockCpsDataService, mockCpsQueryService, mockCpsAdminService, spyObjectMapper))
        objectUnderTest.syncModulesAndCreateAnchor(_) >> null
        return objectUnderTest
    }

    def getCmHandleDataNodeForTest(boolean includeCmHandleProperties) {
        def cmHandleDataNode = new DataNode()
        cmHandleDataNode.leaves = ['dmi-service-name': 'testDmiService']
        if (includeCmHandleProperties) {
            def cmHandlePropertyDataNode = new DataNode()
            cmHandlePropertyDataNode.leaves = ['name': 'testName', 'value': 'testValue']
            cmHandleDataNode.childDataNodes = [cmHandlePropertyDataNode]
        }
        return cmHandleDataNode
    }

}