2 * ============LICENSE_START=======================================================
3 * Copyright (C) 2021 Pantheon.tech
4 * Modification Copyright (C) 2021 highstreet technologies GmbH
5 * Modification Copyright (C) 2021 Nordix Foundation
6 * Modification Copyright (C) 2021 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
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
19 * SPDX-License-Identifier: Apache-2.0
20 * ============LICENSE_END=========================================================
23 package org.onap.cps.ncmp.rest.controller
25 import org.onap.cps.TestUtils
26 import org.onap.cps.spi.model.ModuleReference
28 import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS
29 import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS
30 import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get
31 import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch
32 import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post
33 import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put
34 import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.CREATE
35 import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.UPDATE
37 import com.google.gson.Gson
38 import org.onap.cps.ncmp.api.NetworkCmProxyDataService
39 import org.onap.cps.spi.model.DataNodeBuilder
40 import org.spockframework.spring.SpringBean
41 import org.springframework.beans.factory.annotation.Autowired
42 import org.springframework.beans.factory.annotation.Value
43 import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
44 import org.springframework.http.HttpStatus
45 import org.springframework.http.MediaType
46 import org.springframework.test.web.servlet.MockMvc
47 import spock.lang.Specification
49 @WebMvcTest(NetworkCmProxyController)
50 class NetworkCmProxyControllerSpec extends Specification {
56 NetworkCmProxyDataService mockNetworkCmProxyDataService = Mock()
58 @Value('${rest.api.ncmp-base-path}/v1')
61 def cmHandle = 'some handle'
62 def xpath = 'some xpath'
64 def 'Query data node by cps path for the given cm handle with #scenario.'() {
65 given: 'service method returns a list containing a data node'
66 def dataNode = new DataNodeBuilder().withXpath('/xpath').build()
67 def cpsPath = 'some cps-path'
68 mockNetworkCmProxyDataService.queryDataNodes(cmHandle, cpsPath, expectedCpsDataServiceOption) >> [dataNode]
69 and: 'the query endpoint'
70 def dataNodeEndpoint = "$ncmpBasePathV1/cm-handles/$cmHandle/nodes/query"
71 when: 'query data nodes API is invoked'
72 def response = mvc.perform(get(dataNodeEndpoint)
73 .param('cps-path', cpsPath)
74 .param('include-descendants', includeDescendantsOption))
76 then: 'the response contains the the datanode in json format'
77 response.status == HttpStatus.OK.value()
78 def expectedJsonContent = new Gson().toJson(dataNode)
79 response.getContentAsString().contains(expectedJsonContent)
80 where: 'the following options for include descendants are provided in the request'
81 scenario | includeDescendantsOption || expectedCpsDataServiceOption
82 'no descendants by default' | '' || OMIT_DESCENDANTS
83 'no descendant explicitly' | 'false' || OMIT_DESCENDANTS
84 'descendants' | 'true' || INCLUDE_ALL_DESCENDANTS
87 def 'Create data node: #scenario.'() {
89 def jsonData = 'json data'
90 when: 'post request is performed'
91 def response = mvc.perform(
92 post("$ncmpBasePathV1/cm-handles/$cmHandle/nodes")
93 .contentType(MediaType.APPLICATION_JSON)
95 .param('xpath', reqXpath)
96 ).andReturn().response
97 then: 'the service method is invoked once with expected parameters'
98 1 * mockNetworkCmProxyDataService.createDataNode(cmHandle, usedXpath, jsonData)
99 and: 'response status indicates success'
100 response.status == HttpStatus.CREATED.value()
101 where: 'following parameters were used'
102 scenario | reqXpath || usedXpath
103 'no xpath parameter' | '' || '/'
104 'root xpath' | '/' || '/'
105 'parent node xpath' | '/xpath' || '/xpath'
108 def 'Add list-node elements.'() {
109 given: 'json data and parent node xpath'
110 def jsonData = 'json data'
111 def parentNodeXpath = 'parent node xpath'
112 when: 'post request is performed'
113 def response = mvc.perform(
114 post("$ncmpBasePathV1/cm-handles/$cmHandle/list-node")
115 .contentType(MediaType.APPLICATION_JSON)
117 .param('xpath', parentNodeXpath)
118 ).andReturn().response
119 then: 'the service method is invoked once with expected parameters'
120 1 * mockNetworkCmProxyDataService.addListNodeElements(cmHandle, parentNodeXpath, jsonData)
121 and: 'response status indicates success'
122 response.status == HttpStatus.CREATED.value()
125 def 'Update data node leaves.'() {
127 def jsonData = 'json data'
128 and: 'the query endpoint'
129 def endpoint = "$ncmpBasePathV1/cm-handles/$cmHandle/nodes"
130 when: 'patch request is performed'
131 def response = mvc.perform(
133 .contentType(MediaType.APPLICATION_JSON)
135 .param('xpath', xpath)
136 ).andReturn().response
137 then: 'the service method is invoked once with expected parameters'
138 1 * mockNetworkCmProxyDataService.updateNodeLeaves(cmHandle, xpath, jsonData)
139 and: 'response status indicates success'
140 response.status == HttpStatus.OK.value()
143 def 'Replace data node tree.'() {
145 def jsonData = 'json data'
146 and: 'the query endpoint'
147 def endpoint = "$ncmpBasePathV1/cm-handles/$cmHandle/nodes"
148 when: 'put request is performed'
149 def response = mvc.perform(
151 .contentType(MediaType.APPLICATION_JSON)
153 .param('xpath', xpath)
154 ).andReturn().response
155 then: 'the service method is invoked once with expected parameters'
156 1 * mockNetworkCmProxyDataService.replaceNodeTree(cmHandle, xpath, jsonData)
157 and: 'response status indicates success'
158 response.status == HttpStatus.OK.value()
161 def 'Get data node.'() {
162 given: 'the service returns a data node'
163 def xpath = 'some xpath'
164 def dataNode = new DataNodeBuilder().withXpath(xpath).withLeaves(["leaf": "value"]).build()
165 mockNetworkCmProxyDataService.getDataNode(cmHandle, xpath, OMIT_DESCENDANTS) >> dataNode
166 and: 'the query endpoint'
167 def endpoint = "$ncmpBasePathV1/cm-handles/$cmHandle/node"
168 when: 'get request is performed through REST API'
169 def response = mvc.perform(get(endpoint).param('xpath', xpath)).andReturn().response
170 then: 'a success response is returned'
171 response.status == HttpStatus.OK.value()
172 and: 'response contains expected leaf and value'
173 response.contentAsString.contains('"leaf":"value"')
176 def 'Get Resource Data from passthrough operational.' () {
177 given: 'resource data url'
178 def getUrl = "$ncmpBasePathV1/ch/testCmHandle/data/ds/ncmp-datastore:passthrough-operational" +
179 "?resourceIdentifier=parent/child&options=(a=1,b=2)"
180 when: 'get data resource request is performed'
181 def response = mvc.perform(
183 .contentType(MediaType.APPLICATION_JSON)
184 .accept(MediaType.APPLICATION_JSON_VALUE)
185 ).andReturn().response
186 then: 'the NCMP data service is called with getResourceDataOperationalForCmHandle'
187 1 * mockNetworkCmProxyDataService.getResourceDataOperationalForCmHandle('testCmHandle',
191 and: 'response status is Ok'
192 response.status == HttpStatus.OK.value()
195 def 'Get Resource Data from passthrough running with #scenario value in resource identifier param.' () {
196 given: 'resource data url'
197 def getUrl = "$ncmpBasePathV1/ch/testCmHandle/data/ds/ncmp-datastore:passthrough-running" +
198 "?resourceIdentifier=" + resourceIdentifier + "&options=(a=1,b=2)"
199 and: 'ncmp service returns json object'
200 mockNetworkCmProxyDataService.getResourceDataPassThroughRunningForCmHandle('testCmHandle',
203 '(a=1,b=2)') >> '{valid-json}'
204 when: 'get data resource request is performed'
205 def response = mvc.perform(
207 .contentType(MediaType.APPLICATION_JSON)
208 .accept(MediaType.APPLICATION_JSON_VALUE)
209 ).andReturn().response
210 then: 'response status is Ok'
211 response.status == HttpStatus.OK.value()
212 and: 'response contains valid object body'
213 response.getContentAsString() == '{valid-json}'
214 where: 'tokens are used in the resource identifier parameter'
215 scenario | resourceIdentifier
216 '/' | 'id/with/slashes'
221 '? needs to be encoded as %3F' | 'idWith%3F'
224 def 'Update resource data from passthrough running.' () {
225 given: 'update resource data url'
226 def updateUrl = "$ncmpBasePathV1/ch/testCmHandle/data/ds/ncmp-datastore:passthrough-running" +
227 "?resourceIdentifier=parent/child"
228 when: 'update data resource request is performed'
229 def response = mvc.perform(
231 .contentType(MediaType.APPLICATION_JSON_VALUE)
232 .accept(MediaType.APPLICATION_JSON_VALUE).content('some-request-body')
233 ).andReturn().response
234 then: 'ncmp service method to update resource is called'
235 1 * mockNetworkCmProxyDataService.writeResourceDataPassThroughRunningForCmHandle('testCmHandle',
236 'parent/child', UPDATE,'some-request-body', 'application/json;charset=UTF-8')
237 and: 'the response status is OK'
238 response.status == HttpStatus.OK.value()
241 def 'Create Resource Data from passthrough 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 when: 'create resource request is performed'
246 def response = mvc.perform(
248 .contentType(MediaType.APPLICATION_JSON_VALUE)
249 .accept(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 where: 'given request body'
257 scenario | requestBody
258 'body contains " and new line' | 'body with " quote and \n new line'
259 'body contains normal string' | 'normal request body'
262 def 'Get module references for the given dataspace and cm handle.' () {
263 given: 'get module references url'
264 def getUrl = "$ncmpBasePathV1/ch/some-cmhandle/modules"
265 when: 'get module resource request is performed'
266 def response =mvc.perform(get(getUrl)).andReturn().response
267 then: 'ncmp service method to get yang resource module references is called'
268 mockNetworkCmProxyDataService.getYangResourcesModuleReferences('some-cmhandle')
269 >> [new ModuleReference(moduleName: 'some-name1',revision: 'some-revision1')]
270 and: 'response contains an array with the module name and revision'
271 response.getContentAsString() == '[{"moduleName":"some-name1","revision":"some-revision1"}]'
272 and: 'response returns an OK http code'
273 response.status == HttpStatus.OK.value()
276 def 'Retrieve cm handles.'() {
277 given: 'an endpoint and json data'
278 def searchesEndpoint = "$ncmpBasePathV1/ch/searches"
279 String jsonData = TestUtils.getResourceFileContent('cmhandle-search.json')
280 and: 'the service method is invoked with module names and returns two cm handle ids'
281 mockNetworkCmProxyDataService.executeCmHandleHasAllModulesSearch(['module1', 'module2']) >> ['some-cmhandle-id1', 'some-cmhandle-id2']
282 when: 'the searches api is invoked'
283 def response = mvc.perform(post(searchesEndpoint)
284 .contentType(MediaType.APPLICATION_JSON)
285 .content(jsonData)).andReturn().response
286 then: 'response status returns OK'
287 response.status == HttpStatus.OK.value()
288 and: 'the expected response content is returned'
289 response.contentAsString == '{"cmHandles":[{"cmHandleId":"some-cmhandle-id1"},{"cmHandleId":"some-cmhandle-id2"}]}'
292 def 'Call execute cm handle searches with unrecognized condition name.'() {
293 given: 'an endpoint and json data'
294 def searchesEndpoint = "$ncmpBasePathV1/ch/searches"
295 String jsonData = TestUtils.getResourceFileContent('invalid-cmhandle-search.json')
296 when: 'the searches api is invoked'
297 def response = mvc.perform(post(searchesEndpoint)
298 .contentType(MediaType.APPLICATION_JSON)
299 .content(jsonData)).andReturn().response
300 then: 'an empty cm handle identifier is returned'
301 response.contentAsString == '{"cmHandles":[]}'