2 * ============LICENSE_START=======================================================
3 * Copyright (C) 2021 Pantheon.tech
4 * Modifications Copyright (C) 2021 highstreet technologies GmbH
5 * Modifications Copyright (C) 2021-2022 Nordix Foundation
6 * Modifications Copyright (C) 2021-2022 Bell Canada.
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.rest.controller
26 import com.fasterxml.jackson.databind.ObjectMapper
27 import org.mapstruct.factory.Mappers
28 import org.onap.cps.TestUtils
29 import org.onap.cps.ncmp.api.NetworkCmProxyDataService
30 import org.onap.cps.ncmp.api.inventory.CmHandleState
31 import org.onap.cps.ncmp.api.inventory.CompositeState
32 import org.onap.cps.ncmp.api.inventory.DataStoreSyncState
33 import org.onap.cps.ncmp.api.inventory.LockReasonCategory
34 import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle
35 import org.onap.cps.ncmp.rest.controller.handlers.DatastoreType
36 import org.onap.cps.ncmp.rest.controller.handlers.NcmpDatastoreOperationalResourceRequestHandler
37 import org.onap.cps.ncmp.rest.controller.handlers.NcmpDatastorePassthroughOperationalResourceRequestHandler
38 import org.onap.cps.ncmp.rest.controller.handlers.NcmpDatastorePassthroughRunningResourceRequestHandler
39 import org.onap.cps.ncmp.rest.controller.handlers.NcmpDatastoreResourceRequestHandlerFactory
40 import org.onap.cps.ncmp.rest.executor.CpsNcmpTaskExecutor
41 import org.onap.cps.ncmp.rest.mapper.CmHandleStateMapper
42 import org.onap.cps.ncmp.rest.util.DeprecationHelper
43 import org.onap.cps.spi.FetchDescendantsOption
44 import org.onap.cps.spi.model.ModuleDefinition
45 import org.onap.cps.spi.model.ModuleReference
46 import org.onap.cps.utils.JsonObjectMapper
47 import org.spockframework.spring.SpringBean
48 import org.springframework.beans.factory.annotation.Autowired
49 import org.springframework.beans.factory.annotation.Value
50 import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
51 import org.springframework.http.HttpStatus
52 import org.springframework.http.MediaType
53 import org.springframework.test.web.servlet.MockMvc
54 import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder
55 import spock.lang.Shared
56 import spock.lang.Specification
58 import java.time.OffsetDateTime
59 import java.time.ZoneOffset
60 import java.time.format.DateTimeFormatter
62 import static org.onap.cps.ncmp.api.inventory.CompositeState.DataStores
63 import static org.onap.cps.ncmp.api.inventory.CompositeState.Operational
64 import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.CREATE
65 import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.UPDATE
66 import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.PATCH
67 import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.DELETE
68 import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get
69 import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put
70 import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post
71 import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch
72 import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete
74 @WebMvcTest(NetworkCmProxyController)
75 class NetworkCmProxyControllerSpec extends Specification {
77 public static final int TIMEOUT_IN_MS = 2000
78 public static final boolean NOTIFICATION_ENABLED = true
84 NetworkCmProxyDataService mockNetworkCmProxyDataService = Mock()
87 ObjectMapper objectMapper = new ObjectMapper()
90 JsonObjectMapper jsonObjectMapper = new JsonObjectMapper(objectMapper)
93 NcmpRestInputMapper ncmpRestInputMapper = Mappers.getMapper(NcmpRestInputMapper)
96 CmHandleStateMapper cmHandleStateMapper = Mappers.getMapper(CmHandleStateMapper)
99 CpsNcmpTaskExecutor spiedCpsTaskExecutor = Spy()
102 DeprecationHelper stubbedDeprecationHelper = Stub()
105 NcmpDatastoreResourceRequestHandlerFactory stubbedNcmpDatastoreResourceRequestHandlerFactory = Stub()
107 @Value('${rest.api.ncmp-base-path}/v1')
110 def requestBody = '{"some-key":"some-value"}'
114 def NO_REQUEST_ID = null
116 def formattedDateAndTime = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ")
117 .format(OffsetDateTime.of(2022, 12, 31, 20, 30, 40, 1, ZoneOffset.UTC))
120 stubbedNcmpDatastoreResourceRequestHandlerFactory.getNcmpDatastoreResourceRequestHandler(
121 DatastoreType.OPERATIONAL) >>
122 new NcmpDatastoreOperationalResourceRequestHandler(
123 mockNetworkCmProxyDataService, spiedCpsTaskExecutor, TIMEOUT_IN_MS, NOTIFICATION_ENABLED)
125 stubbedNcmpDatastoreResourceRequestHandlerFactory.getNcmpDatastoreResourceRequestHandler(
126 DatastoreType.PASSTHROUGH_OPERATIONAL) >>
127 new NcmpDatastorePassthroughOperationalResourceRequestHandler(
128 mockNetworkCmProxyDataService, spiedCpsTaskExecutor, TIMEOUT_IN_MS, NOTIFICATION_ENABLED)
130 stubbedNcmpDatastoreResourceRequestHandlerFactory.getNcmpDatastoreResourceRequestHandler(
131 DatastoreType.PASSTHROUGH_RUNNING) >>
132 new NcmpDatastorePassthroughRunningResourceRequestHandler(
133 mockNetworkCmProxyDataService, spiedCpsTaskExecutor, TIMEOUT_IN_MS, NOTIFICATION_ENABLED)
136 def 'Get Resource Data from pass-through operational.'() {
137 given: 'resource data url'
138 def getUrl = "$ncmpBasePathV1/ch/testCmHandle/data/ds/ncmp-datastore:passthrough-operational" +
139 "?resourceIdentifier=parent/child&options=(a=1,b=2)"
140 when: 'get data resource request is performed'
141 def response = mvc.perform(
143 .contentType(MediaType.APPLICATION_JSON)
144 ).andReturn().response
145 then: 'the NCMP data service is called with getResourceDataOperationalForCmHandle'
146 1 * mockNetworkCmProxyDataService.getResourceDataOperationalForCmHandle('testCmHandle',
151 and: 'response status is Ok'
152 response.status == HttpStatus.OK.value()
155 def 'Get Resource Data from #datastoreInUrl with #scenario.'() {
156 given: 'resource data url'
157 def getUrl = "$ncmpBasePathV1/ch/testCmHandle/data/ds/ncmp-datastore:${datastoreInUrl}" +
158 "?resourceIdentifier=parent/child&options=(a=1,b=2)${topicQueryParam}"
159 when: 'get data resource request is performed'
160 def response = mvc.perform(
161 get(getUrl).contentType(MediaType.APPLICATION_JSON)).andReturn().response
162 then: 'task executor is called appropriate number of times'
163 expectedNumberOfExecutorExecutions * spiedCpsTaskExecutor.executeTask(_, TIMEOUT_IN_MS)
164 and: 'response status is expected'
165 response.status == HttpStatus.OK.value()
166 where: 'the following parameters are used'
167 scenario | datastoreInUrl | topicQueryParam || expectedTopicName | expectedNumberOfExecutorExecutions
168 'url with valid topic' | 'passthrough-operational' | '&topic=my-topic-name' || 'my-topic-name' | 1
169 'no topic in url' | 'passthrough-operational' | '' || NO_TOPIC | 0
170 'null topic in url' | 'passthrough-operational' | '&topic=null' || 'null' | 1
171 'url with valid topic' | 'passthrough-running' | '&topic=my-topic-name' || 'my-topic-name' | 1
172 'no topic in url' | 'passthrough-running' | '' || NO_TOPIC | 0
173 'null topic in url' | 'passthrough-running' | '&topic=null' || 'null' | 1
176 def 'Fail to get Resource Data from #datastoreInUrl when #scenario.'() {
177 given: 'resource data url'
178 def getUrl = "$ncmpBasePathV1/ch/testCmHandle/data/ds/ncmp-datastore:${datastoreInUrl}" +
179 "?resourceIdentifier=parent/child&options=(a=1,b=2)${topicQueryParam}"
180 when: 'get data resource request is performed'
181 def response = mvc.perform(
182 get(getUrl).contentType(MediaType.APPLICATION_JSON)).andReturn().response
183 then: 'abad request is returned'
184 response.status == HttpStatus.BAD_REQUEST.value()
185 where: 'the following parameters are used'
186 scenario | datastoreInUrl | topicQueryParam
187 'empty topic in url' | 'passthrough-operational' | '&topic=\"\"'
188 'missing topic in url' | 'passthrough-operational' | '&topic='
189 'blank topic value in url' | 'passthrough-operational' | '&topic=\" \"'
190 'invalid non-empty topic value in url' | 'passthrough-operational' | '&topic=1_5_*_#'
191 'empty topic in url' | 'passthrough-running' | '&topic=\"\"'
192 'missing topic in url' | 'passthrough-running' | '&topic='
193 'blank topic value in url' | 'passthrough-running' | '&topic=\" \"'
194 'invalid non-empty topic value in url' | 'passthrough-running' | '&topic=1_5_*_#'
197 def 'Get Resource Data from pass-through running with #scenario value in resource identifier param.'() {
198 given: 'resource data url'
199 def getUrl = "$ncmpBasePathV1/ch/testCmHandle/data/ds/ncmp-datastore:passthrough-running" +
200 "?resourceIdentifier=" + resourceIdentifier + "&options=(a=1,b=2)"
201 and: 'ncmp service returns json object'
202 mockNetworkCmProxyDataService.getResourceDataPassThroughRunningForCmHandle('testCmHandle',
206 NO_REQUEST_ID) >> '{valid-json}'
207 when: 'get data resource request is performed'
208 def response = mvc.perform(
210 .contentType(MediaType.APPLICATION_JSON)
211 ).andReturn().response
212 then: 'response status is Ok'
213 response.status == HttpStatus.OK.value()
214 and: 'response contains valid object body'
215 response.getContentAsString() == '{valid-json}'
216 where: 'tokens are used in the resource identifier parameter'
217 scenario | resourceIdentifier
218 '/' | 'id/with/slashes'
223 '? needs to be encoded as %3F' | 'idWith%3F'
226 def 'Update resource data from pass-through running.'() {
227 given: 'update resource data url'
228 def updateUrl = "$ncmpBasePathV1/ch/testCmHandle/data/ds/ncmp-datastore:passthrough-running" +
229 "?resourceIdentifier=parent/child"
230 when: 'update data resource request is performed'
231 def response = mvc.perform(
233 .contentType(MediaType.APPLICATION_JSON_VALUE).content(requestBody)
234 ).andReturn().response
235 then: 'ncmp service method to update resource is called'
236 1 * mockNetworkCmProxyDataService.writeResourceDataPassThroughRunningForCmHandle('testCmHandle',
237 'parent/child', UPDATE, requestBody, 'application/json;charset=UTF-8')
238 and: 'the response status is OK'
239 response.status == HttpStatus.OK.value()
242 def 'Create Resource Data from pass-through running with #scenario.'() {
243 given: 'resource data url'
244 def url = "$ncmpBasePathV1/ch/testCmHandle/data/ds/ncmp-datastore:passthrough-running" +
245 "?resourceIdentifier=parent/child"
246 when: 'create resource request is performed'
247 def response = mvc.perform(
249 .contentType(MediaType.APPLICATION_JSON_VALUE).content(requestBody)
250 ).andReturn().response
251 then: 'ncmp service method to create resource called'
252 1 * mockNetworkCmProxyDataService.writeResourceDataPassThroughRunningForCmHandle('testCmHandle',
253 'parent/child', CREATE, requestBody, 'application/json;charset=UTF-8')
254 and: 'resource is created'
255 response.status == HttpStatus.CREATED.value()
258 def 'Get module references for the given dataspace and cm handle.'() {
259 given: 'get module references url'
260 def getUrl = "$ncmpBasePathV1/ch/some-cmhandle/modules"
261 when: 'get module resource request is performed'
262 def response = mvc.perform(get(getUrl)).andReturn().response
263 then: 'ncmp service method to get yang resource module references is called'
264 mockNetworkCmProxyDataService.getYangResourcesModuleReferences('some-cmhandle')
265 >> [new ModuleReference(moduleName: 'some-name1', revision: '2021-10-03')]
266 and: 'response contains an array with the module name and revision'
267 response.getContentAsString() == '[{"moduleName":"some-name1","revision":"2021-10-03"}]'
268 and: 'response returns an OK http code'
269 response.status == HttpStatus.OK.value()
272 def 'Retrieve cm handles.'() {
273 given: 'an endpoint and json data'
274 def searchesEndpoint = "$ncmpBasePathV1/ch/searches"
275 String jsonString = TestUtils.getResourceFileContent('cmhandle-search.json')
276 and: 'the service method is invoked with module names and returns two cm handles'
277 def cmHandle1 = new NcmpServiceCmHandle()
278 cmHandle1.cmHandleId = 'some-cmhandle-id1'
279 cmHandle1.publicProperties = [color: 'yellow']
280 def cmHandle2 = new NcmpServiceCmHandle()
281 cmHandle2.cmHandleId = 'some-cmhandle-id2'
282 cmHandle2.publicProperties = [color: 'green']
283 mockNetworkCmProxyDataService.executeCmHandleSearch(_) >> [cmHandle1, cmHandle2]
284 when: 'the searches api is invoked'
285 def response = mvc.perform(post(searchesEndpoint)
286 .contentType(MediaType.APPLICATION_JSON)
287 .content(jsonString)).andReturn().response
288 then: 'response status returns OK'
289 response.status == HttpStatus.OK.value()
290 and: 'the expected response content is returned'
291 response.contentAsString == '[{"cmHandle":"some-cmhandle-id1","publicCmHandleProperties":[{"color":"yellow"}],"state":null},{"cmHandle":"some-cmhandle-id2","publicCmHandleProperties":[{"color":"green"}],"state":null}]'
294 def 'Get complete Cm Handle details by Cm Handle id.'() {
295 given: 'an endpoint and a cm handle'
296 def cmHandleDetailsEndpoint = "$ncmpBasePathV1/ch/some-cm-handle"
297 and: 'an existing ncmp service cm handle'
298 def cmHandleId = 'some-cm-handle'
299 def dmiProperties = [prop: 'some DMI property']
300 def publicProperties = ["public prop": 'some public property']
301 def compositeState = compositeStateTestObject()
302 def ncmpServiceCmHandle = new NcmpServiceCmHandle(cmHandleId: cmHandleId, dmiProperties: dmiProperties, publicProperties: publicProperties, compositeState: compositeState)
303 and: 'the service method is invoked with the cm handle id'
304 1 * mockNetworkCmProxyDataService.getNcmpServiceCmHandle('some-cm-handle') >> ncmpServiceCmHandle
305 when: 'the cm handle details api is invoked'
306 def response = mvc.perform(
307 get(cmHandleDetailsEndpoint)).andReturn().response
308 then: 'the correct response is returned'
309 response.status == HttpStatus.OK.value()
310 and: 'the response contains the public properties'
311 assertContainsPublicProperties(response)
312 and: 'the response contains the cm handle state'
313 assertContainsState(response)
314 and: 'the content does not contain dmi properties'
315 !response.contentAsString.contains("some DMI property")
318 def 'Get Cm Handle public properties by Cm Handle id.'() {
319 given: 'a cm handle properties endpoint'
320 def cmHandlePropertiesEndpoint = "$ncmpBasePathV1/ch/some-cm-handle/properties"
321 and: 'some cm handle public properties'
322 def publicProperties = ['public prop': 'some public property']
323 and: 'the service method is invoked with the cm handle id returning the cm handle public properties'
324 1 * mockNetworkCmProxyDataService
325 .getCmHandlePublicProperties('some-cm-handle') >> publicProperties
326 when: 'the cm handle properties api is invoked'
327 def response = mvc.perform(
328 get(cmHandlePropertiesEndpoint)).andReturn().response
329 then: 'the correct response is returned'
330 response.status == HttpStatus.OK.value()
331 and: 'the response contains the public properties'
332 assertContainsPublicProperties(response)
335 def 'Get Cm Handle composite state by Cm Handle id.'() {
336 given: 'a cm handle state endpoint'
337 def cmHandlePropertiesEndpoint = "$ncmpBasePathV1/ch/some-cm-handle/state"
338 and: 'some cm handle composite state'
339 def compositeState = compositeStateTestObject()
340 and: 'the service method is invoked with the cm handle id returning the cm handle composite state'
341 1 * mockNetworkCmProxyDataService
342 .getCmHandleCompositeState('some-cm-handle') >> compositeState
343 when: 'the cm handle state api is invoked'
344 def response = mvc.perform(
345 get(cmHandlePropertiesEndpoint)).andReturn().response
346 then: 'the correct response is returned'
347 response.status == HttpStatus.OK.value()
348 and: 'the response contains the cm handle state'
349 assertContainsState(response)
352 def 'Call execute cm handle searches with unrecognized condition name.'() {
353 given: 'an endpoint and json data'
354 def searchesEndpoint = "$ncmpBasePathV1/ch/searches"
355 String jsonString = TestUtils.getResourceFileContent('invalid-cmhandle-search.json')
356 and: 'the service method is invoked with module names and returns two cm handles'
357 def cmHandel1 = new NcmpServiceCmHandle()
358 cmHandel1.cmHandleId = 'some-cmhandle-id1'
359 cmHandel1.publicProperties = [color: 'yellow']
360 def cmHandel2 = new NcmpServiceCmHandle()
361 cmHandel2.cmHandleId = 'some-cmhandle-id2'
362 cmHandel2.publicProperties = [color: 'green']
363 mockNetworkCmProxyDataService.executeCmHandleSearch(_) >> [cmHandel1, cmHandel2]
364 when: 'the searches api is invoked'
365 def response = mvc.perform(
366 post(searchesEndpoint)
367 .contentType(MediaType.APPLICATION_JSON)
368 .content(jsonString)).andReturn().response
369 then: 'an empty cm handle identifier is returned'
370 response.contentAsString == '[{"cmHandle":"some-cmhandle-id1","publicCmHandleProperties":[{"color":"yellow"}],"state":null},{"cmHandle":"some-cmhandle-id2","publicCmHandleProperties":[{"color":"green"}],"state":null}]'
373 def 'Query for cm handles matching query parameters'() {
374 given: 'an endpoint and json data'
375 def searchesEndpoint = "$ncmpBasePathV1/ch/id-searches"
376 and: 'the service method is invoked with module names and returns cm handle ids'
377 1 * mockNetworkCmProxyDataService.executeCmHandleIdSearch(_) >> ['some-cmhandle-id1', 'some-cmhandle-id2']
378 when: 'the searches api is invoked'
379 def response = mvc.perform(
380 post(searchesEndpoint)
381 .contentType(MediaType.APPLICATION_JSON)
382 .content('{}')).andReturn().response
383 then: 'cm handle ids are returned'
384 response.contentAsString == '["some-cmhandle-id1","some-cmhandle-id2"]'
387 def 'Query for cm handles with invalid request payload'() {
388 when: 'the searches api is invoked'
389 def searchesEndpoint = "$ncmpBasePathV1/ch/id-searches"
390 def invalidInputData = '{invalidJson}'
391 def response = mvc.perform(
392 post(searchesEndpoint)
393 .contentType(MediaType.APPLICATION_JSON)
394 .content(invalidInputData)).andReturn().response
395 then: 'BAD_REQUEST is returned'
396 response.getStatus() == 400
399 def 'Patch resource data in pass-through running datastore.'() {
400 given: 'patch resource data url'
401 def url = "$ncmpBasePathV1/ch/testCmHandle/data/ds/ncmp-datastore:passthrough-running" +
402 "?resourceIdentifier=parent/child"
403 when: 'patch data resource request is performed'
404 def response = mvc.perform(
406 .contentType(MediaType.APPLICATION_JSON)
407 .accept(MediaType.APPLICATION_JSON).content(requestBody)
408 ).andReturn().response
409 then: 'ncmp service method to update resource is called'
410 1 * mockNetworkCmProxyDataService.writeResourceDataPassThroughRunningForCmHandle('testCmHandle',
411 'parent/child', PATCH, requestBody, 'application/json;charset=UTF-8')
412 and: 'the response status is OK'
413 response.status == HttpStatus.OK.value()
416 def 'Delete resource data in pass-through running datastore.'() {
417 given: 'delete resource data url'
418 def url = "$ncmpBasePathV1/ch/testCmHandle/data/ds/ncmp-datastore:passthrough-running" +
419 "?resourceIdentifier=parent/child"
420 when: 'delete data resource request is performed'
421 def response = mvc.perform(
423 .contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON)).andReturn().response
424 then: 'the ncmp service method to delete resource is called (with null as body)'
425 1 * mockNetworkCmProxyDataService.writeResourceDataPassThroughRunningForCmHandle('testCmHandle',
426 'parent/child', DELETE, null, 'application/json;charset=UTF-8')
427 and: 'the response is No Content'
428 response.status == HttpStatus.NO_CONTENT.value()
431 def 'Get resource data from DMI with valid topic i.e. async request for #scenario'() {
432 given: 'resource data url'
433 def getUrl = "$ncmpBasePathV1/ch/testCmHandle/data/ds/ncmp-datastore:${datastoreInUrl}" +
434 "?resourceIdentifier=parent/child&options=(a=1,b=2)&topic=my-topic-name"
435 when: 'get data resource request is performed'
436 def response = mvc.perform(
438 .contentType(MediaType.APPLICATION_JSON)
439 .accept(MediaType.APPLICATION_JSON_VALUE)
440 ).andReturn().response
441 then: 'async request id is generated'
442 assert response.contentAsString.contains("requestId")
443 where: 'the following parameters are used'
444 scenario | datastoreInUrl
445 ':passthrough-operational' | 'passthrough-operational'
446 ':passthrough-running' | 'passthrough-running'
449 def 'Get module definitions based on cmHandleId.'() {
450 when: 'get module definition request is performed'
451 def response = mvc.perform(
452 get("$ncmpBasePathV1/ch/some-cmhandle/modules/definitions"))
453 .andReturn().response
454 then: 'ncmp service method to get module definitions is called'
455 mockNetworkCmProxyDataService.getModuleDefinitionsByCmHandleId('some-cmhandle')
456 >> [new ModuleDefinition('sampleModuleName', '2021-10-03',
457 'module sampleModuleName{ sample module content }')]
458 and: 'response contains an array with the module name, revision and content'
459 response.getContentAsString() == '[{"moduleName":"sampleModuleName","revision":"2021-10-03","content":"module sampleModuleName{ sample module content }"}]'
460 and: 'response returns an OK http code'
461 response.status == HttpStatus.OK.value()
464 def 'Set the data sync enabled based on the cm handle id and the data sync flag is #scenario'() {
465 when: 'the set data sync enabled request is invoked'
466 def response = mvc.perform(
467 put("$ncmpBasePathV1/ch/some-cm-handle-id/data-sync?dataSyncEnabled=" + dataSyncEnabledFlag))
468 .andReturn().response
469 then: 'method to set data sync enabled is called'
470 1 * mockNetworkCmProxyDataService.setDataSyncEnabled('some-cm-handle-id', dataSyncEnabledFlag)
471 and: 'the response returns an OK http code'
472 response.status == HttpStatus.OK.value()
473 where: 'the following parameters are used'
474 scenario | dataSyncEnabledFlag
479 def 'Get Resource Data from operational with or without descendants'() {
480 given: 'resource data url with descendants #enabled'
481 def getUrl = "$ncmpBasePathV1/ch/testCmHandle/data/ds/ncmp-datastore:operational" +
482 "?resourceIdentifier=parent/child&include-descendants=${enabled}"
483 when: 'get data resource request is performed'
484 def response = mvc.perform(
486 .contentType(MediaType.APPLICATION_JSON)
487 ).andReturn().response
488 then: 'the NCMP data service is called with getResourceDataOperational with #descendantsOption'
489 1 * mockNetworkCmProxyDataService.getResourceDataOperational('testCmHandle',
492 and: 'response status is Ok'
493 response.status == HttpStatus.OK.value()
494 where: 'the following parameters are used'
495 enabled | descendantsOption
496 false | FetchDescendantsOption.OMIT_DESCENDANTS
497 true | FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS
500 def 'Attempt execute #operation rest operation on resource data with #scenario'() {
501 given: 'resource data url'
502 def url = "$ncmpBasePathV1/ch/testCmHandle/data/ds/${datastoreInUrl}?resourceIdentifier=parent/child"
503 when: 'selected request for data resource is performed on url'
504 def response = mvc.perform(
505 executeRestOperation(operation, url))
506 .andReturn().response
507 then: 'the response status is as expected'
508 assert response.status == HttpStatus.BAD_REQUEST.value()
509 and: 'the response is as expected'
510 assert response.getContentAsString().contains(datastoreInUrl)
511 where: 'the following parameters are used'
512 scenario | operation | datastoreInUrl
513 'unsupported datastore' | 'POST' | 'ncmp-datastore:operational'
514 'invalid datastore' | 'POST' | 'invalid'
515 'unsupported datastore' | 'PUT' | 'ncmp-datastore:operational'
516 'invalid datastore' | 'PUT' | 'invalid'
517 'unsupported datastore' | 'PATCH' | 'ncmp-datastore:operational'
518 'invalid datastore' | 'PATCH' | 'invalid'
519 'unsupported datastore' | 'DELETE' | 'ncmp-datastore:operational'
520 'invalid datastore' | 'DELETE' | 'invalid'
523 def executeRestOperation(operation, url) {
524 if (operation == 'POST') {
525 return post(url).contentType(MediaType.APPLICATION_JSON_VALUE).content(requestBody)
527 if (operation == 'PUT') {
528 return put(url).contentType(MediaType.APPLICATION_JSON_VALUE).content(requestBody)
530 if (operation == 'PATCH') {
531 return patch(url).contentType(MediaType.APPLICATION_JSON_VALUE).content(requestBody)
533 if (operation == 'DELETE') {
534 return delete(url).contentType(MediaType.APPLICATION_JSON_VALUE)
540 .operationalDataStore(Operational.builder()
541 .dataStoreSyncState(DataStoreSyncState.NONE_REQUESTED)
542 .lastSyncTime(formattedDateAndTime.toString()).build()).build()
545 def compositeStateTestObject() {
546 new CompositeState(cmHandleState: CmHandleState.ADVISED,
547 lockReason: CompositeState.LockReason.builder().lockReasonCategory(LockReasonCategory.LOCKED_MODULE_SYNC_FAILED).details("lock details").build(),
548 lastUpdateTime: formattedDateAndTime.toString(),
549 dataSyncEnabled: false,
550 dataStores: dataStores())
553 def assertContainsAll(response, assertContent) {
554 assertContent.forEach(string -> { assert (response.contentAsString.contains(string)) })
558 def assertContainsState(response) {
559 def expectedContent = [
561 '"cmHandleState":"ADVISED"',
562 '"reason":"LOCKED_MISBEHAVING"',
563 '"details":"lock details"',
564 '"lastUpdateTime":"2022-12-31T20:30:40.000+0000"',
565 '"dataSyncEnabled":false',
568 '"syncState":"NONE_REQUESTED"',
569 '"lastSyncTime":"2022-12-31T20:30:40.000+0000"',
572 return assertContainsAll(response, expectedContent)
575 def assertContainsPublicProperties(response) {
576 def expectedContent = [
577 '"publicCmHandleProperties":',
579 '"some public property"'
581 return assertContainsAll(response, expectedContent)