1830f1331df8eedd39694deb05e0ae2b142e45eb
[cps.git] /
1 /*
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
10  *
11  *        http://www.apache.org/licenses/LICENSE-2.0
12  *
13  *  Unless required by applicable law or agreed to in writing, software
14  *  distributed under the License is distributed on an "AS IS" BASIS,
15  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  *  See the License for the specific language governing permissions and
17  *  limitations under the License.
18  *
19  *  SPDX-License-Identifier: Apache-2.0
20  *  ============LICENSE_END=========================================================
21  */
22
23 package org.onap.cps.ncmp.impl.inventory
24
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
43
44 import java.time.OffsetDateTime
45 import java.time.ZoneOffset
46 import java.time.format.DateTimeFormatter
47
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
55
56 class InventoryPersistenceImplSpec extends Specification {
57
58     def spiedJsonObjectMapper = Spy(new JsonObjectMapper(new ObjectMapper()))
59
60     def mockCpsDataService = Mock(CpsDataService)
61
62     def mockCpsModuleService = Mock(CpsModuleService)
63
64     def mockCpsAnchorService = Mock(CpsAnchorService)
65
66     def mockCpsValidator = Mock(CpsValidator)
67
68     def mockCmHandleQueries = Mock(CmHandleQueryService)
69
70     def objectUnderTest = new InventoryPersistenceImpl(spiedJsonObjectMapper, mockCpsDataService, mockCpsModuleService,
71             mockCpsValidator, mockCpsAnchorService, mockCmHandleQueries)
72
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))
75
76     def cmHandleId = 'some-cm-handle'
77     def leaves = ["id":cmHandleId,"dmi-service-name":"common service name","dmi-data-service-name":"data service name","dmi-model-service-name":"model service name"]
78     def xpath = "/dmi-registry/cm-handles[@id='some-cm-handle']"
79
80     def cmHandleId2 = 'another-cm-handle'
81     def xpath2 = "/dmi-registry/cm-handles[@id='another-cm-handle']"
82
83     @Shared
84     def childDataNodesForCmHandleWithAllProperties = [new DataNode(xpath: "/dmi-registry/cm-handles[@id='some cm handle']/additional-properties[@name='name1']", leaves: ["name":"name1", "value":"value1"]),
85                                                       new DataNode(xpath: "/dmi-registry/cm-handles[@id='some cm handle']/public-properties[@name='name2']", leaves: ["name":"name2","value":"value2"])]
86
87     @Shared
88     def childDataNodesForCmHandleWithDMIProperties = [new DataNode(xpath: "/dmi-registry/cm-handles[@id='some-cm-handle']/additional-properties[@name='name1']", leaves: ["name":"name1", "value":"value1"])]
89
90     @Shared
91     def childDataNodesForCmHandleWithPublicProperties = [new DataNode(xpath: "/dmi-registry/cm-handles[@id='some-cm-handle']/public-properties[@name='name2']", leaves: ["name":"name2","value":"value2"])]
92
93     @Shared
94     def childDataNodesForCmHandleWithState = [new DataNode(xpath: "/dmi-registry/cm-handles[@id='some-cm-handle']/state", leaves: ['cm-handle-state': 'ADVISED'])]
95
96     def "Retrieve CmHandle using datanode with #scenario."() {
97         given: 'the cps data service returns a data node from the DMI registry'
98             def dataNode = new DataNode(childDataNodes:childDataNodes, leaves: leaves)
99             mockCpsDataService.getDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, xpath, INCLUDE_ALL_DESCENDANTS) >> [dataNode]
100         when: 'retrieving the yang modelled cm handle'
101             def result = objectUnderTest.getYangModelCmHandle(cmHandleId)
102         then: 'the result has the correct id and service names'
103             result.id == cmHandleId
104             result.dmiServiceName == 'common service name'
105             result.dmiDataServiceName == 'data service name'
106             result.dmiModelServiceName == 'model service name'
107         and: 'the expected DMI properties'
108             result.dmiProperties == expectedDmiProperties
109             result.publicProperties == expectedPublicProperties
110         and: 'the state details are returned'
111             result.compositeState.cmHandleState == expectedCompositeState
112         and: 'the CM Handle ID is validated'
113             1 * mockCpsValidator.validateNameCharacters(cmHandleId)
114         where: 'the following parameters are used'
115             scenario                    | childDataNodes                                || expectedDmiProperties                               || expectedPublicProperties                              || expectedCompositeState
116             'no properties'             | []                                            || []                                                  || []                                                    || null
117             'DMI and public properties' | childDataNodesForCmHandleWithAllProperties    || [new YangModelCmHandle.Property("name1", "value1")] || [new YangModelCmHandle.Property("name2", "value2")]   || null
118             'just DMI properties'       | childDataNodesForCmHandleWithDMIProperties    || [new YangModelCmHandle.Property("name1", "value1")] || []                                                    || null
119             'just public properties'    | childDataNodesForCmHandleWithPublicProperties || []                                                  || [new YangModelCmHandle.Property("name2", "value2")]   || null
120             'with state details'        | childDataNodesForCmHandleWithState            || []                                                  || []                                                    || CmHandleState.ADVISED
121     }
122
123     def "Handling missing service names as null."() {
124         given: 'the cps data service returns a data node from the DMI registry with empty child and leaf attributes'
125             def dataNode = new DataNode(childDataNodes:[], leaves: ['id':cmHandleId])
126             mockCpsDataService.getDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, xpath, INCLUDE_ALL_DESCENDANTS) >> [dataNode]
127         when: 'retrieving the yang modelled cm handle'
128             def result = objectUnderTest.getYangModelCmHandle(cmHandleId)
129         then: 'the service names are returned as null'
130             result.dmiServiceName == null
131             result.dmiDataServiceName == null
132             result.dmiModelServiceName == null
133         and: 'the CM Handle ID is validated'
134             1 * mockCpsValidator.validateNameCharacters(cmHandleId)
135     }
136
137     def "Retrieve multiple YangModelCmHandles"() {
138         given: 'the cps data service returns 2 data nodes from the DMI registry'
139             def dataNodes = [new DataNode(xpath: xpath, leaves: ['id': cmHandleId]), new DataNode(xpath: xpath2, leaves: ['id': cmHandleId2])]
140             mockCpsDataService.getDataNodesForMultipleXpaths(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, [xpath, xpath2] , INCLUDE_ALL_DESCENDANTS) >> dataNodes
141         when: 'retrieving the yang modelled cm handle'
142             def results = objectUnderTest.getYangModelCmHandles([cmHandleId, cmHandleId2])
143         then: 'verify both have returned and cmhandleIds are correct'
144             assert results.size() == 2
145             assert results.id.containsAll([cmHandleId, cmHandleId2])
146     }
147
148     def 'Get a Cm Handle Composite State'() {
149         given: 'a valid cm handle id'
150             def cmHandleId = 'Some-Cm-Handle'
151             def dataNode = new DataNode(leaves: ['cm-handle-state': 'ADVISED'])
152         and: 'cps data service returns a valid data node'
153             mockCpsDataService.getDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
154                     '/dmi-registry/cm-handles[@id=\'Some-Cm-Handle\']/state', FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> [dataNode]
155         when: 'get cm handle state is invoked'
156             def result = objectUnderTest.getCmHandleState(cmHandleId)
157         then: 'result has returned the correct cm handle state'
158             result.cmHandleState == CmHandleState.ADVISED
159         and: 'the CM Handle ID is validated'
160             1 * mockCpsValidator.validateNameCharacters(cmHandleId)
161     }
162
163     def 'Update Cm Handle with #scenario State'() {
164         given: 'a cm handle and a composite state'
165             def cmHandleId = 'Some-Cm-Handle'
166             def compositeState = new CompositeState(cmHandleState: cmHandleState, lastUpdateTime: formattedDateAndTime)
167         when: 'update cm handle state is invoked with the #scenario state'
168             objectUnderTest.saveCmHandleState(cmHandleId, compositeState)
169         then: 'update node leaves is invoked with the correct params'
170             1 * mockCpsDataService.updateDataNodeAndDescendants(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, '/dmi-registry/cm-handles[@id=\'Some-Cm-Handle\']', expectedJsonData, _ as OffsetDateTime, ContentType.JSON)
171         where: 'the following states are used'
172             scenario    | cmHandleState          || expectedJsonData
173             'READY'     | CmHandleState.READY    || '{"state":{"cm-handle-state":"READY","last-update-time":"2022-12-31T20:30:40.000+0000"}}'
174             'LOCKED'    | CmHandleState.LOCKED   || '{"state":{"cm-handle-state":"LOCKED","last-update-time":"2022-12-31T20:30:40.000+0000"}}'
175             'DELETING'  | CmHandleState.DELETING || '{"state":{"cm-handle-state":"DELETING","last-update-time":"2022-12-31T20:30:40.000+0000"}}'
176     }
177
178     def 'Update Cm Handles with #scenario States'() {
179         given: 'a map of cm handles composite states'
180             def compositeState1 = new CompositeState(cmHandleState: cmHandleState, lastUpdateTime: formattedDateAndTime)
181             def compositeState2 = new CompositeState(cmHandleState: cmHandleState, lastUpdateTime: formattedDateAndTime)
182         when: 'update cm handle state is invoked with the #scenario state'
183             def cmHandleStateMap = ['Some-Cm-Handle1' : compositeState1, 'Some-Cm-Handle2' : compositeState2]
184             objectUnderTest.saveCmHandleStateBatch(cmHandleStateMap)
185         then: 'update node leaves is invoked with the correct params'
186             1 * mockCpsDataService.updateDataNodesAndDescendants(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, cmHandlesJsonDataMap, _ as OffsetDateTime, ContentType.JSON)
187         where: 'the following states are used'
188             scenario    | cmHandleState          || cmHandlesJsonDataMap
189             '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"}}']
190             '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"}}']
191             '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"}}']
192     }
193
194     def 'Getting module definitions by module'() {
195         given: 'cps module service returns module definition for module name'
196             def moduleDefinitions = [new ModuleDefinition('moduleName','revision','content')]
197             mockCpsModuleService.getModuleDefinitionsByAnchorAndModule(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME,'some-cmHandle-Id', 'some-module', '2024-01-25') >> moduleDefinitions
198         when: 'get module definitions is invoked with module name'
199             def result = objectUnderTest.getModuleDefinitionsByCmHandleAndModule('some-cmHandle-Id', 'some-module', '2024-01-25')
200         then: 'returned result are the same module definitions as returned from module service'
201             assert result == moduleDefinitions
202         and: 'cm handle id and module name validated'
203             1 * mockCpsValidator.validateNameCharacters('some-cmHandle-Id', 'some-module')
204     }
205
206     def 'Getting module definitions with cm handle id'() {
207         given: 'cps module service returns module definitions for cm handle id'
208             def moduleDefinitions = [new ModuleDefinition('moduleName','revision','content')]
209             mockCpsModuleService.getModuleDefinitionsByAnchorName(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME,'some-cmHandle-Id') >> moduleDefinitions
210         when: 'get module definitions is invoked with cm handle id'
211             def result = objectUnderTest.getModuleDefinitionsByCmHandleId('some-cmHandle-Id')
212         then: 'the returned result are the same module definitions as returned from the module service'
213             assert result == moduleDefinitions
214     }
215
216     def 'Get module references'() {
217         given: 'cps module service returns a collection of module references'
218             def moduleReferences = [new ModuleReference('moduleName','revision','namespace')]
219             mockCpsModuleService.getYangResourcesModuleReferences(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME,'some-cmHandle-Id') >> moduleReferences
220         when: 'get yang resources module references by cmHandle is invoked'
221             def result = objectUnderTest.getYangResourcesModuleReferences('some-cmHandle-Id')
222         then: 'the returned result is a collection of module definitions'
223             assert result == moduleReferences
224         and: 'the CM Handle ID is validated'
225             1 * mockCpsValidator.validateNameCharacters('some-cmHandle-Id')
226     }
227
228     def 'Save Cmhandle'() {
229         given: 'cmHandle represented as Yang Model'
230             def yangModelCmHandle = new YangModelCmHandle(id: 'cmhandle', dmiProperties: [], publicProperties: [])
231         when: 'the method to save cmhandle is called'
232             objectUnderTest.saveCmHandle(yangModelCmHandle)
233         then: 'the data service method to save list elements is called once'
234             1 * mockCpsDataService.saveListElements(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, NCMP_DMI_REGISTRY_PARENT,
235                     _,null, ContentType.JSON) >> {
236                 args -> {
237                     assert args[3].startsWith('{"cm-handles":[{"id":"cmhandle","additional-properties":[],"public-properties":[]}]}')
238                 }
239             }
240     }
241
242     def 'Save Multiple Cmhandles'() {
243         given: 'cm handles represented as Yang Model'
244             def yangModelCmHandle1 = new YangModelCmHandle(id: 'cmhandle1')
245             def yangModelCmHandle2 = new YangModelCmHandle(id: 'cmhandle2')
246         when: 'the cm handles are saved'
247             objectUnderTest.saveCmHandleBatch([yangModelCmHandle1, yangModelCmHandle2])
248         then: 'CPS Data Service persists both cm handles as a batch'
249             1 * mockCpsDataService.saveListElements(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
250                     NCMP_DMI_REGISTRY_PARENT, _,null, ContentType.JSON) >> {
251                 args -> {
252                     def jsonData = (args[3] as String)
253                     jsonData.contains('cmhandle1')
254                     jsonData.contains('cmhandle2')
255                 }
256             }
257     }
258
259     def 'Delete list or list elements'() {
260         when: 'the method to delete list or list elements is called'
261             objectUnderTest.deleteListOrListElement('sample xPath')
262         then: 'the data service method to save list elements is called once'
263             1 * mockCpsDataService.deleteListOrListElement(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,'sample xPath',null)
264     }
265
266     def 'Delete schema set with a valid schema set name'() {
267         when: 'the method to delete schema set is called with valid schema set name'
268             objectUnderTest.deleteSchemaSetWithCascade('validSchemaSetName')
269         then: 'the module service to delete schemaSet is invoked once'
270             1 * mockCpsModuleService.deleteSchemaSet(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, 'validSchemaSetName', CascadeDeleteAllowed.CASCADE_DELETE_ALLOWED)
271         and: 'the schema set name is validated'
272             1 * mockCpsValidator.validateNameCharacters('validSchemaSetName')
273     }
274
275     def 'Delete multiple schema sets with valid schema set names'() {
276         when: 'the method to delete schema sets is called with valid schema set names'
277             objectUnderTest.deleteSchemaSetsWithCascade(['validSchemaSetName1', 'validSchemaSetName2'])
278         then: 'the module service to delete schema sets is invoked once'
279             1 * mockCpsModuleService.deleteSchemaSetsWithCascade(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, ['validSchemaSetName1', 'validSchemaSetName2'])
280         and: 'the schema set names are validated'
281             1 * mockCpsValidator.validateNameCharacters(['validSchemaSetName1', 'validSchemaSetName2'])
282     }
283
284     def 'Get data node via xPath'() {
285         when: 'the method to get data nodes is called'
286             objectUnderTest.getDataNode('sample xPath')
287         then: 'the data persistence service method to get data node is invoked once'
288             1 * mockCpsDataService.getDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,'sample xPath', INCLUDE_ALL_DESCENDANTS)
289     }
290
291     def 'Get cmHandle data node'() {
292         given: 'expected xPath to get cmHandle data node'
293             def expectedXPath = '/dmi-registry/cm-handles[@id=\'sample cmHandleId\']'
294         when: 'the method to get data nodes is called'
295             objectUnderTest.getCmHandleDataNodeByCmHandleId('sample cmHandleId')
296         then: 'the data persistence service method to get cmHandle data node is invoked once with expected xPath'
297             1 * mockCpsDataService.getDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, expectedXPath, INCLUDE_ALL_DESCENDANTS)
298     }
299
300     def 'Get cm handle data node by alternate id'() {
301         given: 'expected xPath to get cmHandle data node'
302             def expectedXPath = '/dmi-registry/cm-handles[@alternate-id=\'alternate id\']'
303         and: 'query service is invoked with expected xpath'
304             mockCmHandleQueries.queryNcmpRegistryByCpsPath(expectedXPath, OMIT_DESCENDANTS) >> [new DataNode()]
305         expect: 'getting the cm handle data node'
306             assert objectUnderTest.getCmHandleDataNodeByAlternateId('alternate id') == new DataNode()
307     }
308
309     def 'Attempt to get non existing cm handle data node by alternate id'() {
310         given: 'query service is invoked and returns empty collection of data nodes'
311             mockCmHandleQueries.queryNcmpRegistryByCpsPath(*_) >> []
312         when: 'getting the cm handle data node'
313             objectUnderTest.getCmHandleDataNodeByAlternateId('alternate id')
314         then: 'no data found exception thrown'
315             def thrownException = thrown(DataNodeNotFoundException)
316             assert thrownException.getMessage().contains('DataNode not found')
317     }
318
319     def 'Get multiple cm handle data nodes by alternate ids'() {
320         given: 'expected xPath to get cmHandle data node'
321             def expectedXPath = "/dmi-registry/cm-handles[@alternate-id='A' or @alternate-id='B']"
322         when: 'getting the cm handle data node'
323             objectUnderTest.getCmHandleDataNodesByAlternateIds(['A', 'B'])
324         then: 'query service is invoked with expected xpath'
325             1 * mockCmHandleQueries.queryNcmpRegistryByCpsPath(expectedXPath, OMIT_DESCENDANTS)
326     }
327
328     def 'Get multiple cm handle data nodes by alternate ids, passing empty collection'() {
329         when: 'getting the cm handle data node for no alternate ids'
330             objectUnderTest.getCmHandleDataNodesByAlternateIds([])
331         then: 'query service is not invoked'
332             0 * mockCmHandleQueries.queryNcmpRegistryByCpsPath(_, _)
333     }
334
335     def 'Get CM handles that has given module names'() {
336         when: 'the method to get cm handles is called'
337             objectUnderTest.getCmHandleIdsWithGivenModules(['sample-module-name'])
338         then: 'the admin persistence service method to query anchors is invoked once with the same parameter'
339             1 * mockCpsAnchorService.queryAnchorNames(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, ['sample-module-name'])
340     }
341
342     def 'Replace list content'() {
343         when: 'replace list content method is called with xpath and data nodes collection'
344             objectUnderTest.replaceListContent('sample xpath', [new DataNode()])
345         then: 'the cps data service method to replace list content is invoked once with same parameters'
346             1 * mockCpsDataService.replaceListContent(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,'sample xpath', [new DataNode()], NO_TIMESTAMP);
347     }
348
349     def 'Delete data node via xPath'() {
350         when: 'Delete data node method is called with xpath as parameter'
351             objectUnderTest.deleteDataNode('sample dataNode xpath')
352         then: 'the cps data service method to delete data node is invoked once with the same xPath'
353             1 * mockCpsDataService.deleteDataNode(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, 'sample dataNode xpath', NO_TIMESTAMP);
354     }
355
356     def 'Delete multiple data nodes via xPath'() {
357         when: 'Delete data nodes method is called with multiple xpaths as parameters'
358             objectUnderTest.deleteDataNodes(['xpath1', 'xpath2'])
359         then: 'the cps data service method to delete data nodes is invoked once with the same xPaths'
360             1 * mockCpsDataService.deleteDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, ['xpath1', 'xpath2'], NO_TIMESTAMP);
361     }
362 }