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