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