Merge "Use DB for checking Alternate IDs"
[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 yangModelCmHandle = new YangModelCmHandle(id: 'some-cm-handle', dmiServiceName: dmiServiceName,
185                 dmiProperties: dmiProperties, publicProperties: publicProperties, compositeState: compositeState)
186             1 * mockInventoryPersistence.getYangModelCmHandle('some-cm-handle') >> yangModelCmHandle
187         when: 'getting cm handle details for a given cm handle id from ncmp service'
188             def result = objectUnderTest.getNcmpServiceCmHandle('some-cm-handle')
189         then: 'the result is a ncmpServiceCmHandle'
190             result.class == NcmpServiceCmHandle.class
191         and: 'the cm handle contains the cm handle id'
192             result.cmHandleId == 'some-cm-handle'
193         and: 'the cm handle contains the DMI Properties'
194             result.dmiProperties ==[ Book:'Romance Novel' ]
195         and: 'the cm handle contains the public Properties'
196             result.publicProperties == [ "Public Book":'Public Romance Novel' ]
197         and: 'the cm handle contains the cm handle composite state'
198             result.compositeState == compositeState
199
200     }
201
202     def 'Get cm handle public properties'() {
203         given: 'a yang modelled cm handle'
204             def dmiProperties = [new YangModelCmHandle.Property('prop', 'some DMI property')]
205             def publicProperties = [new YangModelCmHandle.Property('public prop', 'some public prop')]
206             def yangModelCmHandle = new YangModelCmHandle(id:'some-cm-handle', dmiServiceName: 'some service name', dmiProperties: dmiProperties, publicProperties: publicProperties)
207         and: 'the system returns this yang modelled cm handle'
208             1 * mockInventoryPersistence.getYangModelCmHandle('some-cm-handle') >> yangModelCmHandle
209         when: 'getting cm handle public properties for a given cm handle id from ncmp service'
210             def result = objectUnderTest.getCmHandlePublicProperties('some-cm-handle')
211         then: 'the result returns the correct data'
212             result == [ 'public prop' : 'some public prop' ]
213     }
214
215     def 'Execute cm handle id search for inventory'() {
216         given: 'a ConditionApiProperties object'
217             def conditionProperties = new ConditionProperties()
218             conditionProperties.conditionName = 'hasAllProperties'
219             conditionProperties.conditionParameters = [ [ 'some-key' : 'some-value' ] ]
220             def conditionServiceProps = new CmHandleQueryServiceParameters()
221             conditionServiceProps.cmHandleQueryParameters = [conditionProperties] as List<ConditionProperties>
222         and: 'the system returns an set of cmHandle ids'
223             mockCpsCmHandlerQueryService.queryCmHandleIdsForInventory(*_) >> [ 'cmHandle1', 'cmHandle2' ]
224         when: 'getting cm handle id set for a given dmi property'
225             def result = objectUnderTest.executeCmHandleIdSearchForInventory(conditionServiceProps)
226         then: 'the result returns the correct 2 elements'
227             assert result.size() == 2
228             assert result.contains('cmHandle1')
229             assert result.contains('cmHandle2')
230     }
231
232     def 'Get cm handle composite state'() {
233         given: 'a yang modelled cm handle'
234             def compositeState = new CompositeState(cmHandleState: CmHandleState.ADVISED,
235                 lockReason: CompositeState.LockReason.builder().lockReasonCategory(LockReasonCategory.MODULE_SYNC_FAILED).details("lock details").build(),
236                 lastUpdateTime: 'some-timestamp',
237                 dataSyncEnabled: false,
238                 dataStores: dataStores())
239             def dmiProperties = [new YangModelCmHandle.Property('prop', 'some DMI property')]
240             def publicProperties = [new YangModelCmHandle.Property('public prop', 'some public prop')]
241             def yangModelCmHandle = new YangModelCmHandle(id:'some-cm-handle', dmiServiceName: 'some service name', dmiProperties: dmiProperties, publicProperties: publicProperties, compositeState: compositeState)
242         and: 'the system returns this yang modelled cm handle'
243             1 * mockInventoryPersistence.getYangModelCmHandle('some-cm-handle') >> yangModelCmHandle
244         when: 'getting cm handle composite state for a given cm handle id from ncmp service'
245             def result = objectUnderTest.getCmHandleCompositeState('some-cm-handle')
246         then: 'the result returns the correct data'
247             result == compositeState
248     }
249
250     def 'Update resource data for pass-through running from dmi using POST #scenario DMI properties.'() {
251         given: 'cpsDataService returns valid datanode'
252             mockCpsDataService.getDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> dataNode
253         when: 'get resource data is called'
254             objectUnderTest.writeResourceDataPassThroughRunningForCmHandle('testCmHandle',
255                 'testResourceId', UPDATE,
256                 '{some-json}', 'application/json')
257         then: 'DMI called with correct data'
258             1 * mockDmiDataOperations.writeResourceDataPassThroughRunningFromDmi('testCmHandle', 'testResourceId',
259                     UPDATE, '{some-json}', 'application/json')
260                 >> { new ResponseEntity<>(HttpStatus.OK) }
261     }
262
263     def 'Verify modules and create anchor params.'() {
264         given: 'dmi plugin registration return created cm handles'
265             def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'service1', dmiModelPlugin: 'service1',
266                     dmiDataPlugin: 'service2')
267             dmiPluginRegistration.createdCmHandles = [ncmpServiceCmHandle]
268             mockDmiPluginRegistration.getCreatedCmHandles() >> [ncmpServiceCmHandle]
269         when: 'parse and create cm handle in dmi registration then sync module'
270             objectUnderTest.parseAndProcessCreatedCmHandlesInRegistration(mockDmiPluginRegistration, ['test-cm-handle-id'])
271         then: 'system persists the cm handle state'
272             1 * mockLcmEventsCmHandleStateHandler.initiateStateAdvised(_) >> {
273                 args -> {
274                           def cmHandleStatePerCmHandle = (args[0] as Collection)
275                           cmHandleStatePerCmHandle.each {
276                             assert it.id == 'test-cm-handle-id'
277                           }
278                     }
279             }
280     }
281
282     def 'Execute cm handle id search'() {
283         given: 'valid CmHandleQueryApiParameters input'
284             def cmHandleQueryApiParameters = new CmHandleQueryApiParameters()
285             def conditionApiProperties = new ConditionApiProperties()
286             conditionApiProperties.conditionName = 'hasAllModules'
287             conditionApiProperties.conditionParameters = [[moduleName: 'module-name-1']]
288             cmHandleQueryApiParameters.cmHandleQueryParameters = [conditionApiProperties]
289         and: 'query cm handle method return with a data node list'
290             mockCpsCmHandlerQueryService.queryCmHandleIds(
291                     spiedJsonObjectMapper.convertToValueType(cmHandleQueryApiParameters, CmHandleQueryServiceParameters.class))
292                     >> ['cm-handle-id-1']
293         when: 'execute cm handle search is called'
294             def result = objectUnderTest.executeCmHandleIdSearch(cmHandleQueryApiParameters)
295         then: 'result is the same collection as returned by the CPS Data Service'
296             assert result == ['cm-handle-id-1']
297     }
298
299     def 'Getting module definitions by module'() {
300         when: 'get module definitions is performed with module name'
301             objectUnderTest.getModuleDefinitionsByCmHandleAndModule('some-cm-handle', 'some-module', '2021-08-04')
302         then: 'ncmp inventory persistence service is invoked once with correct parameters'
303             1 * mockInventoryPersistence.getModuleDefinitionsByCmHandleAndModule('some-cm-handle', 'some-module', '2021-08-04')
304     }
305
306     def 'Getting module definitions by cm handle id'() {
307         when: 'get module definitions is performed with cm handle id'
308             objectUnderTest.getModuleDefinitionsByCmHandleId('some-cm-handle')
309         then: 'ncmp inventory persistence service is invoked once with correct parameter'
310             1 * mockInventoryPersistence.getModuleDefinitionsByCmHandleId('some-cm-handle')
311     }
312
313     def 'Execute cm handle search'() {
314         given: 'valid CmHandleQueryApiParameters input'
315             def cmHandleQueryApiParameters = new CmHandleQueryApiParameters()
316             def conditionApiProperties = new ConditionApiProperties()
317             conditionApiProperties.conditionName = 'hasAllModules'
318             conditionApiProperties.conditionParameters = [[moduleName: 'module-name-1']]
319             cmHandleQueryApiParameters.cmHandleQueryParameters = [conditionApiProperties]
320         and: 'query cm handle method return with a data node list'
321             mockCpsCmHandlerQueryService.queryCmHandles(
322                     spiedJsonObjectMapper.convertToValueType(cmHandleQueryApiParameters, CmHandleQueryServiceParameters.class))
323                     >> [new NcmpServiceCmHandle(cmHandleId: 'cm-handle-id-1')]
324         when: 'execute cm handle search is called'
325             def result = objectUnderTest.executeCmHandleSearch(cmHandleQueryApiParameters)
326         then: 'result is the same collection as returned by the CPS Data Service'
327             assert result.stream().map(d -> d.cmHandleId).collect(Collectors.toSet()) == ['cm-handle-id-1'] as Set
328     }
329
330     def 'Set Cm Handle Data Sync Enabled Flag where data sync flag is  #scenario'() {
331         given: 'an existing cm handle composite state'
332             def compositeState = new CompositeState(cmHandleState: CmHandleState.READY, dataSyncEnabled: initialDataSyncEnabledFlag,
333                 dataStores: CompositeState.DataStores.builder()
334                     .operationalDataStore(CompositeState.Operational.builder()
335                         .dataStoreSyncState(initialDataSyncState)
336                         .build()).build())
337         and: 'get cm handle state returns the composite state for the given cm handle id'
338             mockInventoryPersistence.getCmHandleState('some-cm-handle-id') >> compositeState
339         when: 'set data sync enabled is called with the data sync enabled flag set to #dataSyncEnabledFlag'
340             objectUnderTest.setDataSyncEnabled('some-cm-handle-id', dataSyncEnabledFlag)
341         then: 'the data sync enabled flag is set to #dataSyncEnabled'
342             compositeState.dataSyncEnabled == dataSyncEnabledFlag
343         and: 'the data store sync state is set to #expectedDataStoreSyncState'
344             compositeState.dataStores.operationalDataStore.dataStoreSyncState == expectedDataStoreSyncState
345         and: 'the cps data service to delete data nodes is invoked the expected number of times'
346             deleteDataNodeExpectedNumberOfInvocation * mockCpsDataService.deleteDataNode(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, 'some-cm-handle-id', '/netconf-state', _)
347         and: 'the inventory persistence service to update node leaves is called with the correct values'
348             saveCmHandleStateExpectedNumberOfInvocations * mockInventoryPersistence.saveCmHandleState('some-cm-handle-id', compositeState)
349         where: 'the following data sync enabled flag is used'
350             scenario                                              | dataSyncEnabledFlag | initialDataSyncEnabledFlag | initialDataSyncState               || expectedDataStoreSyncState         | deleteDataNodeExpectedNumberOfInvocation | saveCmHandleStateExpectedNumberOfInvocations
351             'enabled'                                             | true                | false                      | DataStoreSyncState.NONE_REQUESTED  || DataStoreSyncState.UNSYNCHRONIZED  | 0                                        | 1
352             'disabled'                                            | false               | true                       | DataStoreSyncState.UNSYNCHRONIZED  || DataStoreSyncState.NONE_REQUESTED  | 0                                        | 1
353             'disabled where sync-state is currently SYNCHRONIZED' | false               | true                       | DataStoreSyncState.SYNCHRONIZED    || DataStoreSyncState.NONE_REQUESTED  | 1                                        | 1
354             'is set to existing flag state'                       | true                | true                       | DataStoreSyncState.UNSYNCHRONIZED  || DataStoreSyncState.UNSYNCHRONIZED  | 0                                        | 0
355     }
356
357     def 'Set cm Handle Data Sync Enabled flag with following cm handle not in ready state exception' () {
358         given: 'a cm handle composite state'
359             def compositeState = new CompositeState(cmHandleState: CmHandleState.ADVISED, dataSyncEnabled: false)
360         and: 'get cm handle state returns the composite state for the given cm handle id'
361             mockInventoryPersistence.getCmHandleState('some-cm-handle-id') >> compositeState
362         when: 'set data sync enabled is called with the data sync enabled flag set to true'
363             objectUnderTest.setDataSyncEnabled('some-cm-handle-id', true)
364         then: 'the expected exception is thrown'
365             thrown(CpsException)
366         and: 'the inventory persistence service to update node leaves is not invoked'
367             0 * mockInventoryPersistence.saveCmHandleState(_, _)
368     }
369
370     def 'Get all cm handle IDs by DMI plugin identifier.' () {
371         given: 'cm handle queries service returns cm handles'
372             1 * mockCmHandleQueries.getCmHandleIdsByDmiPluginIdentifier('some-dmi-plugin-identifier') >> ['cm-handle-1','cm-handle-2']
373         when: 'cm handle Ids are requested with dmi plugin identifier'
374             def result = objectUnderTest.getAllCmHandleIdsByDmiPluginIdentifier('some-dmi-plugin-identifier')
375         then: 'the result size is correct'
376             assert result.size() == 2
377         and: 'the result returns the correct details'
378             assert result.containsAll('cm-handle-1','cm-handle-2')
379     }
380
381     def dataStores() {
382         CompositeState.DataStores.builder()
383             .operationalDataStore(CompositeState.Operational.builder()
384                 .dataStoreSyncState(DataStoreSyncState.NONE_REQUESTED)
385                 .lastSyncTime('some-timestamp').build()).build()
386     }
387
388     def mockDataNode() {
389         mockCpsDataService.getDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> dataNode
390     }
391
392     def getDataOperationRequest(datastore) {
393         def dataOperationRequest = new DataOperationRequest()
394         def dataOperationDefinitions = new ArrayList()
395         dataOperationDefinitions.add(getDataOperationDefinition(datastore))
396         dataOperationRequest.setDataOperationDefinitions(dataOperationDefinitions)
397         return dataOperationRequest
398     }
399
400     def getDataOperationDefinition(datastore) {
401         def dataOperationDefinition = new DataOperationDefinition()
402         dataOperationDefinition.setOperation("read")
403         dataOperationDefinition.setOperationId("operational-12")
404         dataOperationDefinition.setDatastore(datastore)
405         def targetIds = new ArrayList()
406         targetIds.add("some-cm-handle")
407         dataOperationDefinition.setCmHandleIds(targetIds)
408         return dataOperationDefinition
409     }
410 }