2 * ============LICENSE_START=======================================================
3 * Copyright (C) 2022-2025 OpenInfra Foundation Europe. All rights reserved.
4 * Modifications Copyright (C) 2022 Bell Canada
5 * Modifications Copyright (C) 2024 TechMahindra Ltd.
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.impl.inventory
25 import com.fasterxml.jackson.databind.ObjectMapper
26 import com.hazelcast.map.IMap
27 import org.onap.cps.api.CpsAnchorService
28 import org.onap.cps.api.CpsDataService
29 import org.onap.cps.api.CpsModuleService
30 import org.onap.cps.api.exceptions.DataNodeNotFoundException
31 import org.onap.cps.api.exceptions.DataValidationException
32 import org.onap.cps.api.model.DataNode
33 import org.onap.cps.api.model.ModuleDefinition
34 import org.onap.cps.api.model.ModuleReference
35 import org.onap.cps.ncmp.api.inventory.models.CmHandleState
36 import org.onap.cps.ncmp.api.inventory.models.CompositeState
37 import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle
38 import org.onap.cps.ncmp.impl.models.CmHandleStateUpdate
39 import org.onap.cps.utils.ContentType
40 import org.onap.cps.utils.CpsValidator
41 import org.onap.cps.utils.JsonObjectMapper
42 import spock.lang.Shared
43 import spock.lang.Specification
45 import java.time.OffsetDateTime
46 import java.time.ZoneOffset
47 import java.time.format.DateTimeFormatter
49 import static org.onap.cps.api.parameters.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS
50 import static org.onap.cps.api.parameters.FetchDescendantsOption.OMIT_DESCENDANTS
51 import static org.onap.cps.ncmp.impl.inventory.NcmpPersistence.NCMP_DATASPACE_NAME
52 import static org.onap.cps.ncmp.impl.inventory.NcmpPersistence.NCMP_DMI_REGISTRY_ANCHOR
53 import static org.onap.cps.ncmp.impl.inventory.NcmpPersistence.NCMP_DMI_REGISTRY_PARENT
54 import static org.onap.cps.ncmp.impl.inventory.NcmpPersistence.NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME
55 import static org.onap.cps.ncmp.impl.inventory.NcmpPersistence.NO_TIMESTAMP
57 class InventoryPersistenceImplSpec extends Specification {
59 def spiedJsonObjectMapper = Spy(new JsonObjectMapper(new ObjectMapper()))
61 def mockCpsDataService = Mock(CpsDataService)
63 def mockCpsModuleService = Mock(CpsModuleService)
65 def mockCpsAnchorService = Mock(CpsAnchorService)
67 def mockCpsValidator = Mock(CpsValidator)
69 def mockCmHandleIdPerAlternateId = Mock(IMap)
71 def objectUnderTest = Spy(new InventoryPersistenceImpl(mockCpsValidator, spiedJsonObjectMapper, mockCpsAnchorService, mockCpsModuleService, mockCpsDataService, mockCmHandleIdPerAlternateId))
73 def formattedDateAndTime = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ")
74 .format(OffsetDateTime.of(2022, 12, 31, 20, 30, 40, 1, ZoneOffset.UTC))
76 def cmHandleId = 'ch-1'
78 new CmHandleStateUpdate("ch-1", "READY"),
79 new CmHandleStateUpdate("ch-2", "DELETING")
81 def alternateId = 'some-alternate-id'
82 def leaves = ["id":cmHandleId, "alternateId":alternateId,"dmi-service-name":"common service name","dmi-data-service-name":"data service name","dmi-model-service-name":"model service name"]
83 def xpath = "/dmi-registry/cm-handles[@id='ch-1']"
85 def cmHandleId2 = 'another-cm-handle'
86 def xpath2 = "/dmi-registry/cm-handles[@id='another-cm-handle']"
88 def dataNode = new DataNode(xpath: "/dmi-registry/cm-handles[@id='some cm handle']/additional-properties[@name='myAdditionalProperty']", leaves: leaves)
91 def childDataNodesForCmHandleWithAllProperties = [new DataNode(xpath: "/dmi-registry/cm-handles[@id='some cm handle']/additional-properties[@name='myAdditionalProperty']", leaves: ["name":"myAdditionalProperty", "value":"myAdditionalValue"]),
92 new DataNode(xpath: "/dmi-registry/cm-handles[@id='some cm handle']/public-properties[@name='myPublicProperty']", leaves: ["name":"myPublicProperty","value":"myPublicValue"])]
95 def childDataNodesForCmHandleWithAdditionalProperties = [new DataNode(xpath: "/dmi-registry/cm-handles[@id='ch-1']/additional-properties[@name='myAdditionalProperty']", leaves: ["name":"myAdditionalProperty", "value":"myAdditionalValue"])]
98 def childDataNodesForCmHandleWithPublicProperties = [new DataNode(xpath: "/dmi-registry/cm-handles[@id='ch-1']/public-properties[@name='myPublicProperty']", leaves: ["name":"myPublicProperty","value":"myPublicValue"])]
101 def childDataNodesForCmHandleWithState = [new DataNode(xpath: "/dmi-registry/cm-handles[@id='ch-1']/state", leaves: ['cm-handle-state': 'ADVISED'])]
103 def 'Retrieve CmHandle using datanode with #scenario.'() {
104 given: 'the cps data service returns a data node from the DMI registry'
105 def dataNode = new DataNode(childDataNodes:childDataNodes, leaves: leaves)
106 mockCpsDataService.getDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, xpath, INCLUDE_ALL_DESCENDANTS) >> [dataNode]
107 when: 'retrieving the yang modelled cm handle'
108 def result = objectUnderTest.getYangModelCmHandle(cmHandleId)
109 then: 'the result has the correct id and service names'
110 result.id == cmHandleId
111 result.dmiServiceName == 'common service name'
112 result.dmiDataServiceName == 'data service name'
113 result.dmiModelServiceName == 'model service name'
114 and: 'the expected additional properties'
115 result.additionalProperties.name == expectedAdditionalProperties
116 and: 'the expected public properties'
117 result.publicProperties.name == expectedPublicProperties
118 and: 'the state details are returned'
119 result.compositeState.cmHandleState == expectedCompositeState
120 and: 'the CM Handle ID is validated'
121 1 * mockCpsValidator.validateNameCharacters(cmHandleId)
122 where: 'the following parameters are used'
123 scenario | childDataNodes || expectedAdditionalProperties || expectedPublicProperties || expectedCompositeState
124 'no properties' | [] || [] || [] || null
125 'additional and public properties' | childDataNodesForCmHandleWithAllProperties || ["myAdditionalProperty"] || ["myPublicProperty"] || null
126 'just additional properties' | childDataNodesForCmHandleWithAdditionalProperties || ["myAdditionalProperty"] || [] || null
127 'just public properties' | childDataNodesForCmHandleWithPublicProperties || [] || ["myPublicProperty"] || null
128 'with state details' | childDataNodesForCmHandleWithState || [] || [] || CmHandleState.ADVISED
131 def 'Handling missing service names as null.'() {
132 given: 'the cps data service returns a data node from the DMI registry with empty child and leaf attributes'
133 def dataNode = new DataNode(childDataNodes:[], leaves: ['id':cmHandleId])
134 mockCpsDataService.getDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, xpath, INCLUDE_ALL_DESCENDANTS) >> [dataNode]
135 when: 'retrieving the yang modelled cm handle'
136 def result = objectUnderTest.getYangModelCmHandle(cmHandleId)
137 then: 'the service names are returned as null'
138 result.dmiServiceName == null
139 result.dmiDataServiceName == null
140 result.dmiModelServiceName == null
141 and: 'the CM Handle ID is validated'
142 1 * mockCpsValidator.validateNameCharacters(cmHandleId)
145 def 'Retrieve multiple YangModelCmHandles using cm handle ids.'() {
146 given: 'the cps data service returns 2 data nodes from the DMI registry'
147 def dataNodes = [new DataNode(xpath: xpath, leaves: ['id': cmHandleId]), new DataNode(xpath: xpath2, leaves: ['id': cmHandleId2])]
148 mockCpsDataService.getDataNodesForMultipleXpaths(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, [xpath, xpath2] , INCLUDE_ALL_DESCENDANTS) >> dataNodes
149 when: 'retrieving the yang modelled cm handles'
150 def results = objectUnderTest.getYangModelCmHandles([cmHandleId, cmHandleId2])
151 then: 'verify both have returned and cm handle Ids are correct'
152 assert results.size() == 2
153 assert results.id.containsAll([cmHandleId, cmHandleId2])
156 def 'YangModelCmHandles are not returned for invalid cm handle ids.'() {
157 given: 'invalid cm handle id throws a data validation exception'
158 mockCpsValidator.validateNameCharacters('Invalid Cm Handle Id') >> {throw new DataValidationException('','')}
159 and: 'empty collection is returned as no valid cm handle ids are given'
160 mockCpsDataService.getDataNodesForMultipleXpaths(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, [] , INCLUDE_ALL_DESCENDANTS) >> []
161 when: 'retrieving the yang modelled cm handles'
162 def results = objectUnderTest.getYangModelCmHandles(['Invalid Cm Handle Id'])
163 then: 'no YangModelCmHandle is returned'
164 assert results.size() == 0
167 def 'Get a Cm Handle Composite State.'() {
168 given: 'a valid cm handle id'
169 def cmHandleId = 'ch-1'
170 def dataNode = new DataNode(leaves: ['cm-handle-state': 'ADVISED'])
171 and: 'cps data service returns a valid data node'
172 mockCpsDataService.getDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
173 '/dmi-registry/cm-handles[@id=\'ch-1\']/state', INCLUDE_ALL_DESCENDANTS) >> [dataNode]
174 when: 'get cm handle state is invoked'
175 def result = objectUnderTest.getCmHandleState(cmHandleId)
176 then: 'result has returned the correct cm handle state'
177 result.cmHandleState == CmHandleState.ADVISED
178 and: 'the CM Handle ID is validated'
179 1 * mockCpsValidator.validateNameCharacters(cmHandleId)
182 def 'Update Cm Handle with #scenario State.'() {
183 given: 'a cm handle and a composite state'
184 def cmHandleId = 'ch-1'
185 def compositeState = new CompositeState(cmHandleState: cmHandleState, lastUpdateTime: formattedDateAndTime)
186 when: 'update cm handle state is invoked with the #scenario state'
187 objectUnderTest.saveCmHandleState(cmHandleId, compositeState)
188 then: 'update node leaves is invoked with the correct params'
189 1 * mockCpsDataService.updateDataNodeAndDescendants(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, '/dmi-registry/cm-handles[@id=\'ch-1\']', expectedJsonData, _ as OffsetDateTime, ContentType.JSON)
190 where: 'the following states are used'
191 scenario | cmHandleState || expectedJsonData
192 'READY' | CmHandleState.READY || '{"state":{"cm-handle-state":"READY","last-update-time":"2022-12-31T20:30:40.000+0000"}}'
193 'LOCKED' | CmHandleState.LOCKED || '{"state":{"cm-handle-state":"LOCKED","last-update-time":"2022-12-31T20:30:40.000+0000"}}'
194 'DELETING' | CmHandleState.DELETING || '{"state":{"cm-handle-state":"DELETING","last-update-time":"2022-12-31T20:30:40.000+0000"}}'
197 def 'Update Cm Handles with #scenario States.'() {
198 given: 'a map of cm handles composite states'
199 def compositeState1 = new CompositeState(cmHandleState: cmHandleState, lastUpdateTime: formattedDateAndTime)
200 def compositeState2 = new CompositeState(cmHandleState: cmHandleState, lastUpdateTime: formattedDateAndTime)
201 and: 'alternate id cache contains the given cm handle reference'
202 mockCmHandleIdPerAlternateId.containsKey(_) >> true
203 when: 'update cm handle state is invoked with the #scenario state'
204 def cmHandleStateMap = ['ch-11' : compositeState1, 'ch-12' : compositeState2]
205 objectUnderTest.saveCmHandleStateBatch(cmHandleStateMap)
206 then: 'update node leaves is invoked with the correct params'
207 1 * mockCpsDataService.updateDataNodesAndDescendants(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, cmHandlesJsonDataMap, _ as OffsetDateTime, ContentType.JSON)
208 where: 'the following states are used'
209 scenario | cmHandleState || cmHandlesJsonDataMap
210 'READY' | CmHandleState.READY || ['/dmi-registry/cm-handles[@id=\'ch-11\']':'{"state":{"cm-handle-state":"READY","last-update-time":"2022-12-31T20:30:40.000+0000"}}', '/dmi-registry/cm-handles[@id=\'ch-12\']':'{"state":{"cm-handle-state":"READY","last-update-time":"2022-12-31T20:30:40.000+0000"}}']
211 'LOCKED' | CmHandleState.LOCKED || ['/dmi-registry/cm-handles[@id=\'ch-11\']':'{"state":{"cm-handle-state":"LOCKED","last-update-time":"2022-12-31T20:30:40.000+0000"}}', '/dmi-registry/cm-handles[@id=\'ch-12\']':'{"state":{"cm-handle-state":"LOCKED","last-update-time":"2022-12-31T20:30:40.000+0000"}}']
212 'DELETING' | CmHandleState.DELETING || ['/dmi-registry/cm-handles[@id=\'ch-11\']':'{"state":{"cm-handle-state":"DELETING","last-update-time":"2022-12-31T20:30:40.000+0000"}}', '/dmi-registry/cm-handles[@id=\'ch-12\']':'{"state":{"cm-handle-state":"DELETING","last-update-time":"2022-12-31T20:30:40.000+0000"}}']
215 def 'Update cm handle states when #scenario in alternate id cache.'() {
216 given: 'a map of cm handles composite states'
217 def compositeState = new CompositeState(cmHandleState: CmHandleState.ADVISED, lastUpdateTime: formattedDateAndTime)
218 def cmHandleStateMap = ['ch-1' : compositeState]
219 and: 'alternate id cache returns #scenario'
220 mockCmHandleIdPerAlternateId.containsKey(_) >> keyExists
221 mockCmHandleIdPerAlternateId.containsValue(_) >> valueExists
222 when: 'we update the state of a cm handle when #scenario'
223 objectUnderTest.saveCmHandleStateBatch(cmHandleStateMap)
224 then: 'update node leaves is invoked correct number of times'
225 expectedCalls * mockCpsDataService.updateDataNodesAndDescendants(*_)
226 where: 'the following cm handle ids are used'
227 scenario | keyExists | valueExists || expectedCalls
228 'id exists as key' | true | false || 1
229 'id exists as value'| false | true || 1
230 'id does not exist' | false | false || 0
234 def 'Getting module definitions by module.'() {
235 given: 'cps module service returns module definition for module name'
236 def moduleDefinitions = [new ModuleDefinition('moduleName','revision','content')]
237 mockCpsModuleService.getModuleDefinitionsByAnchorAndModule(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME,'some-cmHandle-Id', 'some-module', '2024-01-25') >> moduleDefinitions
238 when: 'get module definitions is invoked with module name'
239 def result = objectUnderTest.getModuleDefinitionsByCmHandleAndModule('some-cmHandle-Id', 'some-module', '2024-01-25')
240 then: 'returned result are the same module definitions as returned from module service'
241 assert result == moduleDefinitions
242 and: 'cm handle id and module name validated'
243 1 * mockCpsValidator.validateNameCharacters('some-cmHandle-Id', 'some-module')
246 def 'Getting module definitions with cm handle id.'() {
247 given: 'cps module service returns module definitions for cm handle id'
248 def moduleDefinitions = [new ModuleDefinition('moduleName','revision','content')]
249 mockCpsModuleService.getModuleDefinitionsByAnchorName(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME,'some-cmHandle-Id') >> moduleDefinitions
250 when: 'get module definitions is invoked with cm handle id'
251 def result = objectUnderTest.getModuleDefinitionsByCmHandleId('some-cmHandle-Id')
252 then: 'the returned result are the same module definitions as returned from the module service'
253 assert result == moduleDefinitions
256 def 'Get module references.'() {
257 given: 'cps module service returns a collection of module references'
258 def moduleReferences = [new ModuleReference('moduleName','revision','namespace')]
259 mockCpsModuleService.getYangResourcesModuleReferences(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME,'some-cmHandle-Id') >> moduleReferences
260 when: 'get yang resources module references by cmHandle is invoked'
261 def result = objectUnderTest.getYangResourcesModuleReferences('some-cmHandle-Id')
262 then: 'the returned result is a collection of module definitions'
263 assert result == moduleReferences
264 and: 'the CM Handle ID is validated'
265 1 * mockCpsValidator.validateNameCharacters('some-cmHandle-Id')
268 def 'Save Cmhandle.'() {
269 given: 'cmHandle represented as Yang Model'
270 def yangModelCmHandle = new YangModelCmHandle(id: 'cmhandle', additionalProperties: [], publicProperties: [])
271 when: 'the method to save cmhandle is called'
272 objectUnderTest.saveCmHandle(yangModelCmHandle)
273 then: 'the data service method to save list elements is called once'
274 1 * mockCpsDataService.saveListElements(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, NCMP_DMI_REGISTRY_PARENT,
275 _,null, ContentType.JSON) >> {
277 assert args[3].startsWith('{"cm-handles":[{"id":"cmhandle","additional-properties":[],"public-properties":[]}]}')
282 def 'Save Multiple Cmhandles.'() {
283 given: 'cm handles represented as Yang Model'
284 def yangModelCmHandle1 = new YangModelCmHandle(id: 'cmhandle1')
285 def yangModelCmHandle2 = new YangModelCmHandle(id: 'cmhandle2')
286 when: 'the cm handles are saved'
287 objectUnderTest.saveCmHandleBatch([yangModelCmHandle1, yangModelCmHandle2])
288 then: 'CPS Data Service persists both cm handles as a batch'
289 1 * mockCpsDataService.saveListElements(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
290 NCMP_DMI_REGISTRY_PARENT, _,null, ContentType.JSON) >> {
292 def jsonData = (args[3] as String)
293 jsonData.contains('cmhandle1')
294 jsonData.contains('cmhandle2')
299 def 'Delete list or list elements.'() {
300 when: 'the method to delete list or list elements is called'
301 objectUnderTest.deleteListOrListElement('sample xPath')
302 then: 'the data service method to save list elements is called once'
303 1 * mockCpsDataService.deleteListOrListElement(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,'sample xPath',null)
306 def 'Get data node via xPath.'() {
307 when: 'the method to get data nodes is called'
308 objectUnderTest.getDataNode('sample xPath')
309 then: 'the data persistence service method to get data node is invoked once'
310 1 * mockCpsDataService.getDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,'sample xPath', INCLUDE_ALL_DESCENDANTS)
313 def 'Get cmHandle data node.'() {
314 given: 'expected xPath to get cmHandle data node'
315 def expectedXPath = '/dmi-registry/cm-handles[@id=\'sample cmHandleId\']'
316 when: 'the method to get data nodes is called'
317 objectUnderTest.getCmHandleDataNodeByCmHandleId('sample cmHandleId', INCLUDE_ALL_DESCENDANTS)
318 then: 'the data persistence service method to get cmHandle data node is invoked once with expected xPath'
319 1 * mockCpsDataService.getDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, expectedXPath, INCLUDE_ALL_DESCENDANTS)
322 def 'Get CM handle ids for CM Handles that has given module names.'() {
323 when: 'the method to get cm handles is called'
324 objectUnderTest.getCmHandleReferencesWithGivenModules(['sample-module-name'], false)
325 then: 'the admin persistence service method to query anchors is invoked once with the same parameter'
326 1 * mockCpsAnchorService.queryAnchorNames(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, ['sample-module-name'])
329 def 'Get Alternate Ids for CM Handles that has given module names.'() {
330 given: 'cps anchor service returns a CM-handle ID for the given module name'
331 mockCpsAnchorService.queryAnchorNames(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, ['sample-module-name']) >> ['ch-1']
332 and: 'cps data service returns some data nodes for the given CM-handle ID'
333 def dataNodes = [new DataNode(xpath: "/dmi-registry/cm-handles[@id='ch-1']", leaves: ['id': 'ch-1', 'alternate-id': 'alt-1'])]
334 mockCpsDataService.getDataNodesForMultipleXpaths(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, ["/dmi-registry/cm-handles[@id='ch-1']"], OMIT_DESCENDANTS) >> dataNodes
335 when: 'the method to get cm-handle references by modules is called (outputting alternate IDs)'
336 def result = objectUnderTest.getCmHandleReferencesWithGivenModules(['sample-module-name'], true)
337 then: 'the result contains the correct alternate Id'
338 assert result == ['alt-1'] as Set
341 def 'Replace list content.'() {
342 when: 'replace list content method is called with xpath and data nodes collection'
343 objectUnderTest.replaceListContent('sample xpath', [new DataNode()])
344 then: 'the cps data service method to replace list content is invoked once with same parameters'
345 1 * mockCpsDataService.replaceListContent(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,'sample xpath', [new DataNode()], NO_TIMESTAMP);
348 def 'Delete data node via xPath.'() {
349 when: 'Delete data node method is called with xpath as parameter'
350 objectUnderTest.deleteDataNode('sample dataNode xpath')
351 then: 'the cps data service method to delete data node is invoked once with the same xPath'
352 1 * mockCpsDataService.deleteDataNode(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, 'sample dataNode xpath', NO_TIMESTAMP);
355 def 'Delete multiple data nodes via xPath.'() {
356 when: 'Delete data nodes method is called with multiple xpaths as parameters'
357 objectUnderTest.deleteDataNodes(['xpath1', 'xpath2'])
358 then: 'the cps data service method to delete data nodes is invoked once with the same xPaths'
359 1 * mockCpsDataService.deleteDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, ['xpath1', 'xpath2'], NO_TIMESTAMP);
362 def 'CM handle exists.'() {
363 given: 'data service returns a datanode with correct cm handle id'
364 mockCpsDataService.getDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, xpath, OMIT_DESCENDANTS) >> [dataNode]
365 expect: 'cm handle exists for given cm handle id'
366 assert true == objectUnderTest.isExistingCmHandleId(cmHandleId)
369 def 'CM handle does not exist (data service returns empty collection).'() {
370 given: 'data service returns an empty datanode'
371 mockCpsDataService.getDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, xpath, OMIT_DESCENDANTS) >> []
372 expect: 'false is returned for non-existent cm handle'
373 assert false == objectUnderTest.isExistingCmHandleId(cmHandleId)
376 def 'CM handle does not exist (data service throws).'() {
377 given: 'data service throws an exception'
378 mockCpsDataService.getDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, "/dmi-registry/cm-handles[@id='non-existent-cm-handle']", OMIT_DESCENDANTS) >> {throw new DataNodeNotFoundException('','')}
379 expect: 'false is returned for non-existent cm handle'
380 assert false == objectUnderTest.isExistingCmHandleId('non-existent-cm-handle')
383 def 'Delete anchors.'() {
384 when: 'Deleting some anchors'
385 objectUnderTest.deleteAnchors(['anchor1' ,'anchor2'])
386 then: 'The call is delegated to the anchor service with teh correct parameters'
387 mockCpsAnchorService.deleteAnchors(NCMP_DATASPACE_NAME ,['anchor1' ,'anchor2'])
390 def 'Get Yang Model CM Handles without properties.'() {
391 given: 'the cps data service returns 2 data nodes from the DMI registry (omitting descendants)'
392 def dataNodes = [new DataNode(xpath: xpath, leaves: ['id': cmHandleId]), new DataNode(xpath: xpath2, leaves: ['id': cmHandleId2])]
393 mockCpsDataService.getDataNodesForMultipleXpaths(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, [xpath, xpath2] , OMIT_DESCENDANTS) >> dataNodes
394 when: 'retrieving cm handles without properties'
395 def result = objectUnderTest.getYangModelCmHandlesWithoutProperties([cmHandleId, cmHandleId2])
396 then: 'The cm handles from the data service are returned'
397 assert result.size() == 2
398 assert result.id.containsAll([cmHandleId, cmHandleId2])
401 def 'Update Cm Handle Field.'(){
402 when: 'update is called.'
403 objectUnderTest.updateCmHandleField('ch-1', 'my field', 'my new value')
404 then: 'call is delegated to updateCmHandleFields'
405 1 * objectUnderTest.updateCmHandleFields('my field', ['ch-1':'my new value'])
408 def 'Bulk update cm handle state.'(){
409 when: 'bulk update is called'
410 objectUnderTest.bulkUpdateCmHandleStates(updates)
411 then: 'call is made to update the fileds of the cm handle'
412 1 * objectUnderTest.updateCmHandleFields('cm-handle-state', ['ch-1':'READY','ch-2':'DELETING'])