Merge "Implement merging all ncmp datastore endpoints into one"
[cps.git] / cps-ncmp-rest / src / test / groovy / org / onap / cps / ncmp / rest / controller / NetworkCmProxyControllerSpec.groovy
1 /*
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
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.rest.controller
25
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 spock.lang.Shared
55 import spock.lang.Specification
56
57 import java.time.OffsetDateTime
58 import java.time.ZoneOffset
59 import java.time.format.DateTimeFormatter
60
61 import static org.onap.cps.ncmp.api.inventory.CompositeState.DataStores
62 import static org.onap.cps.ncmp.api.inventory.CompositeState.Operational
63 import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.CREATE
64 import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.UPDATE
65 import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.PATCH
66 import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.DELETE
67 import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get
68 import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put
69 import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post
70 import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch
71 import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete
72
73 @WebMvcTest(NetworkCmProxyController)
74 class NetworkCmProxyControllerSpec extends Specification {
75
76     public static final int TIMEOUT_IN_MS = 2000
77     public static final boolean NOTIFICATION_ENABLED = true
78
79     @Autowired
80     MockMvc mvc
81
82     @SpringBean
83     NetworkCmProxyDataService mockNetworkCmProxyDataService = Mock()
84
85     @SpringBean
86     ObjectMapper objectMapper = new ObjectMapper()
87
88     @SpringBean
89     JsonObjectMapper jsonObjectMapper = new JsonObjectMapper(objectMapper)
90
91     @SpringBean
92     NcmpRestInputMapper ncmpRestInputMapper = Mappers.getMapper(NcmpRestInputMapper)
93
94     @SpringBean
95     CmHandleStateMapper cmHandleStateMapper = Mappers.getMapper(CmHandleStateMapper)
96
97     @SpringBean
98     CpsNcmpTaskExecutor spiedCpsTaskExecutor = Spy()
99
100     @SpringBean
101     DeprecationHelper stubbedDeprecationHelper = Stub()
102
103     @SpringBean
104     NcmpDatastoreResourceRequestHandlerFactory stubbedNcmpDatastoreResourceRequestHandlerFactory = Stub()
105
106     @Value('${rest.api.ncmp-base-path}/v1')
107     def ncmpBasePathV1
108
109     def requestBody = '{"some-key":"some-value"}'
110
111     @Shared
112     def NO_TOPIC = null
113     def NO_REQUEST_ID = null
114
115     def formattedDateAndTime = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ")
116         .format(OffsetDateTime.of(2022, 12, 31, 20, 30, 40, 1, ZoneOffset.UTC))
117
118     void setup() {
119         stubbedNcmpDatastoreResourceRequestHandlerFactory.getNcmpDatastoreResourceRequestHandler(
120             DatastoreType.OPERATIONAL) >>
121             new NcmpDatastoreOperationalResourceRequestHandler(
122                 mockNetworkCmProxyDataService, spiedCpsTaskExecutor, TIMEOUT_IN_MS, NOTIFICATION_ENABLED)
123
124         stubbedNcmpDatastoreResourceRequestHandlerFactory.getNcmpDatastoreResourceRequestHandler(
125             DatastoreType.PASSTHROUGH_OPERATIONAL) >>
126             new NcmpDatastorePassthroughOperationalResourceRequestHandler(
127                 mockNetworkCmProxyDataService, spiedCpsTaskExecutor, TIMEOUT_IN_MS, NOTIFICATION_ENABLED)
128
129         stubbedNcmpDatastoreResourceRequestHandlerFactory.getNcmpDatastoreResourceRequestHandler(
130             DatastoreType.PASSTHROUGH_RUNNING) >>
131             new NcmpDatastorePassthroughRunningResourceRequestHandler(
132                 mockNetworkCmProxyDataService, spiedCpsTaskExecutor, TIMEOUT_IN_MS, NOTIFICATION_ENABLED)
133     }
134
135     def 'Get Resource Data from pass-through operational.'() {
136         given: 'resource data url'
137             def getUrl = "$ncmpBasePathV1/ch/testCmHandle/data/ds/ncmp-datastore:passthrough-operational" +
138                 "?resourceIdentifier=parent/child&options=(a=1,b=2)"
139         when: 'get data resource request is performed'
140             def response = mvc.perform(
141                 get(getUrl)
142                     .contentType(MediaType.APPLICATION_JSON)
143             ).andReturn().response
144         then: 'the NCMP data service is called with getResourceDataOperationalForCmHandle'
145             1 * mockNetworkCmProxyDataService.getResourceDataOperationalForCmHandle('testCmHandle',
146                 'parent/child',
147                 '(a=1,b=2)',
148                 NO_TOPIC,
149                 NO_REQUEST_ID)
150         and: 'response status is Ok'
151             response.status == HttpStatus.OK.value()
152     }
153
154     def 'Get Resource Data from #datastoreInUrl with #scenario.'() {
155         given: 'resource data url'
156             def getUrl = "$ncmpBasePathV1/ch/testCmHandle/data/ds/ncmp-datastore:${datastoreInUrl}" +
157                 "?resourceIdentifier=parent/child&options=(a=1,b=2)${topicQueryParam}"
158         when: 'get data resource request is performed'
159             def response = mvc.perform(
160                 get(getUrl).contentType(MediaType.APPLICATION_JSON)).andReturn().response
161         then: 'task executor is called appropriate number of times'
162             expectedNumberOfExecutorExecutions * spiedCpsTaskExecutor.executeTask(_, TIMEOUT_IN_MS)
163         and: 'response status is expected'
164             response.status == HttpStatus.OK.value()
165         where: 'the following parameters are used'
166             scenario               | datastoreInUrl            | topicQueryParam        || expectedTopicName | expectedNumberOfExecutorExecutions
167             'url with valid topic' | 'passthrough-operational' | '&topic=my-topic-name' || 'my-topic-name'   | 1
168             'no topic in url'      | 'passthrough-operational' | ''                     || NO_TOPIC          | 0
169             'null topic in url'    | 'passthrough-operational' | '&topic=null'          || 'null'            | 1
170             'url with valid topic' | 'passthrough-running'     | '&topic=my-topic-name' || 'my-topic-name'   | 1
171             'no topic in url'      | 'passthrough-running'     | ''                     || NO_TOPIC          | 0
172             'null topic in url'    | 'passthrough-running'     | '&topic=null'          || 'null'            | 1
173     }
174
175     def 'Fail to get Resource Data from #datastoreInUrl when #scenario.'() {
176         given: 'resource data url'
177             def getUrl = "$ncmpBasePathV1/ch/testCmHandle/data/ds/ncmp-datastore:${datastoreInUrl}" +
178                 "?resourceIdentifier=parent/child&options=(a=1,b=2)${topicQueryParam}"
179         when: 'get data resource request is performed'
180             def response = mvc.perform(
181                 get(getUrl).contentType(MediaType.APPLICATION_JSON)).andReturn().response
182         then: 'abad request is returned'
183             response.status == HttpStatus.BAD_REQUEST.value()
184         where: 'the following parameters are used'
185             scenario                               | datastoreInUrl            | topicQueryParam
186             'empty topic in url'                   | 'passthrough-operational' | '&topic=\"\"'
187             'missing topic in url'                 | 'passthrough-operational' | '&topic='
188             'blank topic value in url'             | 'passthrough-operational' | '&topic=\" \"'
189             'invalid non-empty topic value in url' | 'passthrough-operational' | '&topic=1_5_*_#'
190             'empty topic in url'                   | 'passthrough-running'     | '&topic=\"\"'
191             'missing topic in url'                 | 'passthrough-running'     | '&topic='
192             'blank topic value in url'             | 'passthrough-running'     | '&topic=\" \"'
193             'invalid non-empty topic value in url' | 'passthrough-running'     | '&topic=1_5_*_#'
194     }
195
196     def 'Get Resource Data from pass-through running with #scenario value in resource identifier param.'() {
197         given: 'resource data url'
198             def getUrl = "$ncmpBasePathV1/ch/testCmHandle/data/ds/ncmp-datastore:passthrough-running" +
199                 "?resourceIdentifier=" + resourceIdentifier + "&options=(a=1,b=2)"
200         and: 'ncmp service returns json object'
201             mockNetworkCmProxyDataService.getResourceDataPassThroughRunningForCmHandle('testCmHandle',
202                 resourceIdentifier,
203                 '(a=1,b=2)',
204                 NO_TOPIC,
205                 NO_REQUEST_ID) >> '{valid-json}'
206         when: 'get data resource request is performed'
207             def response = mvc.perform(
208                 get(getUrl)
209                     .contentType(MediaType.APPLICATION_JSON)
210             ).andReturn().response
211         then: 'response status is Ok'
212             response.status == HttpStatus.OK.value()
213         and: 'response contains valid object body'
214             response.getContentAsString() == '{valid-json}'
215         where: 'tokens are used in the resource identifier parameter'
216             scenario                       | resourceIdentifier
217             '/'                            | 'id/with/slashes'
218             '?'                            | 'idWith?'
219             ','                            | 'idWith,'
220             '='                            | 'idWith='
221             '[]'                           | 'idWith[]'
222             '? needs to be encoded as %3F' | 'idWith%3F'
223     }
224
225     def 'Update resource data from pass-through running.'() {
226         given: 'update resource data url'
227             def updateUrl = "$ncmpBasePathV1/ch/testCmHandle/data/ds/ncmp-datastore:passthrough-running" +
228                 "?resourceIdentifier=parent/child"
229         when: 'update data resource request is performed'
230             def response = mvc.perform(
231                 put(updateUrl)
232                     .contentType(MediaType.APPLICATION_JSON_VALUE).content(requestBody)
233             ).andReturn().response
234         then: 'ncmp service method to update resource is called'
235             1 * mockNetworkCmProxyDataService.writeResourceDataPassThroughRunningForCmHandle('testCmHandle',
236                 'parent/child', UPDATE, requestBody, 'application/json;charset=UTF-8')
237         and: 'the response status is OK'
238             response.status == HttpStatus.OK.value()
239     }
240
241     def 'Create Resource Data from pass-through running with #scenario.'() {
242         given: 'resource data url'
243             def url = "$ncmpBasePathV1/ch/testCmHandle/data/ds/ncmp-datastore:passthrough-running" +
244                 "?resourceIdentifier=parent/child"
245             def requestBody = '{"some-key":"some-value"}'
246         when: 'create resource request is performed'
247             def response = mvc.perform(
248                 post(url)
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()
256     }
257
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()
270     }
271
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}]'
292     }
293
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")
316     }
317
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)
333     }
334
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)
350     }
351
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}]'
371     }
372
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"]'
385     }
386
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
397     }
398
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(
405                 patch(url)
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()
414     }
415
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(
422                 delete(url)
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()
429     }
430
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(
437                 get(getUrl)
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'
447     }
448
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()
462     }
463
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
475             'enabled'  | true
476             'disabled' | false
477     }
478
479     def 'Get Resource Data from operational without descendants.'() {
480         given: 'resource data url'
481             def getUrl = "$ncmpBasePathV1/ch/testCmHandle/data/ds/ncmp-datastore:operational" +
482                 "?resourceIdentifier=parent/child&include-descendants=false"
483         when: 'get data resource request is performed'
484             def response = mvc.perform(
485                 get(getUrl)
486                     .contentType(MediaType.APPLICATION_JSON)
487             ).andReturn().response
488         then: 'the NCMP data service is called with getResourceDataOperational'
489             1 * mockNetworkCmProxyDataService.getResourceDataOperational('testCmHandle',
490                 'parent/child',
491                 FetchDescendantsOption.OMIT_DESCENDANTS)
492         and: 'response status is Ok'
493             response.status == HttpStatus.OK.value()
494     }
495
496     def 'Get Resource Data from operational including descendants.'() {
497         given: 'resource data url'
498             def getUrl = "$ncmpBasePathV1/ch/testCmHandle/data/ds/ncmp-datastore:operational" +
499                 "?resourceIdentifier=parent/child&include-descendants=true"
500         when: 'get data resource request is performed'
501             def response = mvc.perform(
502                 get(getUrl)
503                     .contentType(MediaType.APPLICATION_JSON)
504             ).andReturn().response
505         then: 'the NCMP data service is called with getResourceDataOperational'
506             1 * mockNetworkCmProxyDataService.getResourceDataOperational('testCmHandle',
507                 'parent/child',
508                 FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS)
509         and: 'response status is Ok'
510             response.status == HttpStatus.OK.value()
511     }
512
513     def dataStores() {
514         DataStores.builder()
515             .operationalDataStore(Operational.builder()
516                 .dataStoreSyncState(DataStoreSyncState.NONE_REQUESTED)
517                 .lastSyncTime(formattedDateAndTime.toString()).build()).build()
518     }
519
520     def compositeStateTestObject() {
521         new CompositeState(cmHandleState: CmHandleState.ADVISED,
522             lockReason: CompositeState.LockReason.builder().lockReasonCategory(LockReasonCategory.LOCKED_MODULE_SYNC_FAILED).details("lock details").build(),
523             lastUpdateTime: formattedDateAndTime.toString(),
524             dataSyncEnabled: false,
525             dataStores: dataStores())
526     }
527
528     def assertContainsAll(response, assertContent) {
529         assertContent.forEach(string -> { assert (response.contentAsString.contains(string)) })
530         return void
531     }
532
533     def assertContainsState(response) {
534         def expectedContent = [
535             '"state":',
536             '"cmHandleState":"ADVISED"',
537             '"reason":"LOCKED_MISBEHAVING"',
538             '"details":"lock details"',
539             '"lastUpdateTime":"2022-12-31T20:30:40.000+0000"',
540             '"dataSyncEnabled":false',
541             '"dataSyncState":',
542             '"operational":',
543             '"syncState":"NONE_REQUESTED"',
544             '"lastSyncTime":"2022-12-31T20:30:40.000+0000"',
545             '"running":null'
546         ]
547         return assertContainsAll(response, expectedContent)
548     }
549
550     def assertContainsPublicProperties(response) {
551         def expectedContent = [
552             '"publicCmHandleProperties":',
553             '"public prop"',
554             '"some public property"'
555         ]
556         return assertContainsAll(response, expectedContent)
557     }
558
559 }
560