2 * ============LICENSE_START=======================================================
3 * Copyright (C) 2022-2024 Nordix Foundation
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 org.onap.cps.api.CpsAnchorService
27 import org.onap.cps.api.CpsDataService
28 import org.onap.cps.api.CpsModuleService
29 import org.onap.cps.impl.utils.CpsValidator
30 import org.onap.cps.ncmp.api.inventory.models.CompositeState
31 import org.onap.cps.ncmp.impl.inventory.models.CmHandleState
32 import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle
33 import org.onap.cps.spi.CascadeDeleteAllowed
34 import org.onap.cps.spi.FetchDescendantsOption
35 import org.onap.cps.spi.exceptions.DataNodeNotFoundException
36 import org.onap.cps.spi.model.DataNode
37 import org.onap.cps.spi.model.ModuleDefinition
38 import org.onap.cps.spi.model.ModuleReference
39 import org.onap.cps.utils.ContentType
40 import org.onap.cps.utils.JsonObjectMapper
41 import spock.lang.Shared
42 import spock.lang.Specification
44 import java.time.OffsetDateTime
45 import java.time.ZoneOffset
46 import java.time.format.DateTimeFormatter
48 import static org.onap.cps.ncmp.impl.inventory.NcmpPersistence.NCMP_DATASPACE_NAME
49 import static org.onap.cps.ncmp.impl.inventory.NcmpPersistence.NCMP_DMI_REGISTRY_ANCHOR
50 import static org.onap.cps.ncmp.impl.inventory.NcmpPersistence.NCMP_DMI_REGISTRY_PARENT
51 import static org.onap.cps.ncmp.impl.inventory.NcmpPersistence.NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME
52 import static org.onap.cps.ncmp.impl.inventory.NcmpPersistence.NO_TIMESTAMP
53 import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS
54 import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS
56 class InventoryPersistenceImplSpec extends Specification {
58 def spiedJsonObjectMapper = Spy(new JsonObjectMapper(new ObjectMapper()))
60 def mockCpsDataService = Mock(CpsDataService)
62 def mockCpsModuleService = Mock(CpsModuleService)
64 def mockCpsAnchorService = Mock(CpsAnchorService)
66 def mockCpsValidator = Mock(CpsValidator)
68 def mockCmHandleQueries = Mock(CmHandleQueryService)
70 def objectUnderTest = new InventoryPersistenceImpl(spiedJsonObjectMapper, mockCpsDataService, mockCpsModuleService,
71 mockCpsValidator, mockCpsAnchorService, mockCmHandleQueries)
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 = 'some-cm-handle'
77 def alternateId = 'some-alternate-id'
78 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"]
79 def xpath = "/dmi-registry/cm-handles[@id='some-cm-handle']"
81 def cmHandleId2 = 'another-cm-handle'
82 def alternateId2 = 'another-alternate-id'
83 def xpath2 = "/dmi-registry/cm-handles[@id='another-cm-handle']"
85 def dataNode = new DataNode(xpath: "/dmi-registry/cm-handles[@id='some cm handle']/additional-properties[@name='name1']", leaves: ["name":"name1", "value":"value1"])
88 def childDataNodesForCmHandleWithAllProperties = [new DataNode(xpath: "/dmi-registry/cm-handles[@id='some cm handle']/additional-properties[@name='name1']", leaves: ["name":"name1", "value":"value1"]),
89 new DataNode(xpath: "/dmi-registry/cm-handles[@id='some cm handle']/public-properties[@name='name2']", leaves: ["name":"name2","value":"value2"])]
92 def childDataNodesForCmHandleWithDMIProperties = [new DataNode(xpath: "/dmi-registry/cm-handles[@id='some-cm-handle']/additional-properties[@name='name1']", leaves: ["name":"name1", "value":"value1"])]
95 def childDataNodesForCmHandleWithPublicProperties = [new DataNode(xpath: "/dmi-registry/cm-handles[@id='some-cm-handle']/public-properties[@name='name2']", leaves: ["name":"name2","value":"value2"])]
98 def childDataNodesForCmHandleWithState = [new DataNode(xpath: "/dmi-registry/cm-handles[@id='some-cm-handle']/state", leaves: ['cm-handle-state': 'ADVISED'])]
100 def "Retrieve CmHandle using datanode with #scenario."() {
101 given: 'the cps data service returns a data node from the DMI registry'
102 def dataNode = new DataNode(childDataNodes:childDataNodes, leaves: leaves)
103 mockCpsDataService.getDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, xpath, INCLUDE_ALL_DESCENDANTS) >> [dataNode]
104 when: 'retrieving the yang modelled cm handle'
105 def result = objectUnderTest.getYangModelCmHandle(cmHandleId)
106 then: 'the result has the correct id and service names'
107 result.id == cmHandleId
108 result.dmiServiceName == 'common service name'
109 result.dmiDataServiceName == 'data service name'
110 result.dmiModelServiceName == 'model service name'
111 and: 'the expected DMI properties'
112 result.dmiProperties == expectedDmiProperties
113 result.publicProperties == expectedPublicProperties
114 and: 'the state details are returned'
115 result.compositeState.cmHandleState == expectedCompositeState
116 and: 'the CM Handle ID is validated'
117 1 * mockCpsValidator.validateNameCharacters(cmHandleId)
118 where: 'the following parameters are used'
119 scenario | childDataNodes || expectedDmiProperties || expectedPublicProperties || expectedCompositeState
120 'no properties' | [] || [] || [] || null
121 'DMI and public properties' | childDataNodesForCmHandleWithAllProperties || [new YangModelCmHandle.Property("name1", "value1")] || [new YangModelCmHandle.Property("name2", "value2")] || null
122 'just DMI properties' | childDataNodesForCmHandleWithDMIProperties || [new YangModelCmHandle.Property("name1", "value1")] || [] || null
123 'just public properties' | childDataNodesForCmHandleWithPublicProperties || [] || [new YangModelCmHandle.Property("name2", "value2")] || null
124 'with state details' | childDataNodesForCmHandleWithState || [] || [] || CmHandleState.ADVISED
127 def "Handling missing service names as null."() {
128 given: 'the cps data service returns a data node from the DMI registry with empty child and leaf attributes'
129 def dataNode = new DataNode(childDataNodes:[], leaves: ['id':cmHandleId])
130 mockCpsDataService.getDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, xpath, INCLUDE_ALL_DESCENDANTS) >> [dataNode]
131 when: 'retrieving the yang modelled cm handle'
132 def result = objectUnderTest.getYangModelCmHandle(cmHandleId)
133 then: 'the service names are returned as null'
134 result.dmiServiceName == null
135 result.dmiDataServiceName == null
136 result.dmiModelServiceName == null
137 and: 'the CM Handle ID is validated'
138 1 * mockCpsValidator.validateNameCharacters(cmHandleId)
141 def "Retrieve multiple YangModelCmHandles using cm handle ids"() {
142 given: 'the cps data service returns 2 data nodes from the DMI registry'
143 def dataNodes = [new DataNode(xpath: xpath, leaves: ['id': cmHandleId]), new DataNode(xpath: xpath2, leaves: ['id': cmHandleId2])]
144 mockCpsDataService.getDataNodesForMultipleXpaths(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, [xpath, xpath2] , INCLUDE_ALL_DESCENDANTS) >> dataNodes
145 when: 'retrieving the yang modelled cm handle'
146 def results = objectUnderTest.getYangModelCmHandles([cmHandleId, cmHandleId2])
147 then: 'verify both have returned and cmhandleIds are correct'
148 assert results.size() == 2
149 assert results.id.containsAll([cmHandleId, cmHandleId2])
152 def "Retrieve multiple YangModelCmHandles using cm handle references"() {
153 given: 'the cps data service returns 2 data nodes from the DMI registry'
154 def dataNodes = [new DataNode(xpath: xpath, leaves: ['id': cmHandleId, 'alternate-id':alternateId]), new DataNode(xpath: xpath2, leaves: ['id': cmHandleId2,'alternate-id':alternateId2])]
155 mockCmHandleQueries.queryNcmpRegistryByCpsPath(_, INCLUDE_ALL_DESCENDANTS) >> dataNodes
156 when: 'retrieving the yang modelled cm handle'
157 def results = objectUnderTest.getYangModelCmHandlesFromCmHandleReferences([cmHandleId, cmHandleId2])
158 then: 'verify both have returned and cmhandleIds are correct'
159 assert results.size() == 2
160 assert results.id.containsAll([cmHandleId, cmHandleId2])
163 def 'Get a Cm Handle Composite State'() {
164 given: 'a valid cm handle id'
165 def cmHandleId = 'Some-Cm-Handle'
166 def dataNode = new DataNode(leaves: ['cm-handle-state': 'ADVISED'])
167 and: 'cps data service returns a valid data node'
168 mockCpsDataService.getDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
169 '/dmi-registry/cm-handles[@id=\'Some-Cm-Handle\']/state', FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> [dataNode]
170 when: 'get cm handle state is invoked'
171 def result = objectUnderTest.getCmHandleState(cmHandleId)
172 then: 'result has returned the correct cm handle state'
173 result.cmHandleState == CmHandleState.ADVISED
174 and: 'the CM Handle ID is validated'
175 1 * mockCpsValidator.validateNameCharacters(cmHandleId)
178 def 'Update Cm Handle with #scenario State'() {
179 given: 'a cm handle and a composite state'
180 def cmHandleId = 'Some-Cm-Handle'
181 def compositeState = new CompositeState(cmHandleState: cmHandleState, lastUpdateTime: formattedDateAndTime)
182 when: 'update cm handle state is invoked with the #scenario state'
183 objectUnderTest.saveCmHandleState(cmHandleId, compositeState)
184 then: 'update node leaves is invoked with the correct params'
185 1 * mockCpsDataService.updateDataNodeAndDescendants(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, '/dmi-registry/cm-handles[@id=\'Some-Cm-Handle\']', expectedJsonData, _ as OffsetDateTime, ContentType.JSON)
186 where: 'the following states are used'
187 scenario | cmHandleState || expectedJsonData
188 'READY' | CmHandleState.READY || '{"state":{"cm-handle-state":"READY","last-update-time":"2022-12-31T20:30:40.000+0000"}}'
189 'LOCKED' | CmHandleState.LOCKED || '{"state":{"cm-handle-state":"LOCKED","last-update-time":"2022-12-31T20:30:40.000+0000"}}'
190 'DELETING' | CmHandleState.DELETING || '{"state":{"cm-handle-state":"DELETING","last-update-time":"2022-12-31T20:30:40.000+0000"}}'
193 def 'Update Cm Handles with #scenario States'() {
194 given: 'a map of cm handles composite states'
195 def compositeState1 = new CompositeState(cmHandleState: cmHandleState, lastUpdateTime: formattedDateAndTime)
196 def compositeState2 = new CompositeState(cmHandleState: cmHandleState, lastUpdateTime: formattedDateAndTime)
197 when: 'update cm handle state is invoked with the #scenario state'
198 def cmHandleStateMap = ['Some-Cm-Handle1' : compositeState1, 'Some-Cm-Handle2' : compositeState2]
199 objectUnderTest.saveCmHandleStateBatch(cmHandleStateMap)
200 then: 'update node leaves is invoked with the correct params'
201 1 * mockCpsDataService.updateDataNodesAndDescendants(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, cmHandlesJsonDataMap, _ as OffsetDateTime, ContentType.JSON)
202 where: 'the following states are used'
203 scenario | cmHandleState || cmHandlesJsonDataMap
204 'READY' | CmHandleState.READY || ['/dmi-registry/cm-handles[@id=\'Some-Cm-Handle1\']':'{"state":{"cm-handle-state":"READY","last-update-time":"2022-12-31T20:30:40.000+0000"}}', '/dmi-registry/cm-handles[@id=\'Some-Cm-Handle2\']':'{"state":{"cm-handle-state":"READY","last-update-time":"2022-12-31T20:30:40.000+0000"}}']
205 'LOCKED' | CmHandleState.LOCKED || ['/dmi-registry/cm-handles[@id=\'Some-Cm-Handle1\']':'{"state":{"cm-handle-state":"LOCKED","last-update-time":"2022-12-31T20:30:40.000+0000"}}', '/dmi-registry/cm-handles[@id=\'Some-Cm-Handle2\']':'{"state":{"cm-handle-state":"LOCKED","last-update-time":"2022-12-31T20:30:40.000+0000"}}']
206 'DELETING' | CmHandleState.DELETING || ['/dmi-registry/cm-handles[@id=\'Some-Cm-Handle1\']':'{"state":{"cm-handle-state":"DELETING","last-update-time":"2022-12-31T20:30:40.000+0000"}}', '/dmi-registry/cm-handles[@id=\'Some-Cm-Handle2\']':'{"state":{"cm-handle-state":"DELETING","last-update-time":"2022-12-31T20:30:40.000+0000"}}']
209 def 'Getting module definitions by module'() {
210 given: 'cps module service returns module definition for module name'
211 def moduleDefinitions = [new ModuleDefinition('moduleName','revision','content')]
212 mockCpsModuleService.getModuleDefinitionsByAnchorAndModule(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME,'some-cmHandle-Id', 'some-module', '2024-01-25') >> moduleDefinitions
213 when: 'get module definitions is invoked with module name'
214 def result = objectUnderTest.getModuleDefinitionsByCmHandleAndModule('some-cmHandle-Id', 'some-module', '2024-01-25')
215 then: 'returned result are the same module definitions as returned from module service'
216 assert result == moduleDefinitions
217 and: 'cm handle id and module name validated'
218 1 * mockCpsValidator.validateNameCharacters('some-cmHandle-Id', 'some-module')
221 def 'Getting module definitions with cm handle id'() {
222 given: 'cps module service returns module definitions for cm handle id'
223 def moduleDefinitions = [new ModuleDefinition('moduleName','revision','content')]
224 mockCpsModuleService.getModuleDefinitionsByAnchorName(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME,'some-cmHandle-Id') >> moduleDefinitions
225 when: 'get module definitions is invoked with cm handle id'
226 def result = objectUnderTest.getModuleDefinitionsByCmHandleId('some-cmHandle-Id')
227 then: 'the returned result are the same module definitions as returned from the module service'
228 assert result == moduleDefinitions
231 def 'Get module references'() {
232 given: 'cps module service returns a collection of module references'
233 def moduleReferences = [new ModuleReference('moduleName','revision','namespace')]
234 mockCpsModuleService.getYangResourcesModuleReferences(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME,'some-cmHandle-Id') >> moduleReferences
235 when: 'get yang resources module references by cmHandle is invoked'
236 def result = objectUnderTest.getYangResourcesModuleReferences('some-cmHandle-Id')
237 then: 'the returned result is a collection of module definitions'
238 assert result == moduleReferences
239 and: 'the CM Handle ID is validated'
240 1 * mockCpsValidator.validateNameCharacters('some-cmHandle-Id')
243 def 'Save Cmhandle'() {
244 given: 'cmHandle represented as Yang Model'
245 def yangModelCmHandle = new YangModelCmHandle(id: 'cmhandle', dmiProperties: [], publicProperties: [])
246 when: 'the method to save cmhandle is called'
247 objectUnderTest.saveCmHandle(yangModelCmHandle)
248 then: 'the data service method to save list elements is called once'
249 1 * mockCpsDataService.saveListElements(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, NCMP_DMI_REGISTRY_PARENT,
250 _,null, ContentType.JSON) >> {
252 assert args[3].startsWith('{"cm-handles":[{"id":"cmhandle","additional-properties":[],"public-properties":[]}]}')
257 def 'Save Multiple Cmhandles'() {
258 given: 'cm handles represented as Yang Model'
259 def yangModelCmHandle1 = new YangModelCmHandle(id: 'cmhandle1')
260 def yangModelCmHandle2 = new YangModelCmHandle(id: 'cmhandle2')
261 when: 'the cm handles are saved'
262 objectUnderTest.saveCmHandleBatch([yangModelCmHandle1, yangModelCmHandle2])
263 then: 'CPS Data Service persists both cm handles as a batch'
264 1 * mockCpsDataService.saveListElements(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
265 NCMP_DMI_REGISTRY_PARENT, _,null, ContentType.JSON) >> {
267 def jsonData = (args[3] as String)
268 jsonData.contains('cmhandle1')
269 jsonData.contains('cmhandle2')
274 def 'Delete list or list elements'() {
275 when: 'the method to delete list or list elements is called'
276 objectUnderTest.deleteListOrListElement('sample xPath')
277 then: 'the data service method to save list elements is called once'
278 1 * mockCpsDataService.deleteListOrListElement(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,'sample xPath',null)
281 def 'Delete schema set with a valid schema set name'() {
282 when: 'the method to delete schema set is called with valid schema set name'
283 objectUnderTest.deleteSchemaSetWithCascade('validSchemaSetName')
284 then: 'the module service to delete schemaSet is invoked once'
285 1 * mockCpsModuleService.deleteSchemaSet(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, 'validSchemaSetName', CascadeDeleteAllowed.CASCADE_DELETE_ALLOWED)
286 and: 'the schema set name is validated'
287 1 * mockCpsValidator.validateNameCharacters('validSchemaSetName')
290 def 'Delete multiple schema sets with valid schema set names'() {
291 when: 'the method to delete schema sets is called with valid schema set names'
292 objectUnderTest.deleteSchemaSetsWithCascade(['validSchemaSetName1', 'validSchemaSetName2'])
293 then: 'the module service to delete schema sets is invoked once'
294 1 * mockCpsModuleService.deleteSchemaSetsWithCascade(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, ['validSchemaSetName1', 'validSchemaSetName2'])
295 and: 'the schema set names are validated'
296 1 * mockCpsValidator.validateNameCharacters(['validSchemaSetName1', 'validSchemaSetName2'])
299 def 'Get data node via xPath'() {
300 when: 'the method to get data nodes is called'
301 objectUnderTest.getDataNode('sample xPath')
302 then: 'the data persistence service method to get data node is invoked once'
303 1 * mockCpsDataService.getDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,'sample xPath', INCLUDE_ALL_DESCENDANTS)
306 def 'Get cmHandle data node'() {
307 given: 'expected xPath to get cmHandle data node'
308 def expectedXPath = '/dmi-registry/cm-handles[@id=\'sample cmHandleId\']'
309 when: 'the method to get data nodes is called'
310 objectUnderTest.getCmHandleDataNodeByCmHandleId('sample cmHandleId')
311 then: 'the data persistence service method to get cmHandle data node is invoked once with expected xPath'
312 1 * mockCpsDataService.getDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, expectedXPath, INCLUDE_ALL_DESCENDANTS)
315 def 'Get cm handle data node by alternate id'() {
316 given: 'expected xPath to get cmHandle data node'
317 def expectedXPath = '/dmi-registry/cm-handles[@alternate-id=\'alternate id\']'
318 and: 'query service is invoked with expected xpath'
319 mockCmHandleQueries.queryNcmpRegistryByCpsPath(expectedXPath, OMIT_DESCENDANTS) >> [new DataNode()]
320 expect: 'getting the cm handle data node'
321 assert objectUnderTest.getCmHandleDataNodeByAlternateId('alternate id') == new DataNode()
324 def 'Attempt to get non existing cm handle data node by alternate id'() {
325 given: 'query service is invoked and returns empty collection of data nodes'
326 mockCmHandleQueries.queryNcmpRegistryByCpsPath(*_) >> []
327 when: 'getting the cm handle data node'
328 objectUnderTest.getCmHandleDataNodeByAlternateId('alternate id')
329 then: 'no data found exception thrown'
330 def thrownException = thrown(DataNodeNotFoundException)
331 assert thrownException.getMessage().contains('DataNode not found')
334 def 'Get multiple cm handle data nodes by alternate ids, passing empty collection'() {
335 when: 'getting the cm handle data node for no alternate ids'
336 objectUnderTest.getCmHandleDataNodesByAlternateIds([])
337 then: 'query service is not invoked'
338 0 * mockCmHandleQueries.queryNcmpRegistryByCpsPath(_, _)
341 def 'Get CM handle ids for CM Handles that has given module names'() {
342 when: 'the method to get cm handles is called'
343 objectUnderTest.getCmHandleReferencesWithGivenModules(['sample-module-name'], false)
344 then: 'the admin persistence service method to query anchors is invoked once with the same parameter'
345 1 * mockCpsAnchorService.queryAnchorNames(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, ['sample-module-name'])
348 def 'Get Alternate Ids for CM Handles that has given module names'() {
349 given: 'A Collection of data nodes'
350 def dataNodes = [new DataNode(xpath: "/dmi-registry/cm-handles[@id='ch-1']", leaves: ['id': 'ch-1', 'alternate-id': 'alt-1'])]
351 when: 'the methods to get dataNodes is called and returns correct values'
352 mockCpsAnchorService.queryAnchorNames(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, ['sample-module-name']) >> ['ch-1']
353 mockCpsDataService.getDataNodesForMultipleXpaths(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, ["/dmi-registry/cm-handles[@id='ch-1']"], INCLUDE_ALL_DESCENDANTS) >> dataNodes
354 and: 'the method returns a result'
355 def result = objectUnderTest.getCmHandleReferencesWithGivenModules(['sample-module-name'], true)
356 then: 'the result contains the correct alternate Id'
357 assert result == ['alt-1'] as HashSet
360 def 'Replace list content'() {
361 when: 'replace list content method is called with xpath and data nodes collection'
362 objectUnderTest.replaceListContent('sample xpath', [new DataNode()])
363 then: 'the cps data service method to replace list content is invoked once with same parameters'
364 1 * mockCpsDataService.replaceListContent(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,'sample xpath', [new DataNode()], NO_TIMESTAMP);
367 def 'Delete data node via xPath'() {
368 when: 'Delete data node method is called with xpath as parameter'
369 objectUnderTest.deleteDataNode('sample dataNode xpath')
370 then: 'the cps data service method to delete data node is invoked once with the same xPath'
371 1 * mockCpsDataService.deleteDataNode(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, 'sample dataNode xpath', NO_TIMESTAMP);
374 def 'Delete multiple data nodes via xPath'() {
375 when: 'Delete data nodes method is called with multiple xpaths as parameters'
376 objectUnderTest.deleteDataNodes(['xpath1', 'xpath2'])
377 then: 'the cps data service method to delete data nodes is invoked once with the same xPaths'
378 1 * mockCpsDataService.deleteDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, ['xpath1', 'xpath2'], NO_TIMESTAMP);
381 def 'Check if cm handle exists for a given cm handle id'() {
382 given: 'data service returns a datanode with correct cm handle id'
383 mockCpsDataService.getDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, xpath, INCLUDE_ALL_DESCENDANTS) >> [dataNode]
384 expect: 'cm handle exists for given cm handle id'
385 assert true == objectUnderTest.isExistingCmHandleId('some-cm-handle')