9b4fe146b99ebca7aac8977f7db492da3c8dbec3
[cps.git] / cps-ncmp-service / src / test / groovy / org / onap / cps / ncmp / api / impl / NetworkCmProxyDataServiceImplSpec.groovy
1 /*
2  *  ============LICENSE_START=======================================================
3  *  Copyright (C) 2021-2024 Nordix Foundation
4  *  Modifications Copyright (C) 2021 Pantheon.tech
5  *  Modifications Copyright (C) 2021-2022 Bell Canada
6  *  Modifications Copyright (C) 2023 TechMahindra Ltd.
7  *  ================================================================================
8  *  Licensed under the Apache License, Version 2.0 (the "License");
9  *  you may not use this file except in compliance with the License.
10  *  You may obtain a copy of the License at
11  *
12  *        http://www.apache.org/licenses/LICENSE-2.0
13  *
14  *  Unless required by applicable law or agreed to in writing, software
15  *  distributed under the License is distributed on an "AS IS" BASIS,
16  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  *  See the License for the specific language governing permissions and
18  *  limitations under the License.
19  *
20  *  SPDX-License-Identifier: Apache-2.0
21  *  ============LICENSE_END=========================================================
22  */
23
24 package org.onap.cps.ncmp.api.impl
25
26 import org.onap.cps.ncmp.api.models.DmiPluginRegistrationResponse
27
28 import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME
29 import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DATASPACE_NAME
30 import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DMI_REGISTRY_ANCHOR
31 import static org.onap.cps.ncmp.api.impl.operations.DatastoreType.OPERATIONAL
32 import static org.onap.cps.ncmp.api.impl.operations.DatastoreType.PASSTHROUGH_OPERATIONAL
33 import static org.onap.cps.ncmp.api.impl.operations.DatastoreType.PASSTHROUGH_RUNNING
34 import static org.onap.cps.ncmp.api.impl.operations.OperationType.CREATE
35 import static org.onap.cps.ncmp.api.impl.operations.OperationType.UPDATE
36
37 import org.onap.cps.ncmp.api.impl.utils.AlternateIdChecker
38 import com.hazelcast.map.IMap
39 import org.onap.cps.ncmp.api.NetworkCmProxyCmHandleQueryService
40 import org.onap.cps.ncmp.api.impl.events.lcm.LcmEventsCmHandleStateHandler
41 import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevel
42 import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevelManager
43 import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle
44 import org.onap.cps.ncmp.api.impl.inventory.CmHandleQueries
45 import org.onap.cps.ncmp.api.impl.inventory.CmHandleState
46 import org.onap.cps.ncmp.api.impl.inventory.CompositeState
47 import org.onap.cps.ncmp.api.impl.inventory.InventoryPersistence
48 import org.onap.cps.ncmp.api.impl.inventory.LockReasonCategory
49 import org.onap.cps.ncmp.api.impl.inventory.DataStoreSyncState
50 import org.onap.cps.ncmp.api.models.DataOperationDefinition
51 import org.onap.cps.ncmp.api.models.CmHandleQueryApiParameters
52 import org.onap.cps.ncmp.api.models.CmHandleQueryServiceParameters
53 import org.onap.cps.ncmp.api.models.ConditionApiProperties
54 import org.onap.cps.ncmp.api.models.DmiPluginRegistration
55 import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle
56 import org.onap.cps.ncmp.api.models.DataOperationRequest
57 import org.onap.cps.spi.exceptions.CpsException
58 import org.onap.cps.spi.model.ConditionProperties
59
60 import java.util.stream.Collectors
61 import org.onap.cps.utils.JsonObjectMapper
62 import com.fasterxml.jackson.databind.ObjectMapper
63 import org.onap.cps.api.CpsDataService
64 import org.onap.cps.ncmp.api.impl.operations.DmiDataOperations
65 import org.onap.cps.spi.FetchDescendantsOption
66 import org.onap.cps.spi.model.DataNode
67 import org.springframework.http.HttpStatus
68 import org.springframework.http.ResponseEntity
69 import spock.lang.Specification
70
71 class NetworkCmProxyDataServiceImplSpec extends Specification {
72
73     def mockCpsDataService = Mock(CpsDataService)
74     def spiedJsonObjectMapper = Spy(new JsonObjectMapper(new ObjectMapper()))
75     def mockDmiDataOperations = Mock(DmiDataOperations)
76     def nullNetworkCmProxyDataServicePropertyHandler = null
77     def mockInventoryPersistence = Mock(InventoryPersistence)
78     def mockCmHandleQueries = Mock(CmHandleQueries)
79     def mockDmiPluginRegistration = Mock(DmiPluginRegistration)
80     def mockCpsCmHandlerQueryService = Mock(NetworkCmProxyCmHandleQueryService)
81     def mockLcmEventsCmHandleStateHandler = Mock(LcmEventsCmHandleStateHandler)
82     def stubModuleSyncStartedOnCmHandles = Stub(IMap<String, Object>)
83     def stubTrustLevelPerDmiPlugin = Stub(Map<String, TrustLevel>)
84     def mockTrustLevelManager = Mock(TrustLevelManager)
85     def mockAlternateIdChecker = Mock(AlternateIdChecker)
86
87     def NO_TOPIC = null
88     def NO_REQUEST_ID = null
89     def OPTIONS_PARAM = '(a=1,b=2)'
90     def ncmpServiceCmHandle = new NcmpServiceCmHandle(cmHandleId: 'test-cm-handle-id')
91
92     def objectUnderTest = new NetworkCmProxyDataServiceImpl(
93             spiedJsonObjectMapper,
94             mockDmiDataOperations,
95             nullNetworkCmProxyDataServicePropertyHandler,
96             mockInventoryPersistence,
97             mockCmHandleQueries,
98             mockCpsCmHandlerQueryService,
99             mockLcmEventsCmHandleStateHandler,
100             mockCpsDataService,
101             stubModuleSyncStartedOnCmHandles,
102             stubTrustLevelPerDmiPlugin,
103             mockTrustLevelManager,
104             mockAlternateIdChecker)
105
106     def cmHandleXPath = "/dmi-registry/cm-handles[@id='testCmHandle']"
107
108     def dataNode = [new DataNode(leaves: ['id': 'some-cm-handle', 'dmi-service-name': 'testDmiService'])]
109
110     def 'Write resource data for pass-through running from DMI using POST.'() {
111         given: 'cpsDataService returns valid datanode'
112             mockDataNode()
113         when: 'write resource data is called'
114             objectUnderTest.writeResourceDataPassThroughRunningForCmHandle('testCmHandle',
115                 'testResourceId', CREATE,
116                 '{some-json}', 'application/json')
117         then: 'DMI called with correct data'
118             1 * mockDmiDataOperations.writeResourceDataPassThroughRunningFromDmi('testCmHandle', 'testResourceId',
119                     CREATE, '{some-json}', 'application/json')
120                 >> { new ResponseEntity<>(HttpStatus.CREATED) }
121     }
122
123     def 'Get resource data for pass-through operational from DMI.'() {
124         given: 'cpsDataService returns valid data node'
125             mockDataNode()
126         and: 'get resource data from DMI is called'
127             mockDmiDataOperations.getResourceDataFromDmi(PASSTHROUGH_OPERATIONAL.datastoreName,'testCmHandle', 'testResourceId', OPTIONS_PARAM, NO_TOPIC, NO_REQUEST_ID) >>
128                     new ResponseEntity<>('dmi-response', HttpStatus.OK)
129         when: 'get resource data operational for cm-handle is called'
130             def response = objectUnderTest.getResourceDataForCmHandle(PASSTHROUGH_OPERATIONAL.datastoreName, 'testCmHandle', 'testResourceId', OPTIONS_PARAM, NO_TOPIC, NO_REQUEST_ID)
131         then: 'DMI returns a json response'
132             assert response == 'dmi-response'
133     }
134
135     def 'Get resource data for pass-through running from DMI.'() {
136         given: 'cpsDataService returns valid data node'
137             mockDataNode()
138         and: 'DMI returns valid response and data'
139             mockDmiDataOperations.getResourceDataFromDmi(PASSTHROUGH_RUNNING.datastoreName, 'testCmHandle', 'testResourceId', OPTIONS_PARAM, NO_TOPIC, NO_REQUEST_ID) >>
140                     new ResponseEntity<>('{dmi-response}', HttpStatus.OK)
141         when: 'get resource data is called'
142             def response = objectUnderTest.getResourceDataForCmHandle(PASSTHROUGH_RUNNING.datastoreName, 'testCmHandle', 'testResourceId', OPTIONS_PARAM, NO_TOPIC, NO_REQUEST_ID)
143         then: 'get resource data returns expected response'
144             assert response == '{dmi-response}'
145     }
146
147     def 'Get resource data for operational (cached) data.'() {
148         given: 'CPS Data service returns some object(s)'
149             mockCpsDataService.getDataNodes(OPERATIONAL.datastoreName, 'testCmHandle', 'testResourceId', FetchDescendantsOption.OMIT_DESCENDANTS) >> ['First Object', 'other Object']
150         when: 'get resource data is called'
151             def response = objectUnderTest.getResourceDataForCmHandle(OPERATIONAL.datastoreName, 'testCmHandle', 'testResourceId', FetchDescendantsOption.OMIT_DESCENDANTS)
152         then: 'get resource data returns teh first object from the data service'
153             assert response == 'First Object'
154     }
155
156     def 'Execute (async) data operation for #datastoreName from DMI.'() {
157         given: 'cpsDataService returns valid data node'
158             def dataOperationRequest = getDataOperationRequest(datastoreName)
159         when: 'request resource data for data operation is called'
160             objectUnderTest.executeDataOperationForCmHandles('some topic', dataOperationRequest, 'requestId')
161         then: 'request resource data for data operation returns expected response'
162             1 * mockDmiDataOperations.requestResourceDataFromDmi('some topic', dataOperationRequest, 'requestId')
163         where: 'the following data stores are used'
164             datastoreName << [PASSTHROUGH_RUNNING.datastoreName, PASSTHROUGH_OPERATIONAL.datastoreName]
165     }
166
167     def 'Getting Yang Resources.'() {
168         when: 'yang resources is called'
169             objectUnderTest.getYangResourcesModuleReferences('some-cm-handle')
170         then: 'CPS module services is invoked for the correct dataspace and cm handle'
171             1 * mockInventoryPersistence.getYangResourcesModuleReferences('some-cm-handle')
172     }
173
174     def 'Get a cm handle.'() {
175         given: 'the system returns a yang modelled cm handle'
176             def dmiServiceName = 'some service name'
177             def compositeState = new CompositeState(cmHandleState: CmHandleState.ADVISED,
178                     lockReason: CompositeState.LockReason.builder().lockReasonCategory(LockReasonCategory.MODULE_SYNC_FAILED).details("lock details").build(),
179                     lastUpdateTime: 'some-timestamp',
180                     dataSyncEnabled: false,
181                     dataStores: dataStores())
182             def dmiProperties = [new YangModelCmHandle.Property('Book', 'Romance Novel')]
183             def publicProperties = [new YangModelCmHandle.Property('Public Book', 'Public Romance Novel')]
184             def moduleSetTag = 'some-module-set-tag'
185             def alternateId = 'some-alternate-id'
186             def yangModelCmHandle = new YangModelCmHandle(id: 'some-cm-handle', dmiServiceName: dmiServiceName,
187                     dmiProperties: dmiProperties, publicProperties: publicProperties, compositeState: compositeState,
188                     moduleSetTag: moduleSetTag, alternateId: alternateId)
189             1 * mockInventoryPersistence.getYangModelCmHandle('some-cm-handle') >> yangModelCmHandle
190         when: 'getting cm handle details for a given cm handle id from ncmp service'
191             def result = objectUnderTest.getNcmpServiceCmHandle('some-cm-handle')
192         then: 'the result is a ncmpServiceCmHandle'
193             result.class == NcmpServiceCmHandle.class
194         and: 'the cm handle contains the cm handle id'
195             result.cmHandleId == 'some-cm-handle'
196         and: 'the cm handle contains the alternate id'
197             result.alternateId == 'some-alternate-id'
198         and: 'the cm handle contains the module-set-tag'
199             result.moduleSetTag == 'some-module-set-tag'
200         and: 'the cm handle contains the DMI Properties'
201             result.dmiProperties ==[ Book:'Romance Novel' ]
202         and: 'the cm handle contains the public Properties'
203             result.publicProperties == [ "Public Book":'Public Romance Novel' ]
204         and: 'the cm handle contains the cm handle composite state'
205             result.compositeState == compositeState
206     }
207
208     def 'Get cm handle public properties'() {
209         given: 'a yang modelled cm handle'
210             def dmiProperties = [new YangModelCmHandle.Property('prop', 'some DMI property')]
211             def publicProperties = [new YangModelCmHandle.Property('public prop', 'some public prop')]
212             def yangModelCmHandle = new YangModelCmHandle(id:'some-cm-handle', dmiServiceName: 'some service name', dmiProperties: dmiProperties, publicProperties: publicProperties)
213         and: 'the system returns this yang modelled cm handle'
214             1 * mockInventoryPersistence.getYangModelCmHandle('some-cm-handle') >> yangModelCmHandle
215         when: 'getting cm handle public properties for a given cm handle id from ncmp service'
216             def result = objectUnderTest.getCmHandlePublicProperties('some-cm-handle')
217         then: 'the result returns the correct data'
218             result == [ 'public prop' : 'some public prop' ]
219     }
220
221     def 'Execute cm handle id search for inventory'() {
222         given: 'a ConditionApiProperties object'
223             def conditionProperties = new ConditionProperties()
224             conditionProperties.conditionName = 'hasAllProperties'
225             conditionProperties.conditionParameters = [ [ 'some-key' : 'some-value' ] ]
226             def conditionServiceProps = new CmHandleQueryServiceParameters()
227             conditionServiceProps.cmHandleQueryParameters = [conditionProperties] as List<ConditionProperties>
228         and: 'the system returns an set of cmHandle ids'
229             mockCpsCmHandlerQueryService.queryCmHandleIdsForInventory(*_) >> [ 'cmHandle1', 'cmHandle2' ]
230         when: 'getting cm handle id set for a given dmi property'
231             def result = objectUnderTest.executeCmHandleIdSearchForInventory(conditionServiceProps)
232         then: 'the result returns the correct 2 elements'
233             assert result.size() == 2
234             assert result.contains('cmHandle1')
235             assert result.contains('cmHandle2')
236     }
237
238     def 'Get cm handle composite state'() {
239         given: 'a yang modelled cm handle'
240             def compositeState = new CompositeState(cmHandleState: CmHandleState.ADVISED,
241                 lockReason: CompositeState.LockReason.builder().lockReasonCategory(LockReasonCategory.MODULE_SYNC_FAILED).details("lock details").build(),
242                 lastUpdateTime: 'some-timestamp',
243                 dataSyncEnabled: false,
244                 dataStores: dataStores())
245             def dmiProperties = [new YangModelCmHandle.Property('prop', 'some DMI property')]
246             def publicProperties = [new YangModelCmHandle.Property('public prop', 'some public prop')]
247             def yangModelCmHandle = new YangModelCmHandle(id:'some-cm-handle', dmiServiceName: 'some service name', dmiProperties: dmiProperties, publicProperties: publicProperties, compositeState: compositeState)
248         and: 'the system returns this yang modelled cm handle'
249             1 * mockInventoryPersistence.getYangModelCmHandle('some-cm-handle') >> yangModelCmHandle
250         when: 'getting cm handle composite state for a given cm handle id from ncmp service'
251             def result = objectUnderTest.getCmHandleCompositeState('some-cm-handle')
252         then: 'the result returns the correct data'
253             result == compositeState
254     }
255
256     def 'Update resource data for pass-through running from dmi using POST #scenario DMI properties.'() {
257         given: 'cpsDataService returns valid datanode'
258             mockCpsDataService.getDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> dataNode
259         when: 'get resource data is called'
260             objectUnderTest.writeResourceDataPassThroughRunningForCmHandle('testCmHandle',
261                 'testResourceId', UPDATE,
262                 '{some-json}', 'application/json')
263         then: 'DMI called with correct data'
264             1 * mockDmiDataOperations.writeResourceDataPassThroughRunningFromDmi('testCmHandle', 'testResourceId',
265                     UPDATE, '{some-json}', 'application/json')
266                 >> { new ResponseEntity<>(HttpStatus.OK) }
267     }
268
269     def 'Verify modules and create anchor params.'() {
270         given: 'dmi plugin registration return created cm handles'
271             def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'service1', dmiModelPlugin: 'service1',
272                     dmiDataPlugin: 'service2')
273             dmiPluginRegistration.createdCmHandles = [ncmpServiceCmHandle]
274             mockDmiPluginRegistration.getCreatedCmHandles() >> [ncmpServiceCmHandle]
275         and: 'no rejected cm handles because of alternate ids'
276             mockAlternateIdChecker.getIdsOfCmHandlesWithRejectedAlternateId(_) >> []
277         when: 'parse and create cm handle in dmi registration then sync module'
278             mockDmiPluginRegistration.createdCmHandles = ['test-cm-handle-id']
279             objectUnderTest.processCreatedCmHandles(mockDmiPluginRegistration, new DmiPluginRegistrationResponse())
280         then: 'system persists the cm handle state'
281             1 * mockLcmEventsCmHandleStateHandler.initiateStateAdvised(_) >> {
282                 args -> {
283                           def cmHandleStatePerCmHandle = (args[0] as Collection)
284                           cmHandleStatePerCmHandle.each {
285                             assert it.id == 'test-cm-handle-id'
286                           }
287                     }
288             }
289     }
290
291     def 'Execute cm handle id search'() {
292         given: 'valid CmHandleQueryApiParameters input'
293             def cmHandleQueryApiParameters = new CmHandleQueryApiParameters()
294             def conditionApiProperties = new ConditionApiProperties()
295             conditionApiProperties.conditionName = 'hasAllModules'
296             conditionApiProperties.conditionParameters = [[moduleName: 'module-name-1']]
297             cmHandleQueryApiParameters.cmHandleQueryParameters = [conditionApiProperties]
298         and: 'query cm handle method return with a data node list'
299             mockCpsCmHandlerQueryService.queryCmHandleIds(
300                     spiedJsonObjectMapper.convertToValueType(cmHandleQueryApiParameters, CmHandleQueryServiceParameters.class))
301                     >> ['cm-handle-id-1']
302         when: 'execute cm handle search is called'
303             def result = objectUnderTest.executeCmHandleIdSearch(cmHandleQueryApiParameters)
304         then: 'result is the same collection as returned by the CPS Data Service'
305             assert result == ['cm-handle-id-1']
306     }
307
308     def 'Getting module definitions by module'() {
309         when: 'get module definitions is performed with module name'
310             objectUnderTest.getModuleDefinitionsByCmHandleAndModule('some-cm-handle', 'some-module', '2021-08-04')
311         then: 'ncmp inventory persistence service is invoked once with correct parameters'
312             1 * mockInventoryPersistence.getModuleDefinitionsByCmHandleAndModule('some-cm-handle', 'some-module', '2021-08-04')
313     }
314
315     def 'Getting module definitions by cm handle id'() {
316         when: 'get module definitions is performed with cm handle id'
317             objectUnderTest.getModuleDefinitionsByCmHandleId('some-cm-handle')
318         then: 'ncmp inventory persistence service is invoked once with correct parameter'
319             1 * mockInventoryPersistence.getModuleDefinitionsByCmHandleId('some-cm-handle')
320     }
321
322     def 'Execute cm handle search'() {
323         given: 'valid CmHandleQueryApiParameters input'
324             def cmHandleQueryApiParameters = new CmHandleQueryApiParameters()
325             def conditionApiProperties = new ConditionApiProperties()
326             conditionApiProperties.conditionName = 'hasAllModules'
327             conditionApiProperties.conditionParameters = [[moduleName: 'module-name-1']]
328             cmHandleQueryApiParameters.cmHandleQueryParameters = [conditionApiProperties]
329         and: 'query cm handle method return with a data node list'
330             mockCpsCmHandlerQueryService.queryCmHandles(
331                     spiedJsonObjectMapper.convertToValueType(cmHandleQueryApiParameters, CmHandleQueryServiceParameters.class))
332                     >> [new NcmpServiceCmHandle(cmHandleId: 'cm-handle-id-1')]
333         when: 'execute cm handle search is called'
334             def result = objectUnderTest.executeCmHandleSearch(cmHandleQueryApiParameters)
335         then: 'result is the same collection as returned by the CPS Data Service'
336             assert result.stream().map(d -> d.cmHandleId).collect(Collectors.toSet()) == ['cm-handle-id-1'] as Set
337     }
338
339     def 'Set Cm Handle Data Sync Enabled Flag where data sync flag is  #scenario'() {
340         given: 'an existing cm handle composite state'
341             def compositeState = new CompositeState(cmHandleState: CmHandleState.READY, dataSyncEnabled: initialDataSyncEnabledFlag,
342                 dataStores: CompositeState.DataStores.builder()
343                     .operationalDataStore(CompositeState.Operational.builder()
344                         .dataStoreSyncState(initialDataSyncState)
345                         .build()).build())
346         and: 'get cm handle state returns the composite state for the given cm handle id'
347             mockInventoryPersistence.getCmHandleState('some-cm-handle-id') >> compositeState
348         when: 'set data sync enabled is called with the data sync enabled flag set to #dataSyncEnabledFlag'
349             objectUnderTest.setDataSyncEnabled('some-cm-handle-id', dataSyncEnabledFlag)
350         then: 'the data sync enabled flag is set to #dataSyncEnabled'
351             compositeState.dataSyncEnabled == dataSyncEnabledFlag
352         and: 'the data store sync state is set to #expectedDataStoreSyncState'
353             compositeState.dataStores.operationalDataStore.dataStoreSyncState == expectedDataStoreSyncState
354         and: 'the cps data service to delete data nodes is invoked the expected number of times'
355             deleteDataNodeExpectedNumberOfInvocation * mockCpsDataService.deleteDataNode(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, 'some-cm-handle-id', '/netconf-state', _)
356         and: 'the inventory persistence service to update node leaves is called with the correct values'
357             saveCmHandleStateExpectedNumberOfInvocations * mockInventoryPersistence.saveCmHandleState('some-cm-handle-id', compositeState)
358         where: 'the following data sync enabled flag is used'
359             scenario                                              | dataSyncEnabledFlag | initialDataSyncEnabledFlag | initialDataSyncState               || expectedDataStoreSyncState         | deleteDataNodeExpectedNumberOfInvocation | saveCmHandleStateExpectedNumberOfInvocations
360             'enabled'                                             | true                | false                      | DataStoreSyncState.NONE_REQUESTED  || DataStoreSyncState.UNSYNCHRONIZED  | 0                                        | 1
361             'disabled'                                            | false               | true                       | DataStoreSyncState.UNSYNCHRONIZED  || DataStoreSyncState.NONE_REQUESTED  | 0                                        | 1
362             'disabled where sync-state is currently SYNCHRONIZED' | false               | true                       | DataStoreSyncState.SYNCHRONIZED    || DataStoreSyncState.NONE_REQUESTED  | 1                                        | 1
363             'is set to existing flag state'                       | true                | true                       | DataStoreSyncState.UNSYNCHRONIZED  || DataStoreSyncState.UNSYNCHRONIZED  | 0                                        | 0
364     }
365
366     def 'Set cm Handle Data Sync Enabled flag with following cm handle not in ready state exception' () {
367         given: 'a cm handle composite state'
368             def compositeState = new CompositeState(cmHandleState: CmHandleState.ADVISED, dataSyncEnabled: false)
369         and: 'get cm handle state returns the composite state for the given cm handle id'
370             mockInventoryPersistence.getCmHandleState('some-cm-handle-id') >> compositeState
371         when: 'set data sync enabled is called with the data sync enabled flag set to true'
372             objectUnderTest.setDataSyncEnabled('some-cm-handle-id', true)
373         then: 'the expected exception is thrown'
374             thrown(CpsException)
375         and: 'the inventory persistence service to update node leaves is not invoked'
376             0 * mockInventoryPersistence.saveCmHandleState(_, _)
377     }
378
379     def 'Get all cm handle IDs by DMI plugin identifier.' () {
380         given: 'cm handle queries service returns cm handles'
381             1 * mockCmHandleQueries.getCmHandleIdsByDmiPluginIdentifier('some-dmi-plugin-identifier') >> ['cm-handle-1','cm-handle-2']
382         when: 'cm handle Ids are requested with dmi plugin identifier'
383             def result = objectUnderTest.getAllCmHandleIdsByDmiPluginIdentifier('some-dmi-plugin-identifier')
384         then: 'the result size is correct'
385             assert result.size() == 2
386         and: 'the result returns the correct details'
387             assert result.containsAll('cm-handle-1','cm-handle-2')
388     }
389
390     def dataStores() {
391         CompositeState.DataStores.builder()
392             .operationalDataStore(CompositeState.Operational.builder()
393                 .dataStoreSyncState(DataStoreSyncState.NONE_REQUESTED)
394                 .lastSyncTime('some-timestamp').build()).build()
395     }
396
397     def mockDataNode() {
398         mockCpsDataService.getDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> dataNode
399     }
400
401     def getDataOperationRequest(datastore) {
402         def dataOperationRequest = new DataOperationRequest()
403         def dataOperationDefinitions = new ArrayList()
404         dataOperationDefinitions.add(getDataOperationDefinition(datastore))
405         dataOperationRequest.setDataOperationDefinitions(dataOperationDefinitions)
406         return dataOperationRequest
407     }
408
409     def getDataOperationDefinition(datastore) {
410         def dataOperationDefinition = new DataOperationDefinition()
411         dataOperationDefinition.setOperation("read")
412         dataOperationDefinition.setOperationId("operational-12")
413         dataOperationDefinition.setDatastore(datastore)
414         def targetIds = new ArrayList()
415         targetIds.add("some-cm-handle")
416         dataOperationDefinition.setCmHandleIds(targetIds)
417         return dataOperationDefinition
418     }
419 }