9221f318668cc542411c5362ac4d6c182e3ddae5
[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 static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME
27 import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DATASPACE_NAME
28 import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DMI_REGISTRY_ANCHOR
29 import static org.onap.cps.ncmp.api.impl.operations.DatastoreType.OPERATIONAL
30 import static org.onap.cps.ncmp.api.impl.operations.DatastoreType.PASSTHROUGH_OPERATIONAL
31 import static org.onap.cps.ncmp.api.impl.operations.DatastoreType.PASSTHROUGH_RUNNING
32 import static org.onap.cps.ncmp.api.impl.operations.OperationType.CREATE
33 import static org.onap.cps.ncmp.api.impl.operations.OperationType.UPDATE
34
35 import org.onap.cps.ncmp.api.impl.utils.AlternateIdChecker
36 import com.hazelcast.map.IMap
37 import org.onap.cps.ncmp.api.NetworkCmProxyCmHandleQueryService
38 import org.onap.cps.ncmp.api.impl.events.lcm.LcmEventsCmHandleStateHandler
39 import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevel
40 import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevelManager
41 import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle
42 import org.onap.cps.ncmp.api.impl.inventory.CmHandleQueries
43 import org.onap.cps.ncmp.api.impl.inventory.CmHandleState
44 import org.onap.cps.ncmp.api.impl.inventory.CompositeState
45 import org.onap.cps.ncmp.api.impl.inventory.InventoryPersistence
46 import org.onap.cps.ncmp.api.impl.inventory.LockReasonCategory
47 import org.onap.cps.ncmp.api.impl.inventory.DataStoreSyncState
48 import org.onap.cps.ncmp.api.models.DataOperationDefinition
49 import org.onap.cps.ncmp.api.models.CmHandleQueryApiParameters
50 import org.onap.cps.ncmp.api.models.CmHandleQueryServiceParameters
51 import org.onap.cps.ncmp.api.models.ConditionApiProperties
52 import org.onap.cps.ncmp.api.models.DmiPluginRegistration
53 import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle
54 import org.onap.cps.ncmp.api.models.DataOperationRequest
55 import org.onap.cps.spi.exceptions.CpsException
56 import org.onap.cps.spi.model.ConditionProperties
57
58 import java.util.stream.Collectors
59 import org.onap.cps.utils.JsonObjectMapper
60 import com.fasterxml.jackson.databind.ObjectMapper
61 import org.onap.cps.api.CpsDataService
62 import org.onap.cps.ncmp.api.impl.operations.DmiDataOperations
63 import org.onap.cps.spi.FetchDescendantsOption
64 import org.onap.cps.spi.model.DataNode
65 import org.springframework.http.HttpStatus
66 import org.springframework.http.ResponseEntity
67 import spock.lang.Specification
68
69 class NetworkCmProxyDataServiceImplSpec extends Specification {
70
71     def mockCpsDataService = Mock(CpsDataService)
72     def spiedJsonObjectMapper = Spy(new JsonObjectMapper(new ObjectMapper()))
73     def mockDmiDataOperations = Mock(DmiDataOperations)
74     def nullNetworkCmProxyDataServicePropertyHandler = null
75     def mockInventoryPersistence = Mock(InventoryPersistence)
76     def mockCmHandleQueries = Mock(CmHandleQueries)
77     def mockDmiPluginRegistration = Mock(DmiPluginRegistration)
78     def mockCpsCmHandlerQueryService = Mock(NetworkCmProxyCmHandleQueryService)
79     def mockLcmEventsCmHandleStateHandler = Mock(LcmEventsCmHandleStateHandler)
80     def stubModuleSyncStartedOnCmHandles = Stub(IMap<String, Object>)
81     def stubTrustLevelPerDmiPlugin = Stub(Map<String, TrustLevel>)
82     def mockTrustLevelManager = Mock(TrustLevelManager)
83     def mockAlternateIdChecker = Mock(AlternateIdChecker)
84     def mockModuleSetTagCache = [:]
85
86     def NO_TOPIC = null
87     def NO_REQUEST_ID = null
88     def OPTIONS_PARAM = '(a=1,b=2)'
89     def ncmpServiceCmHandle = new NcmpServiceCmHandle(cmHandleId: 'test-cm-handle-id')
90
91     def objectUnderTest = new NetworkCmProxyDataServiceImpl(
92             spiedJsonObjectMapper,
93             mockDmiDataOperations,
94             nullNetworkCmProxyDataServicePropertyHandler,
95             mockInventoryPersistence,
96             mockCmHandleQueries,
97             mockCpsCmHandlerQueryService,
98             mockLcmEventsCmHandleStateHandler,
99             mockCpsDataService,
100             stubModuleSyncStartedOnCmHandles,
101             stubTrustLevelPerDmiPlugin,
102             mockTrustLevelManager,
103             mockAlternateIdChecker,
104             mockModuleSetTagCache)
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         when: 'parse and create cm handle in dmi registration then sync module'
276             objectUnderTest.parseAndProcessCreatedCmHandlesInRegistration(mockDmiPluginRegistration, ['test-cm-handle-id'])
277         then: 'system persists the cm handle state'
278             1 * mockLcmEventsCmHandleStateHandler.initiateStateAdvised(_) >> {
279                 args -> {
280                           def cmHandleStatePerCmHandle = (args[0] as Collection)
281                           cmHandleStatePerCmHandle.each {
282                             assert it.id == 'test-cm-handle-id'
283                           }
284                     }
285             }
286     }
287
288     def 'Execute cm handle id search'() {
289         given: 'valid CmHandleQueryApiParameters input'
290             def cmHandleQueryApiParameters = new CmHandleQueryApiParameters()
291             def conditionApiProperties = new ConditionApiProperties()
292             conditionApiProperties.conditionName = 'hasAllModules'
293             conditionApiProperties.conditionParameters = [[moduleName: 'module-name-1']]
294             cmHandleQueryApiParameters.cmHandleQueryParameters = [conditionApiProperties]
295         and: 'query cm handle method return with a data node list'
296             mockCpsCmHandlerQueryService.queryCmHandleIds(
297                     spiedJsonObjectMapper.convertToValueType(cmHandleQueryApiParameters, CmHandleQueryServiceParameters.class))
298                     >> ['cm-handle-id-1']
299         when: 'execute cm handle search is called'
300             def result = objectUnderTest.executeCmHandleIdSearch(cmHandleQueryApiParameters)
301         then: 'result is the same collection as returned by the CPS Data Service'
302             assert result == ['cm-handle-id-1']
303     }
304
305     def 'Getting module definitions by module'() {
306         when: 'get module definitions is performed with module name'
307             objectUnderTest.getModuleDefinitionsByCmHandleAndModule('some-cm-handle', 'some-module', '2021-08-04')
308         then: 'ncmp inventory persistence service is invoked once with correct parameters'
309             1 * mockInventoryPersistence.getModuleDefinitionsByCmHandleAndModule('some-cm-handle', 'some-module', '2021-08-04')
310     }
311
312     def 'Getting module definitions by cm handle id'() {
313         when: 'get module definitions is performed with cm handle id'
314             objectUnderTest.getModuleDefinitionsByCmHandleId('some-cm-handle')
315         then: 'ncmp inventory persistence service is invoked once with correct parameter'
316             1 * mockInventoryPersistence.getModuleDefinitionsByCmHandleId('some-cm-handle')
317     }
318
319     def 'Execute cm handle search'() {
320         given: 'valid CmHandleQueryApiParameters input'
321             def cmHandleQueryApiParameters = new CmHandleQueryApiParameters()
322             def conditionApiProperties = new ConditionApiProperties()
323             conditionApiProperties.conditionName = 'hasAllModules'
324             conditionApiProperties.conditionParameters = [[moduleName: 'module-name-1']]
325             cmHandleQueryApiParameters.cmHandleQueryParameters = [conditionApiProperties]
326         and: 'query cm handle method return with a data node list'
327             mockCpsCmHandlerQueryService.queryCmHandles(
328                     spiedJsonObjectMapper.convertToValueType(cmHandleQueryApiParameters, CmHandleQueryServiceParameters.class))
329                     >> [new NcmpServiceCmHandle(cmHandleId: 'cm-handle-id-1')]
330         when: 'execute cm handle search is called'
331             def result = objectUnderTest.executeCmHandleSearch(cmHandleQueryApiParameters)
332         then: 'result is the same collection as returned by the CPS Data Service'
333             assert result.stream().map(d -> d.cmHandleId).collect(Collectors.toSet()) == ['cm-handle-id-1'] as Set
334     }
335
336     def 'Set Cm Handle Data Sync Enabled Flag where data sync flag is  #scenario'() {
337         given: 'an existing cm handle composite state'
338             def compositeState = new CompositeState(cmHandleState: CmHandleState.READY, dataSyncEnabled: initialDataSyncEnabledFlag,
339                 dataStores: CompositeState.DataStores.builder()
340                     .operationalDataStore(CompositeState.Operational.builder()
341                         .dataStoreSyncState(initialDataSyncState)
342                         .build()).build())
343         and: 'get cm handle state returns the composite state for the given cm handle id'
344             mockInventoryPersistence.getCmHandleState('some-cm-handle-id') >> compositeState
345         when: 'set data sync enabled is called with the data sync enabled flag set to #dataSyncEnabledFlag'
346             objectUnderTest.setDataSyncEnabled('some-cm-handle-id', dataSyncEnabledFlag)
347         then: 'the data sync enabled flag is set to #dataSyncEnabled'
348             compositeState.dataSyncEnabled == dataSyncEnabledFlag
349         and: 'the data store sync state is set to #expectedDataStoreSyncState'
350             compositeState.dataStores.operationalDataStore.dataStoreSyncState == expectedDataStoreSyncState
351         and: 'the cps data service to delete data nodes is invoked the expected number of times'
352             deleteDataNodeExpectedNumberOfInvocation * mockCpsDataService.deleteDataNode(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, 'some-cm-handle-id', '/netconf-state', _)
353         and: 'the inventory persistence service to update node leaves is called with the correct values'
354             saveCmHandleStateExpectedNumberOfInvocations * mockInventoryPersistence.saveCmHandleState('some-cm-handle-id', compositeState)
355         where: 'the following data sync enabled flag is used'
356             scenario                                              | dataSyncEnabledFlag | initialDataSyncEnabledFlag | initialDataSyncState               || expectedDataStoreSyncState         | deleteDataNodeExpectedNumberOfInvocation | saveCmHandleStateExpectedNumberOfInvocations
357             'enabled'                                             | true                | false                      | DataStoreSyncState.NONE_REQUESTED  || DataStoreSyncState.UNSYNCHRONIZED  | 0                                        | 1
358             'disabled'                                            | false               | true                       | DataStoreSyncState.UNSYNCHRONIZED  || DataStoreSyncState.NONE_REQUESTED  | 0                                        | 1
359             'disabled where sync-state is currently SYNCHRONIZED' | false               | true                       | DataStoreSyncState.SYNCHRONIZED    || DataStoreSyncState.NONE_REQUESTED  | 1                                        | 1
360             'is set to existing flag state'                       | true                | true                       | DataStoreSyncState.UNSYNCHRONIZED  || DataStoreSyncState.UNSYNCHRONIZED  | 0                                        | 0
361     }
362
363     def 'Set cm Handle Data Sync Enabled flag with following cm handle not in ready state exception' () {
364         given: 'a cm handle composite state'
365             def compositeState = new CompositeState(cmHandleState: CmHandleState.ADVISED, dataSyncEnabled: false)
366         and: 'get cm handle state returns the composite state for the given cm handle id'
367             mockInventoryPersistence.getCmHandleState('some-cm-handle-id') >> compositeState
368         when: 'set data sync enabled is called with the data sync enabled flag set to true'
369             objectUnderTest.setDataSyncEnabled('some-cm-handle-id', true)
370         then: 'the expected exception is thrown'
371             thrown(CpsException)
372         and: 'the inventory persistence service to update node leaves is not invoked'
373             0 * mockInventoryPersistence.saveCmHandleState(_, _)
374     }
375
376     def 'Get all cm handle IDs by DMI plugin identifier.' () {
377         given: 'cm handle queries service returns cm handles'
378             1 * mockCmHandleQueries.getCmHandleIdsByDmiPluginIdentifier('some-dmi-plugin-identifier') >> ['cm-handle-1','cm-handle-2']
379         when: 'cm handle Ids are requested with dmi plugin identifier'
380             def result = objectUnderTest.getAllCmHandleIdsByDmiPluginIdentifier('some-dmi-plugin-identifier')
381         then: 'the result size is correct'
382             assert result.size() == 2
383         and: 'the result returns the correct details'
384             assert result.containsAll('cm-handle-1','cm-handle-2')
385     }
386
387     def dataStores() {
388         CompositeState.DataStores.builder()
389             .operationalDataStore(CompositeState.Operational.builder()
390                 .dataStoreSyncState(DataStoreSyncState.NONE_REQUESTED)
391                 .lastSyncTime('some-timestamp').build()).build()
392     }
393
394     def mockDataNode() {
395         mockCpsDataService.getDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> dataNode
396     }
397
398     def getDataOperationRequest(datastore) {
399         def dataOperationRequest = new DataOperationRequest()
400         def dataOperationDefinitions = new ArrayList()
401         dataOperationDefinitions.add(getDataOperationDefinition(datastore))
402         dataOperationRequest.setDataOperationDefinitions(dataOperationDefinitions)
403         return dataOperationRequest
404     }
405
406     def getDataOperationDefinition(datastore) {
407         def dataOperationDefinition = new DataOperationDefinition()
408         dataOperationDefinition.setOperation("read")
409         dataOperationDefinition.setOperationId("operational-12")
410         dataOperationDefinition.setDatastore(datastore)
411         def targetIds = new ArrayList()
412         targetIds.add("some-cm-handle")
413         dataOperationDefinition.setCmHandleIds(targetIds)
414         return dataOperationDefinition
415     }
416 }