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
 
  11  *        http://www.apache.org/licenses/LICENSE-2.0
 
  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.
 
  19  *  SPDX-License-Identifier: Apache-2.0
 
  20  *  ============LICENSE_END=========================================================
 
  23 package org.onap.cps.ncmp.api.impl
 
  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
 
  48 class NetworkCmProxyDataServiceImplSpec extends Specification {
 
  51     def persistenceCmHandle = new CmHandle()
 
  53     def cmHandlesArray = ['cmHandle001']
 
  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)
 
  63     def objectUnderTest = new NetworkCmProxyDataServiceImpl(mockDmiOperations, mockCpsModuleService,
 
  64             mockCpsDataService, mockCpsQueryService, mockCpsAdminService, spyObjectMapper)
 
  66     def cmHandle = 'some handle'
 
  67     def noTimestamp = null
 
  68     def cmHandleXPath = "/dmi-registry/cm-handles[@id='testCmHandle']"
 
  69     def expectedDataspaceName = 'NFP-Operational'
 
  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()
 
  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()
 
  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'
 
 102             'root level xpath' | '/'
 
 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)
 
 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)
 
 125     def 'Update data node leaves.'() {
 
 126         given: 'a cm Handle and a cps path'
 
 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)
 
 135     def 'Replace data node tree.'() {
 
 136         given: 'a cm Handle and a cps path'
 
 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)
 
 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)
 
 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
 
 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)
 
 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)
 
 206             scenario | createdCmHandles      | updatedCmHandles
 
 207             'create' | [persistenceCmHandle] | []
 
 208             'update' | []                    | [persistenceCmHandle]
 
 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'
 
 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',
 
 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',
 
 242         then: 'dmi returns ok response'
 
 243             response == 'result-json'
 
 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',
 
 261         then: 'exception is thrown with the expected details'
 
 262             def exceptionThrown = thrown(NcmpException.class)
 
 263             exceptionThrown.details == 'testException'
 
 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',
 
 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',
 
 285         then: 'exception is thrown'
 
 286             def exceptionThrown = thrown(NcmpException.class)
 
 287         and: 'details contains the original response'
 
 288             exceptionThrown.details.contains('NOK-json')
 
 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',
 
 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',
 
 309         then: 'get resource data returns expected response'
 
 310             response == '{result-json}'
 
 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',
 
 328         then: 'exception is thrown with the expected details'
 
 329             def exceptionThrown = thrown(NcmpException.class)
 
 330             exceptionThrown.details == 'testException'
 
 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',
 
 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',
 
 352         then: 'exception is thrown'
 
 353             def exceptionThrown = thrown(NcmpException.class)
 
 354         and: 'details contains the original response'
 
 355             exceptionThrown.details.contains('NOK-json')
 
 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',
 
 367                     '{some-json}', 'application/json')
 
 368         then: 'dmi called with correct data'
 
 369             1 * mockDmiOperations.createResourceDataPassThroughRunningFromDmi('testDmiService',
 
 372                 '{"operation":"create","dataType":"application/json","data":"{some-json}","cmHandleProperties":'
 
 373                 + expectedJsonForCmhandleProperties+ '}')
 
 374                 >> { new ResponseEntity<>(HttpStatus.CREATED) }
 
 376             scenario  | includeCmHandleProperties || expectedJsonForCmhandleProperties
 
 377             'with'    | true                      || '{"testName":"testValue"}'
 
 378             'without' | false                     || '{}'
 
 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',
 
 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')
 
 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)
 
 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')] |'{}'
 
 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')
 
 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
 
 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'])
 
 460     def 'Update resource data for pass-through running from dmi using POST #scenario cm handle properties.'() {
 
 461         given: 'data node representing cmHandle #scenario cm handle properties'
 
 462             def cmHandleDataNode = getCmHandleDataNodeForTest(includeCmHandleProperties)
 
 463         and: 'cpsDataService returns valid cm-handle datanode'
 
 464             mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
 
 465                     cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> cmHandleDataNode
 
 466         when: 'update resource data is called'
 
 467             objectUnderTest.updateResourceDataPassThroughRunningForCmHandle('testCmHandle',
 
 469                     '{some-json}', 'application/json')
 
 470         then: 'dmi called with correct data'
 
 471             1 * mockDmiOperations.updateResourceDataPassThroughRunningFromDmi('testDmiService',
 
 474                     '{"operation":"update","dataType":"application/json","data":"{some-json}","cmHandleProperties":'
 
 475                             + expectedJsonForCmhandleProperties + '}')
 
 476                     >> new ResponseEntity<>(HttpStatus.OK)
 
 478             scenario  | includeCmHandleProperties || expectedJsonForCmhandleProperties
 
 479             'with'    | true                      || '{"testName":"testValue"}'
 
 480             'without' | false                     || '{}'
 
 483     def getObjectUnderTestWithModelSyncDisabled() {
 
 484         def objectUnderTest = Spy(new NetworkCmProxyDataServiceImpl(mockDmiOperations, mockCpsModuleService,
 
 485                 mockCpsDataService, mockCpsQueryService, mockCpsAdminService, spyObjectMapper))
 
 486         objectUnderTest.syncModulesAndCreateAnchor(_) >> null
 
 487         return objectUnderTest
 
 490     def getCmHandleDataNodeForTest(boolean includeCmHandleProperties) {
 
 491         def cmHandleDataNode = new DataNode()
 
 492         cmHandleDataNode.leaves = ['dmi-service-name': 'testDmiService']
 
 493         if (includeCmHandleProperties) {
 
 494             def cmHandlePropertyDataNode = new DataNode()
 
 495             cmHandlePropertyDataNode.leaves = ['name': 'testName', 'value': 'testValue']
 
 496             cmHandleDataNode.childDataNodes = [cmHandlePropertyDataNode]
 
 498         return cmHandleDataNode