Merge "Save new cm notification subscription"
[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 import org.onap.cps.ncmp.api.models.CmResourceAddress
28
29 import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME
30 import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DATASPACE_NAME
31 import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DMI_REGISTRY_ANCHOR
32 import static org.onap.cps.ncmp.api.impl.operations.DatastoreType.OPERATIONAL
33 import static org.onap.cps.ncmp.api.impl.operations.DatastoreType.PASSTHROUGH_OPERATIONAL
34 import static org.onap.cps.ncmp.api.impl.operations.DatastoreType.PASSTHROUGH_RUNNING
35 import static org.onap.cps.ncmp.api.impl.operations.OperationType.CREATE
36 import static org.onap.cps.ncmp.api.impl.operations.OperationType.UPDATE
37
38 import org.onap.cps.ncmp.api.impl.utils.AlternateIdChecker
39 import com.hazelcast.map.IMap
40 import org.onap.cps.ncmp.api.NetworkCmProxyCmHandleQueryService
41 import org.onap.cps.ncmp.api.impl.events.lcm.LcmEventsCmHandleStateHandler
42 import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevel
43 import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevelManager
44 import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle
45 import org.onap.cps.ncmp.api.impl.inventory.CmHandleQueries
46 import org.onap.cps.ncmp.api.impl.inventory.CmHandleState
47 import org.onap.cps.ncmp.api.impl.inventory.CompositeState
48 import org.onap.cps.ncmp.api.impl.inventory.InventoryPersistence
49 import org.onap.cps.ncmp.api.impl.inventory.LockReasonCategory
50 import org.onap.cps.ncmp.api.impl.inventory.DataStoreSyncState
51 import org.onap.cps.ncmp.api.models.DataOperationDefinition
52 import org.onap.cps.ncmp.api.models.CmHandleQueryApiParameters
53 import org.onap.cps.ncmp.api.models.CmHandleQueryServiceParameters
54 import org.onap.cps.ncmp.api.models.ConditionApiProperties
55 import org.onap.cps.ncmp.api.models.DmiPluginRegistration
56 import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle
57 import org.onap.cps.ncmp.api.models.DataOperationRequest
58 import org.onap.cps.spi.exceptions.CpsException
59 import org.onap.cps.spi.model.ConditionProperties
60
61 import java.util.stream.Collectors
62 import org.onap.cps.utils.JsonObjectMapper
63 import com.fasterxml.jackson.databind.ObjectMapper
64 import org.onap.cps.api.CpsDataService
65 import org.onap.cps.ncmp.api.impl.operations.DmiDataOperations
66 import org.onap.cps.spi.FetchDescendantsOption
67 import org.onap.cps.spi.model.DataNode
68 import org.springframework.http.HttpStatus
69 import org.springframework.http.ResponseEntity
70 import spock.lang.Specification
71
72 class NetworkCmProxyDataServiceImplSpec extends Specification {
73
74     def mockCpsDataService = Mock(CpsDataService)
75     def spiedJsonObjectMapper = Spy(new JsonObjectMapper(new ObjectMapper()))
76     def mockDmiDataOperations = Mock(DmiDataOperations)
77     def nullNetworkCmProxyDataServicePropertyHandler = null
78     def mockInventoryPersistence = Mock(InventoryPersistence)
79     def mockCmHandleQueries = Mock(CmHandleQueries)
80     def mockDmiPluginRegistration = Mock(DmiPluginRegistration)
81     def mockCpsCmHandlerQueryService = Mock(NetworkCmProxyCmHandleQueryService)
82     def mockLcmEventsCmHandleStateHandler = Mock(LcmEventsCmHandleStateHandler)
83     def stubModuleSyncStartedOnCmHandles = Stub(IMap<String, Object>)
84     def stubTrustLevelPerDmiPlugin = Stub(Map<String, TrustLevel>)
85     def mockTrustLevelManager = Mock(TrustLevelManager)
86     def mockAlternateIdChecker = Mock(AlternateIdChecker)
87
88     def NO_TOPIC = null
89     def NO_REQUEST_ID = null
90     def NO_AUTH_HEADER = null
91     def OPTIONS_PARAM = '(a=1,b=2)'
92     def ncmpServiceCmHandle = new NcmpServiceCmHandle(cmHandleId: 'test-cm-handle-id')
93
94     def objectUnderTest = new NetworkCmProxyDataServiceImpl(
95             spiedJsonObjectMapper,
96             mockDmiDataOperations,
97             nullNetworkCmProxyDataServicePropertyHandler,
98             mockInventoryPersistence,
99             mockCmHandleQueries,
100             mockCpsCmHandlerQueryService,
101             mockLcmEventsCmHandleStateHandler,
102             mockCpsDataService,
103             stubModuleSyncStartedOnCmHandles,
104             stubTrustLevelPerDmiPlugin,
105             mockTrustLevelManager,
106             mockAlternateIdChecker)
107
108     def cmHandleXPath = "/dmi-registry/cm-handles[@id='testCmHandle']"
109
110     def dataNode = [new DataNode(leaves: ['id': 'some-cm-handle', 'dmi-service-name': 'testDmiService'])]
111
112     def 'Write resource data for pass-through running from DMI using POST.'() {
113         given: 'cpsDataService returns valid datanode'
114             mockDataNode()
115         when: 'write resource data is called'
116             objectUnderTest.writeResourceDataPassThroughRunningForCmHandle('testCmHandle',
117                 'testResourceId', CREATE,
118                 '{some-json}', 'application/json', null)
119         then: 'DMI called with correct data'
120             1 * mockDmiDataOperations.writeResourceDataPassThroughRunningFromDmi('testCmHandle', 'testResourceId',
121                     CREATE, '{some-json}', 'application/json', null)
122                 >> { new ResponseEntity<>(HttpStatus.CREATED) }
123     }
124
125     def 'Get resource data for from DMI.'() {
126         given: 'cpsDataService returns valid data node'
127             mockDataNode()
128         and: 'some cm resource address'
129             def cmResourceAddress = new CmResourceAddress('some datastore','some CM Handle', 'some resource Id')
130         and: 'get resource data from DMI is called'
131             mockDmiDataOperations.getResourceDataFromDmi(cmResourceAddress, OPTIONS_PARAM, NO_TOPIC, NO_REQUEST_ID, NO_AUTH_HEADER) >>
132                     new ResponseEntity<>('dmi-response', HttpStatus.OK)
133         when: 'get resource data operational for the given cm resource address is called'
134             def response = objectUnderTest.getResourceDataForCmHandle(cmResourceAddress, OPTIONS_PARAM, NO_TOPIC, NO_REQUEST_ID, NO_AUTH_HEADER)
135         then: 'DMI returns a json response'
136             assert response == 'dmi-response'
137     }
138
139     def 'Get resource data for operational (cached) data.'() {
140         given: 'CPS Data service returns some object(s)'
141             mockCpsDataService.getDataNodes(OPERATIONAL.datastoreName, 'testCmHandle', 'testResourceId', FetchDescendantsOption.OMIT_DESCENDANTS) >> ['First Object', 'other Object']
142         and: 'a cm resource address for the same datastore, cm handle and resource id'
143             def cmResourceAddress = new CmResourceAddress(OPERATIONAL.datastoreName, 'testCmHandle', 'testResourceId')
144         when: 'get resource data is called'
145             def response = objectUnderTest.getResourceDataForCmHandle(cmResourceAddress, FetchDescendantsOption.OMIT_DESCENDANTS)
146         then: 'get resource data returns teh first object from the data service'
147             assert response == 'First Object'
148     }
149
150     def 'Execute (async) data operation for #datastoreName from DMI.'() {
151         given: 'cpsDataService returns valid data node'
152             def dataOperationRequest = getDataOperationRequest(datastoreName)
153         when: 'request resource data for data operation is called'
154             objectUnderTest.executeDataOperationForCmHandles('some topic', dataOperationRequest, 'requestId', NO_AUTH_HEADER)
155         then: 'request resource data for data operation returns expected response'
156             1 * mockDmiDataOperations.requestResourceDataFromDmi('some topic', dataOperationRequest, 'requestId', NO_AUTH_HEADER)
157         where: 'the following data stores are used'
158             datastoreName << [PASSTHROUGH_RUNNING.datastoreName, PASSTHROUGH_OPERATIONAL.datastoreName]
159     }
160
161     def 'Getting Yang Resources.'() {
162         when: 'yang resources is called'
163             objectUnderTest.getYangResourcesModuleReferences('some-cm-handle')
164         then: 'CPS module services is invoked for the correct dataspace and cm handle'
165             1 * mockInventoryPersistence.getYangResourcesModuleReferences('some-cm-handle')
166     }
167
168     def 'Get a cm handle.'() {
169         given: 'the system returns a yang modelled cm handle'
170             def dmiServiceName = 'some service name'
171             def compositeState = new CompositeState(cmHandleState: CmHandleState.ADVISED,
172                     lockReason: CompositeState.LockReason.builder().lockReasonCategory(LockReasonCategory.MODULE_SYNC_FAILED).details("lock details").build(),
173                     lastUpdateTime: 'some-timestamp',
174                     dataSyncEnabled: false,
175                     dataStores: dataStores())
176             def dmiProperties = [new YangModelCmHandle.Property('Book', 'Romance Novel')]
177             def publicProperties = [new YangModelCmHandle.Property('Public Book', 'Public Romance Novel')]
178             def moduleSetTag = 'some-module-set-tag'
179             def alternateId = 'some-alternate-id'
180             def yangModelCmHandle = new YangModelCmHandle(id: 'some-cm-handle', dmiServiceName: dmiServiceName,
181                     dmiProperties: dmiProperties, publicProperties: publicProperties, compositeState: compositeState,
182                     moduleSetTag: moduleSetTag, alternateId: alternateId)
183             1 * mockInventoryPersistence.getYangModelCmHandle('some-cm-handle') >> yangModelCmHandle
184         when: 'getting cm handle details for a given cm handle id from ncmp service'
185             def result = objectUnderTest.getNcmpServiceCmHandle('some-cm-handle')
186         then: 'the result is a ncmpServiceCmHandle'
187             result.class == NcmpServiceCmHandle.class
188         and: 'the cm handle contains the cm handle id'
189             result.cmHandleId == 'some-cm-handle'
190         and: 'the cm handle contains the alternate id'
191             result.alternateId == 'some-alternate-id'
192         and: 'the cm handle contains the module-set-tag'
193             result.moduleSetTag == 'some-module-set-tag'
194         and: 'the cm handle contains the DMI Properties'
195             result.dmiProperties ==[ Book:'Romance Novel' ]
196         and: 'the cm handle contains the public Properties'
197             result.publicProperties == [ "Public Book":'Public Romance Novel' ]
198         and: 'the cm handle contains the cm handle composite state'
199             result.compositeState == compositeState
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', NO_AUTH_HEADER)
257         then: 'DMI called with correct data'
258             1 * mockDmiDataOperations.writeResourceDataPassThroughRunningFromDmi('testCmHandle', 'testResourceId',
259                     UPDATE, '{some-json}', 'application/json', NO_AUTH_HEADER)
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         and: 'no rejected cm handles because of alternate ids'
270             mockAlternateIdChecker.getIdsOfCmHandlesWithRejectedAlternateId(*_) >> []
271         when: 'parse and create cm handle in dmi registration then sync module'
272             mockDmiPluginRegistration.createdCmHandles = ['test-cm-handle-id']
273             objectUnderTest.processCreatedCmHandles(mockDmiPluginRegistration, new DmiPluginRegistrationResponse())
274         then: 'system persists the cm handle state'
275             1 * mockLcmEventsCmHandleStateHandler.initiateStateAdvised(_) >> {
276                 args -> {
277                           def cmHandleStatePerCmHandle = (args[0] as Collection)
278                           cmHandleStatePerCmHandle.each {
279                             assert it.id == 'test-cm-handle-id'
280                           }
281                     }
282             }
283     }
284
285     def 'Execute cm handle id search'() {
286         given: 'valid CmHandleQueryApiParameters input'
287             def cmHandleQueryApiParameters = new CmHandleQueryApiParameters()
288             def conditionApiProperties = new ConditionApiProperties()
289             conditionApiProperties.conditionName = 'hasAllModules'
290             conditionApiProperties.conditionParameters = [[moduleName: 'module-name-1']]
291             cmHandleQueryApiParameters.cmHandleQueryParameters = [conditionApiProperties]
292         and: 'query cm handle method return with a data node list'
293             mockCpsCmHandlerQueryService.queryCmHandleIds(
294                     spiedJsonObjectMapper.convertToValueType(cmHandleQueryApiParameters, CmHandleQueryServiceParameters.class))
295                     >> ['cm-handle-id-1']
296         when: 'execute cm handle search is called'
297             def result = objectUnderTest.executeCmHandleIdSearch(cmHandleQueryApiParameters)
298         then: 'result is the same collection as returned by the CPS Data Service'
299             assert result == ['cm-handle-id-1']
300     }
301
302     def 'Getting module definitions by module'() {
303         when: 'get module definitions is performed with module name'
304             objectUnderTest.getModuleDefinitionsByCmHandleAndModule('some-cm-handle', 'some-module', '2021-08-04')
305         then: 'ncmp inventory persistence service is invoked once with correct parameters'
306             1 * mockInventoryPersistence.getModuleDefinitionsByCmHandleAndModule('some-cm-handle', 'some-module', '2021-08-04')
307     }
308
309     def 'Getting module definitions by cm handle id'() {
310         when: 'get module definitions is performed with cm handle id'
311             objectUnderTest.getModuleDefinitionsByCmHandleId('some-cm-handle')
312         then: 'ncmp inventory persistence service is invoked once with correct parameter'
313             1 * mockInventoryPersistence.getModuleDefinitionsByCmHandleId('some-cm-handle')
314     }
315
316     def 'Execute cm handle search'() {
317         given: 'valid CmHandleQueryApiParameters input'
318             def cmHandleQueryApiParameters = new CmHandleQueryApiParameters()
319             def conditionApiProperties = new ConditionApiProperties()
320             conditionApiProperties.conditionName = 'hasAllModules'
321             conditionApiProperties.conditionParameters = [[moduleName: 'module-name-1']]
322             cmHandleQueryApiParameters.cmHandleQueryParameters = [conditionApiProperties]
323         and: 'query cm handle method return with a data node list'
324             mockCpsCmHandlerQueryService.queryCmHandles(
325                     spiedJsonObjectMapper.convertToValueType(cmHandleQueryApiParameters, CmHandleQueryServiceParameters.class))
326                     >> [new NcmpServiceCmHandle(cmHandleId: 'cm-handle-id-1')]
327         when: 'execute cm handle search is called'
328             def result = objectUnderTest.executeCmHandleSearch(cmHandleQueryApiParameters)
329         then: 'result is the same collection as returned by the CPS Data Service'
330             assert result.stream().map(d -> d.cmHandleId).collect(Collectors.toSet()) == ['cm-handle-id-1'] as Set
331     }
332
333     def 'Set Cm Handle Data Sync Enabled Flag where data sync flag is  #scenario'() {
334         given: 'an existing cm handle composite state'
335             def compositeState = new CompositeState(cmHandleState: CmHandleState.READY, dataSyncEnabled: initialDataSyncEnabledFlag,
336                 dataStores: CompositeState.DataStores.builder()
337                     .operationalDataStore(CompositeState.Operational.builder()
338                         .dataStoreSyncState(initialDataSyncState)
339                         .build()).build())
340         and: 'get cm handle state returns the composite state for the given cm handle id'
341             mockInventoryPersistence.getCmHandleState('some-cm-handle-id') >> compositeState
342         when: 'set data sync enabled is called with the data sync enabled flag set to #dataSyncEnabledFlag'
343             objectUnderTest.setDataSyncEnabled('some-cm-handle-id', dataSyncEnabledFlag)
344         then: 'the data sync enabled flag is set to #dataSyncEnabled'
345             compositeState.dataSyncEnabled == dataSyncEnabledFlag
346         and: 'the data store sync state is set to #expectedDataStoreSyncState'
347             compositeState.dataStores.operationalDataStore.dataStoreSyncState == expectedDataStoreSyncState
348         and: 'the cps data service to delete data nodes is invoked the expected number of times'
349             deleteDataNodeExpectedNumberOfInvocation * mockCpsDataService.deleteDataNode(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, 'some-cm-handle-id', '/netconf-state', _)
350         and: 'the inventory persistence service to update node leaves is called with the correct values'
351             saveCmHandleStateExpectedNumberOfInvocations * mockInventoryPersistence.saveCmHandleState('some-cm-handle-id', compositeState)
352         where: 'the following data sync enabled flag is used'
353             scenario                                              | dataSyncEnabledFlag | initialDataSyncEnabledFlag | initialDataSyncState               || expectedDataStoreSyncState         | deleteDataNodeExpectedNumberOfInvocation | saveCmHandleStateExpectedNumberOfInvocations
354             'enabled'                                             | true                | false                      | DataStoreSyncState.NONE_REQUESTED  || DataStoreSyncState.UNSYNCHRONIZED  | 0                                        | 1
355             'disabled'                                            | false               | true                       | DataStoreSyncState.UNSYNCHRONIZED  || DataStoreSyncState.NONE_REQUESTED  | 0                                        | 1
356             'disabled where sync-state is currently SYNCHRONIZED' | false               | true                       | DataStoreSyncState.SYNCHRONIZED    || DataStoreSyncState.NONE_REQUESTED  | 1                                        | 1
357             'is set to existing flag state'                       | true                | true                       | DataStoreSyncState.UNSYNCHRONIZED  || DataStoreSyncState.UNSYNCHRONIZED  | 0                                        | 0
358     }
359
360     def 'Set cm Handle Data Sync Enabled flag with following cm handle not in ready state exception' () {
361         given: 'a cm handle composite state'
362             def compositeState = new CompositeState(cmHandleState: CmHandleState.ADVISED, dataSyncEnabled: false)
363         and: 'get cm handle state returns the composite state for the given cm handle id'
364             mockInventoryPersistence.getCmHandleState('some-cm-handle-id') >> compositeState
365         when: 'set data sync enabled is called with the data sync enabled flag set to true'
366             objectUnderTest.setDataSyncEnabled('some-cm-handle-id', true)
367         then: 'the expected exception is thrown'
368             thrown(CpsException)
369         and: 'the inventory persistence service to update node leaves is not invoked'
370             0 * mockInventoryPersistence.saveCmHandleState(_, _)
371     }
372
373     def 'Get all cm handle IDs by DMI plugin identifier.' () {
374         given: 'cm handle queries service returns cm handles'
375             1 * mockCmHandleQueries.getCmHandleIdsByDmiPluginIdentifier('some-dmi-plugin-identifier') >> ['cm-handle-1','cm-handle-2']
376         when: 'cm handle Ids are requested with dmi plugin identifier'
377             def result = objectUnderTest.getAllCmHandleIdsByDmiPluginIdentifier('some-dmi-plugin-identifier')
378         then: 'the result size is correct'
379             assert result.size() == 2
380         and: 'the result returns the correct details'
381             assert result.containsAll('cm-handle-1','cm-handle-2')
382     }
383
384     def dataStores() {
385         CompositeState.DataStores.builder()
386             .operationalDataStore(CompositeState.Operational.builder()
387                 .dataStoreSyncState(DataStoreSyncState.NONE_REQUESTED)
388                 .lastSyncTime('some-timestamp').build()).build()
389     }
390
391     def mockDataNode() {
392         mockCpsDataService.getDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> dataNode
393     }
394
395     def getDataOperationRequest(datastore) {
396         def dataOperationRequest = new DataOperationRequest()
397         def dataOperationDefinitions = new ArrayList()
398         dataOperationDefinitions.add(getDataOperationDefinition(datastore))
399         dataOperationRequest.setDataOperationDefinitions(dataOperationDefinitions)
400         return dataOperationRequest
401     }
402
403     def getDataOperationDefinition(datastore) {
404         def dataOperationDefinition = new DataOperationDefinition()
405         dataOperationDefinition.setOperation("read")
406         dataOperationDefinition.setOperationId("operational-12")
407         dataOperationDefinition.setDatastore(datastore)
408         def targetIds = new ArrayList()
409         targetIds.add("some-cm-handle")
410         dataOperationDefinition.setCmHandleIds(targetIds)
411         return dataOperationDefinition
412     }
413 }