Merge "Ncmp out event for REJECTED scenario"
[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.models.DmiPluginRegistrationResponse
36 import org.onap.cps.ncmp.api.models.CmResourceAddress
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 import java.util.stream.Collectors
60 import org.onap.cps.utils.JsonObjectMapper
61 import com.fasterxml.jackson.databind.ObjectMapper
62 import org.onap.cps.api.CpsDataService
63 import org.onap.cps.ncmp.api.impl.operations.DmiDataOperations
64 import org.onap.cps.spi.FetchDescendantsOption
65 import org.onap.cps.spi.model.DataNode
66 import org.springframework.http.HttpStatus
67 import org.springframework.http.ResponseEntity
68 import spock.lang.Specification
69
70 class NetworkCmProxyDataServiceImplSpec extends Specification {
71
72     def mockCpsDataService = Mock(CpsDataService)
73     def spiedJsonObjectMapper = Spy(new JsonObjectMapper(new ObjectMapper()))
74     def mockDmiDataOperations = Mock(DmiDataOperations)
75     def nullNetworkCmProxyDataServicePropertyHandler = null
76     def mockInventoryPersistence = Mock(InventoryPersistence)
77     def mockCmHandleQueries = Mock(CmHandleQueries)
78     def mockDmiPluginRegistration = Mock(DmiPluginRegistration)
79     def mockCpsCmHandlerQueryService = Mock(NetworkCmProxyCmHandleQueryService)
80     def mockLcmEventsCmHandleStateHandler = Mock(LcmEventsCmHandleStateHandler)
81     def stubModuleSyncStartedOnCmHandles = Stub(IMap<String, Object>)
82     def stubTrustLevelPerDmiPlugin = Stub(Map<String, TrustLevel>)
83     def mockTrustLevelManager = Mock(TrustLevelManager)
84     def mockAlternateIdChecker = Mock(AlternateIdChecker)
85
86     def NO_TOPIC = null
87     def NO_REQUEST_ID = null
88     def NO_AUTH_HEADER = 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', null)
117         then: 'DMI called with correct data'
118             1 * mockDmiDataOperations.writeResourceDataPassThroughRunningFromDmi('testCmHandle', 'testResourceId',
119                     CREATE, '{some-json}', 'application/json', null)
120                 >> { new ResponseEntity<>(HttpStatus.CREATED) }
121     }
122
123     def 'Get resource data for from DMI.'() {
124         given: 'cpsDataService returns valid data node'
125             mockDataNode()
126         and: 'some cm resource address'
127             def cmResourceAddress = new CmResourceAddress('some datastore','some CM Handle', 'some resource Id')
128         and: 'get resource data from DMI is called'
129             mockDmiDataOperations.getResourceDataFromDmi(cmResourceAddress, OPTIONS_PARAM, NO_TOPIC, NO_REQUEST_ID, NO_AUTH_HEADER) >>
130                     new ResponseEntity<>('dmi-response', HttpStatus.OK)
131         when: 'get resource data operational for the given cm resource address is called'
132             def response = objectUnderTest.getResourceDataForCmHandle(cmResourceAddress, OPTIONS_PARAM, NO_TOPIC, NO_REQUEST_ID, NO_AUTH_HEADER)
133         then: 'DMI returns a json response'
134             assert response == 'dmi-response'
135     }
136
137     def 'Get resource data for operational (cached) data.'() {
138         given: 'CPS Data service returns some object(s)'
139             mockCpsDataService.getDataNodes(OPERATIONAL.datastoreName, 'testCmHandle', 'testResourceId', FetchDescendantsOption.OMIT_DESCENDANTS) >> ['First Object', 'other Object']
140         and: 'a cm resource address for the same datastore, cm handle and resource id'
141             def cmResourceAddress = new CmResourceAddress(OPERATIONAL.datastoreName, 'testCmHandle', 'testResourceId')
142         when: 'get resource data is called'
143             def response = objectUnderTest.getResourceDataForCmHandle(cmResourceAddress, FetchDescendantsOption.OMIT_DESCENDANTS)
144         then: 'get resource data returns teh first object from the data service'
145             assert response == 'First Object'
146     }
147
148     def 'Execute (async) data operation for #datastoreName from DMI.'() {
149         given: 'cpsDataService returns valid data node'
150             def dataOperationRequest = getDataOperationRequest(datastoreName)
151         when: 'request resource data for data operation is called'
152             objectUnderTest.executeDataOperationForCmHandles('some topic', dataOperationRequest, 'requestId', NO_AUTH_HEADER)
153         then: 'request resource data for data operation returns expected response'
154             1 * mockDmiDataOperations.requestResourceDataFromDmi('some topic', dataOperationRequest, 'requestId', NO_AUTH_HEADER)
155         where: 'the following data stores are used'
156             datastoreName << [PASSTHROUGH_RUNNING.datastoreName, PASSTHROUGH_OPERATIONAL.datastoreName]
157     }
158
159     def 'Getting Yang Resources.'() {
160         when: 'yang resources is called'
161             objectUnderTest.getYangResourcesModuleReferences('some-cm-handle')
162         then: 'CPS module services is invoked for the correct dataspace and cm handle'
163             1 * mockInventoryPersistence.getYangResourcesModuleReferences('some-cm-handle')
164     }
165
166     def 'Get a cm handle.'() {
167         given: 'the system returns a yang modelled cm handle'
168             def dmiServiceName = 'some service name'
169             def compositeState = new CompositeState(cmHandleState: CmHandleState.ADVISED,
170                     lockReason: CompositeState.LockReason.builder().lockReasonCategory(LockReasonCategory.MODULE_SYNC_FAILED).details("lock details").build(),
171                     lastUpdateTime: 'some-timestamp',
172                     dataSyncEnabled: false,
173                     dataStores: dataStores())
174             def dmiProperties = [new YangModelCmHandle.Property('Book', 'Romance Novel')]
175             def publicProperties = [new YangModelCmHandle.Property('Public Book', 'Public Romance Novel')]
176             def moduleSetTag = 'some-module-set-tag'
177             def alternateId = 'some-alternate-id'
178             def yangModelCmHandle = new YangModelCmHandle(id: 'some-cm-handle', dmiServiceName: dmiServiceName,
179                     dmiProperties: dmiProperties, publicProperties: publicProperties, compositeState: compositeState,
180                     moduleSetTag: moduleSetTag, alternateId: alternateId)
181             1 * mockInventoryPersistence.getYangModelCmHandle('some-cm-handle') >> yangModelCmHandle
182         when: 'getting cm handle details for a given cm handle id from ncmp service'
183             def result = objectUnderTest.getNcmpServiceCmHandle('some-cm-handle')
184         then: 'the result is a ncmpServiceCmHandle'
185             result.class == NcmpServiceCmHandle.class
186         and: 'the cm handle contains the cm handle id'
187             result.cmHandleId == 'some-cm-handle'
188         and: 'the cm handle contains the alternate id'
189             result.alternateId == 'some-alternate-id'
190         and: 'the cm handle contains the module-set-tag'
191             result.moduleSetTag == 'some-module-set-tag'
192         and: 'the cm handle contains the DMI Properties'
193             result.dmiProperties ==[ Book:'Romance Novel' ]
194         and: 'the cm handle contains the public Properties'
195             result.publicProperties == [ "Public Book":'Public Romance Novel' ]
196         and: 'the cm handle contains the cm handle composite state'
197             result.compositeState == compositeState
198     }
199
200     def 'Get cm handle public properties'() {
201         given: 'a yang modelled cm handle'
202             def dmiProperties = [new YangModelCmHandle.Property('prop', 'some DMI property')]
203             def publicProperties = [new YangModelCmHandle.Property('public prop', 'some public prop')]
204             def yangModelCmHandle = new YangModelCmHandle(id:'some-cm-handle', dmiServiceName: 'some service name', dmiProperties: dmiProperties, publicProperties: publicProperties)
205         and: 'the system returns this yang modelled cm handle'
206             1 * mockInventoryPersistence.getYangModelCmHandle('some-cm-handle') >> yangModelCmHandle
207         when: 'getting cm handle public properties for a given cm handle id from ncmp service'
208             def result = objectUnderTest.getCmHandlePublicProperties('some-cm-handle')
209         then: 'the result returns the correct data'
210             result == [ 'public prop' : 'some public prop' ]
211     }
212
213     def 'Execute cm handle id search for inventory'() {
214         given: 'a ConditionApiProperties object'
215             def conditionProperties = new ConditionProperties()
216             conditionProperties.conditionName = 'hasAllProperties'
217             conditionProperties.conditionParameters = [ [ 'some-key' : 'some-value' ] ]
218             def conditionServiceProps = new CmHandleQueryServiceParameters()
219             conditionServiceProps.cmHandleQueryParameters = [conditionProperties] as List<ConditionProperties>
220         and: 'the system returns an set of cmHandle ids'
221             mockCpsCmHandlerQueryService.queryCmHandleIdsForInventory(*_) >> [ 'cmHandle1', 'cmHandle2' ]
222         when: 'getting cm handle id set for a given dmi property'
223             def result = objectUnderTest.executeCmHandleIdSearchForInventory(conditionServiceProps)
224         then: 'the result returns the correct 2 elements'
225             assert result.size() == 2
226             assert result.contains('cmHandle1')
227             assert result.contains('cmHandle2')
228     }
229
230     def 'Get cm handle composite state'() {
231         given: 'a yang modelled cm handle'
232             def compositeState = new CompositeState(cmHandleState: CmHandleState.ADVISED,
233                 lockReason: CompositeState.LockReason.builder().lockReasonCategory(LockReasonCategory.MODULE_SYNC_FAILED).details("lock details").build(),
234                 lastUpdateTime: 'some-timestamp',
235                 dataSyncEnabled: false,
236                 dataStores: dataStores())
237             def dmiProperties = [new YangModelCmHandle.Property('prop', 'some DMI property')]
238             def publicProperties = [new YangModelCmHandle.Property('public prop', 'some public prop')]
239             def yangModelCmHandle = new YangModelCmHandle(id:'some-cm-handle', dmiServiceName: 'some service name', dmiProperties: dmiProperties, publicProperties: publicProperties, compositeState: compositeState)
240         and: 'the system returns this yang modelled cm handle'
241             1 * mockInventoryPersistence.getYangModelCmHandle('some-cm-handle') >> yangModelCmHandle
242         when: 'getting cm handle composite state for a given cm handle id from ncmp service'
243             def result = objectUnderTest.getCmHandleCompositeState('some-cm-handle')
244         then: 'the result returns the correct data'
245             result == compositeState
246     }
247
248     def 'Update resource data for pass-through running from dmi using POST #scenario DMI properties.'() {
249         given: 'cpsDataService returns valid datanode'
250             mockCpsDataService.getDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> dataNode
251         when: 'get resource data is called'
252             objectUnderTest.writeResourceDataPassThroughRunningForCmHandle('testCmHandle',
253                 'testResourceId', UPDATE,
254                 '{some-json}', 'application/json', NO_AUTH_HEADER)
255         then: 'DMI called with correct data'
256             1 * mockDmiDataOperations.writeResourceDataPassThroughRunningFromDmi('testCmHandle', 'testResourceId',
257                     UPDATE, '{some-json}', 'application/json', NO_AUTH_HEADER)
258                 >> { new ResponseEntity<>(HttpStatus.OK) }
259     }
260
261     def 'Verify modules and create anchor params.'() {
262         given: 'dmi plugin registration return created cm handles'
263             def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'service1', dmiModelPlugin: 'service1',
264                     dmiDataPlugin: 'service2')
265             dmiPluginRegistration.createdCmHandles = [ncmpServiceCmHandle]
266             mockDmiPluginRegistration.getCreatedCmHandles() >> [ncmpServiceCmHandle]
267         and: 'no rejected cm handles because of alternate ids'
268             mockAlternateIdChecker.getIdsOfCmHandlesWithRejectedAlternateId(*_) >> []
269         when: 'parse and create cm handle in dmi registration then sync module'
270             mockDmiPluginRegistration.createdCmHandles = ['test-cm-handle-id']
271             objectUnderTest.processCreatedCmHandles(mockDmiPluginRegistration, new DmiPluginRegistrationResponse())
272         then: 'system persists the cm handle state'
273             1 * mockLcmEventsCmHandleStateHandler.initiateStateAdvised(_) >> {
274                 args -> {
275                           def cmHandleStatePerCmHandle = (args[0] as Collection)
276                           cmHandleStatePerCmHandle.each {
277                             assert it.id == 'test-cm-handle-id'
278                           }
279                     }
280             }
281     }
282
283     def 'Execute cm handle id search'() {
284         given: 'valid CmHandleQueryApiParameters input'
285             def cmHandleQueryApiParameters = new CmHandleQueryApiParameters()
286             def conditionApiProperties = new ConditionApiProperties()
287             conditionApiProperties.conditionName = 'hasAllModules'
288             conditionApiProperties.conditionParameters = [[moduleName: 'module-name-1']]
289             cmHandleQueryApiParameters.cmHandleQueryParameters = [conditionApiProperties]
290         and: 'query cm handle method return with a data node list'
291             mockCpsCmHandlerQueryService.queryCmHandleIds(
292                     spiedJsonObjectMapper.convertToValueType(cmHandleQueryApiParameters, CmHandleQueryServiceParameters.class))
293                     >> ['cm-handle-id-1']
294         when: 'execute cm handle search is called'
295             def result = objectUnderTest.executeCmHandleIdSearch(cmHandleQueryApiParameters)
296         then: 'result is the same collection as returned by the CPS Data Service'
297             assert result == ['cm-handle-id-1']
298     }
299
300     def 'Getting module definitions by module'() {
301         when: 'get module definitions is performed with module name'
302             objectUnderTest.getModuleDefinitionsByCmHandleAndModule('some-cm-handle', 'some-module', '2021-08-04')
303         then: 'ncmp inventory persistence service is invoked once with correct parameters'
304             1 * mockInventoryPersistence.getModuleDefinitionsByCmHandleAndModule('some-cm-handle', 'some-module', '2021-08-04')
305     }
306
307     def 'Getting module definitions by cm handle id'() {
308         when: 'get module definitions is performed with cm handle id'
309             objectUnderTest.getModuleDefinitionsByCmHandleId('some-cm-handle')
310         then: 'ncmp inventory persistence service is invoked once with correct parameter'
311             1 * mockInventoryPersistence.getModuleDefinitionsByCmHandleId('some-cm-handle')
312     }
313
314     def 'Execute cm handle search'() {
315         given: 'valid CmHandleQueryApiParameters input'
316             def cmHandleQueryApiParameters = new CmHandleQueryApiParameters()
317             def conditionApiProperties = new ConditionApiProperties()
318             conditionApiProperties.conditionName = 'hasAllModules'
319             conditionApiProperties.conditionParameters = [[moduleName: 'module-name-1']]
320             cmHandleQueryApiParameters.cmHandleQueryParameters = [conditionApiProperties]
321         and: 'query cm handle method return with a data node list'
322             mockCpsCmHandlerQueryService.queryCmHandles(
323                     spiedJsonObjectMapper.convertToValueType(cmHandleQueryApiParameters, CmHandleQueryServiceParameters.class))
324                     >> [new NcmpServiceCmHandle(cmHandleId: 'cm-handle-id-1')]
325         when: 'execute cm handle search is called'
326             def result = objectUnderTest.executeCmHandleSearch(cmHandleQueryApiParameters)
327         then: 'result is the same collection as returned by the CPS Data Service'
328             assert result.stream().map(d -> d.cmHandleId).collect(Collectors.toSet()) == ['cm-handle-id-1'] as Set
329     }
330
331     def 'Set Cm Handle Data Sync Enabled Flag where data sync flag is  #scenario'() {
332         given: 'an existing cm handle composite state'
333             def compositeState = new CompositeState(cmHandleState: CmHandleState.READY, dataSyncEnabled: initialDataSyncEnabledFlag,
334                 dataStores: CompositeState.DataStores.builder()
335                     .operationalDataStore(CompositeState.Operational.builder()
336                         .dataStoreSyncState(initialDataSyncState)
337                         .build()).build())
338         and: 'get cm handle state returns the composite state for the given cm handle id'
339             mockInventoryPersistence.getCmHandleState('some-cm-handle-id') >> compositeState
340         when: 'set data sync enabled is called with the data sync enabled flag set to #dataSyncEnabledFlag'
341             objectUnderTest.setDataSyncEnabled('some-cm-handle-id', dataSyncEnabledFlag)
342         then: 'the data sync enabled flag is set to #dataSyncEnabled'
343             compositeState.dataSyncEnabled == dataSyncEnabledFlag
344         and: 'the data store sync state is set to #expectedDataStoreSyncState'
345             compositeState.dataStores.operationalDataStore.dataStoreSyncState == expectedDataStoreSyncState
346         and: 'the cps data service to delete data nodes is invoked the expected number of times'
347             deleteDataNodeExpectedNumberOfInvocation * mockCpsDataService.deleteDataNode(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, 'some-cm-handle-id', '/netconf-state', _)
348         and: 'the inventory persistence service to update node leaves is called with the correct values'
349             saveCmHandleStateExpectedNumberOfInvocations * mockInventoryPersistence.saveCmHandleState('some-cm-handle-id', compositeState)
350         where: 'the following data sync enabled flag is used'
351             scenario                                              | dataSyncEnabledFlag | initialDataSyncEnabledFlag | initialDataSyncState               || expectedDataStoreSyncState         | deleteDataNodeExpectedNumberOfInvocation | saveCmHandleStateExpectedNumberOfInvocations
352             'enabled'                                             | true                | false                      | DataStoreSyncState.NONE_REQUESTED  || DataStoreSyncState.UNSYNCHRONIZED  | 0                                        | 1
353             'disabled'                                            | false               | true                       | DataStoreSyncState.UNSYNCHRONIZED  || DataStoreSyncState.NONE_REQUESTED  | 0                                        | 1
354             'disabled where sync-state is currently SYNCHRONIZED' | false               | true                       | DataStoreSyncState.SYNCHRONIZED    || DataStoreSyncState.NONE_REQUESTED  | 1                                        | 1
355             'is set to existing flag state'                       | true                | true                       | DataStoreSyncState.UNSYNCHRONIZED  || DataStoreSyncState.UNSYNCHRONIZED  | 0                                        | 0
356     }
357
358     def 'Set cm Handle Data Sync Enabled flag with following cm handle not in ready state exception' () {
359         given: 'a cm handle composite state'
360             def compositeState = new CompositeState(cmHandleState: CmHandleState.ADVISED, dataSyncEnabled: false)
361         and: 'get cm handle state returns the composite state for the given cm handle id'
362             mockInventoryPersistence.getCmHandleState('some-cm-handle-id') >> compositeState
363         when: 'set data sync enabled is called with the data sync enabled flag set to true'
364             objectUnderTest.setDataSyncEnabled('some-cm-handle-id', true)
365         then: 'the expected exception is thrown'
366             thrown(CpsException)
367         and: 'the inventory persistence service to update node leaves is not invoked'
368             0 * mockInventoryPersistence.saveCmHandleState(_, _)
369     }
370
371     def 'Get all cm handle IDs by DMI plugin identifier.' () {
372         given: 'cm handle queries service returns cm handles'
373             1 * mockCmHandleQueries.getCmHandleIdsByDmiPluginIdentifier('some-dmi-plugin-identifier') >> ['cm-handle-1','cm-handle-2']
374         when: 'cm handle Ids are requested with dmi plugin identifier'
375             def result = objectUnderTest.getAllCmHandleIdsByDmiPluginIdentifier('some-dmi-plugin-identifier')
376         then: 'the result size is correct'
377             assert result.size() == 2
378         and: 'the result returns the correct details'
379             assert result.containsAll('cm-handle-1','cm-handle-2')
380     }
381
382     def dataStores() {
383         CompositeState.DataStores.builder()
384             .operationalDataStore(CompositeState.Operational.builder()
385                 .dataStoreSyncState(DataStoreSyncState.NONE_REQUESTED)
386                 .lastSyncTime('some-timestamp').build()).build()
387     }
388
389     def mockDataNode() {
390         mockCpsDataService.getDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> dataNode
391     }
392
393     def getDataOperationRequest(datastore) {
394         def dataOperationRequest = new DataOperationRequest()
395         def dataOperationDefinitions = new ArrayList()
396         dataOperationDefinitions.add(getDataOperationDefinition(datastore))
397         dataOperationRequest.setDataOperationDefinitions(dataOperationDefinitions)
398         return dataOperationRequest
399     }
400
401     def getDataOperationDefinition(datastore) {
402         def dataOperationDefinition = new DataOperationDefinition()
403         dataOperationDefinition.setOperation("read")
404         dataOperationDefinition.setOperationId("operational-12")
405         dataOperationDefinition.setDatastore(datastore)
406         def targetIds = new ArrayList()
407         targetIds.add("some-cm-handle")
408         dataOperationDefinition.setCmHandleIds(targetIds)
409         return dataOperationDefinition
410     }
411 }