Merge "CM SUBSCRIPTION: add new subscription for non existing xpath"
[cps.git] / cps-rest / src / test / groovy / org / onap / cps / rest / controller / QueryRestControllerSpec.groovy
1 /*
2  *  ============LICENSE_START=======================================================
3  *  Copyright (C) 2021-2022 Nordix Foundation
4  *  Modifications Copyright (C) 2021-2022 Bell Canada.
5  *  Modifications Copyright (C) 2021 Pantheon.tech
6  *  Modifications Copyright (C) 2022-2023 TechMahindra Ltd.
7  *  ================================================================================
8  *  Licensed under the Apache License, Version 2.0 (the "License");
9  *  you may not use this file except in compliance with the License.
10  *  You may obtain a copy of the License at
11  *
12  *        http://www.apache.org/licenses/LICENSE-2.0
13  *
14  *  Unless required by applicable law or agreed to in writing, software
15  *  distributed under the License is distributed on an "AS IS" BASIS,
16  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  *  See the License for the specific language governing permissions and
18  *  limitations under the License.
19  *
20  *  SPDX-License-Identifier: Apache-2.0
21  *  ============LICENSE_END=========================================================
22  */
23
24 package org.onap.cps.rest.controller
25
26 import org.onap.cps.spi.PaginationOption
27 import org.onap.cps.utils.PrefixResolver
28
29 import static org.onap.cps.spi.FetchDescendantsOption.DIRECT_CHILDREN_ONLY
30 import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS
31 import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS
32 import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get
33
34 import com.fasterxml.jackson.databind.ObjectMapper
35 import org.onap.cps.utils.JsonObjectMapper
36 import org.onap.cps.api.CpsQueryService
37 import org.onap.cps.spi.model.DataNodeBuilder
38 import org.spockframework.spring.SpringBean
39 import org.springframework.beans.factory.annotation.Autowired
40 import org.springframework.beans.factory.annotation.Value
41 import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
42 import org.springframework.http.HttpStatus
43 import org.springframework.test.web.servlet.MockMvc
44 import spock.lang.Specification
45
46 @WebMvcTest(QueryRestController)
47 class QueryRestControllerSpec extends Specification {
48
49     @SpringBean
50     CpsQueryService mockCpsQueryService = Mock()
51
52     @SpringBean
53     JsonObjectMapper jsonObjectMapper = new JsonObjectMapper(new ObjectMapper())
54
55     @SpringBean
56     PrefixResolver prefixResolver = Mock()
57
58     @Autowired
59     MockMvc mvc
60
61     @Value('${rest.api.cps-base-path}')
62     def basePath
63
64     def dataspaceName = 'my_dataspace'
65     def anchorName = 'my_anchor'
66     def cpsPath = 'some cps-path'
67     def dataNodeEndpointV2
68
69     def setup() {
70          dataNodeEndpointV2 = "$basePath/v2/dataspaces/$dataspaceName/anchors/$anchorName/nodes/query"
71     }
72
73     def 'Query data node by cps path for the given dataspace and anchor with #scenario.'() {
74         given: 'service method returns a list containing a data node'
75             def dataNode1 = new DataNodeBuilder().withXpath('/xpath')
76                     .withLeaves([leaf: 'value', leafList: ['leaveListElement1', 'leaveListElement2']]).build()
77             mockCpsQueryService.queryDataNodes(dataspaceName, anchorName, cpsPath, expectedCpsDataServiceOption) >> [dataNode1, dataNode1]
78         and: 'the query endpoint'
79             def dataNodeEndpoint = "$basePath/v1/dataspaces/$dataspaceName/anchors/$anchorName/nodes/query"
80         when: 'query data nodes API is invoked'
81             def response =
82                     mvc.perform(
83                             get(dataNodeEndpoint)
84                                     .param('cps-path', cpsPath)
85                                     .param('include-descendants', includeDescendantsOption))
86                             .andReturn().response
87         then: 'the response contains the the datanode in json format'
88             response.status == HttpStatus.OK.value()
89             response.getContentAsString().contains('{"xpath":{"leaf":"value","leafList":["leaveListElement1","leaveListElement2"]}}')
90         where: 'the following options for include descendants are provided in the request'
91             scenario                    | includeDescendantsOption || expectedCpsDataServiceOption
92             'no descendants by default' | ''                       || OMIT_DESCENDANTS
93             'no descendant explicitly'  | 'false'                  || OMIT_DESCENDANTS
94             'descendants'               | 'true'                   || INCLUDE_ALL_DESCENDANTS
95     }
96
97    def 'Query data node v2 api by cps path for the given dataspace and anchor with #scenario.'() {
98         given: 'service method returns a list containing a data node'
99             def dataNode1 = new DataNodeBuilder().withXpath('/xpath')
100                 .withLeaves([leaf: 'value', leafList: ['leaveListElement1', 'leaveListElement2']]).build()
101             mockCpsQueryService.queryDataNodes(dataspaceName, anchorName, cpsPath, { descendantsOption -> {
102                 assert descendantsOption.depth == expectedDepth}}) >> [dataNode1, dataNode1]
103         when: 'query data nodes API is invoked'
104             def response =
105                 mvc.perform(
106                         get(dataNodeEndpointV2)
107                                 .param('cps-path', cpsPath)
108                                 .param('descendants', includeDescendantsOptionString))
109                         .andReturn().response
110         then: 'the response contains the the datanode in json format'
111             assert response.status == HttpStatus.OK.value()
112             assert response.getContentAsString().contains('{"xpath":{"leaf":"value","leafList":["leaveListElement1","leaveListElement2"]}}')
113        where: 'the following options for include descendants are provided in the request'
114            scenario          | includeDescendantsOptionString || expectedDepth
115            'direct children' | 'direct'                       || 1
116            'descendants'     | '2'                            || 2
117     }
118
119     def 'Query data node by cps path for the given dataspace across all anchors with #scenario.'() {
120         given: 'service method returns a list containing a data node from different anchors'
121             def dataNode1 = new DataNodeBuilder().withXpath('/xpath')
122                 .withAnchor('my_anchor')
123                 .withLeaves([leaf: 'value', leafList: ['leaveListElement1', 'leaveListElement2']]).build()
124             def dataNode2 = new DataNodeBuilder().withXpath('/xpath')
125                 .withAnchor('my_anchor_2')
126                 .withLeaves([leaf: 'value', leafList: ['leaveListElement3', 'leaveListElement4']]).build()
127         and: 'second data node for the same anchor'
128             def dataNode3 = new DataNodeBuilder().withXpath('/xpath')
129                 .withAnchor('my_anchor_2')
130                 .withLeaves([leaf: 'value', leafList: ['leaveListElement5', 'leaveListElement6']]).build()
131         and: 'the query endpoint'
132             def dataspaceName = 'my_dataspace'
133             def cpsPath = 'some/cps/path'
134             def dataNodeEndpoint = "$basePath/v2/dataspaces/$dataspaceName/nodes/query"
135             mockCpsQueryService.queryDataNodesAcrossAnchors(dataspaceName, cpsPath,
136                 expectedCpsDataServiceOption, PaginationOption.NO_PAGINATION) >> [dataNode1, dataNode2, dataNode3]
137             mockCpsQueryService.countAnchorsForDataspaceAndCpsPath(dataspaceName, cpsPath) >> 2
138         when: 'query data nodes API is invoked'
139             def response =
140                 mvc.perform(
141                         get(dataNodeEndpoint)
142                                 .param('cps-path', cpsPath)
143                                 .param('descendants', includeDescendantsOptionString))
144                         .andReturn().response
145         then: 'the response contains the the datanode in json format'
146             response.status == HttpStatus.OK.value()
147             response.getContentAsString().contains('{"xpath":{"leaf":"value","leafList":["leaveListElement1","leaveListElement2"]}}')
148             response.getContentAsString().contains('{"xpath":{"leaf":"value","leafList":["leaveListElement3","leaveListElement4"]}}')
149             response.getContentAsString().contains('{"xpath":{"leaf":"value","leafList":["leaveListElement5","leaveListElement6"]}}')
150         where: 'the following options for include descendants are provided in the request'
151             scenario                    | includeDescendantsOptionString || expectedCpsDataServiceOption
152             'no descendants by default' | ''                             || OMIT_DESCENDANTS
153             'no descendant explicitly'  | 'none'                         || OMIT_DESCENDANTS
154             'descendants'               | 'all'                          || INCLUDE_ALL_DESCENDANTS
155             'direct children'           | 'direct'                       || DIRECT_CHILDREN_ONLY
156     }
157
158     def 'Query data node by cps path for the given dataspace across all anchors with pagination #scenario.'() {
159         given: 'service method returns a list containing a data node from different anchors'
160         def dataNode1 = new DataNodeBuilder().withXpath('/xpath')
161                 .withAnchor('my_anchor')
162                 .withLeaves([leaf: 'value', leafList: ['leaveListElement1', 'leaveListElement2']]).build()
163         def dataNode2 = new DataNodeBuilder().withXpath('/xpath')
164                 .withAnchor('my_anchor_2')
165                 .withLeaves([leaf: 'value', leafList: ['leaveListElement3', 'leaveListElement4']]).build()
166         and: 'the query endpoint'
167             def dataspaceName = 'my_dataspace'
168             def cpsPath = 'some/cps/path'
169             def dataNodeEndpoint = "$basePath/v2/dataspaces/$dataspaceName/nodes/query"
170             mockCpsQueryService.queryDataNodesAcrossAnchors(dataspaceName, cpsPath,
171                 INCLUDE_ALL_DESCENDANTS, new PaginationOption(pageIndex,pageSize)) >> [dataNode1, dataNode2]
172             mockCpsQueryService.countAnchorsForDataspaceAndCpsPath(dataspaceName, cpsPath) >> totalAnchors
173         when: 'query data nodes API is invoked'
174             def response =
175                 mvc.perform(
176                         get(dataNodeEndpoint)
177                                 .param('cps-path', cpsPath)
178                                 .param('descendants', "all")
179                                 .param('pageIndex', String.valueOf(pageIndex))
180                                 .param('pageSize', String.valueOf(pageSize)))
181                         .andReturn().response
182         then: 'the response contains the the datanode in json format'
183             assert response.status == HttpStatus.OK.value()
184             assert Integer.valueOf(response.getHeaderValue("total-pages")) == expectedTotalPageSize
185             assert response.getContentAsString().contains('{"xpath":{"leaf":"value","leafList":["leaveListElement1","leaveListElement2"]}}')
186             assert response.getContentAsString().contains('{"xpath":{"leaf":"value","leafList":["leaveListElement3","leaveListElement4"]}}')
187         where: 'the following options for include descendants are provided in the request'
188             scenario                     | pageIndex | pageSize | totalAnchors || expectedTotalPageSize
189             '1st page with all anchors'  | 1         | 3        | 3            || 1
190             '1st page with less anchors' | 1         | 2        | 3            || 2
191     }
192
193     def 'Query data node across all anchors with pagination option with #scenario.'() {
194         given: 'service method returns a list containing a data node from different anchors'
195         def dataNode1 = new DataNodeBuilder().withXpath('/xpath')
196                 .withAnchor('my_anchor')
197                 .withLeaves([leaf: 'value', leafList: ['leaveListElement1', 'leaveListElement2']]).build()
198         def dataNode2 = new DataNodeBuilder().withXpath('/xpath')
199                 .withAnchor('my_anchor_2')
200                 .withLeaves([leaf: 'value', leafList: ['leaveListElement3', 'leaveListElement4']]).build()
201         and: 'the query endpoint'
202             def dataspaceName = 'my_dataspace'
203             def cpsPath = 'some/cps/path'
204             def dataNodeEndpoint = "$basePath/v2/dataspaces/$dataspaceName/nodes/query"
205             mockCpsQueryService.queryDataNodesAcrossAnchors(dataspaceName, cpsPath,
206                 INCLUDE_ALL_DESCENDANTS, PaginationOption.NO_PAGINATION) >> [dataNode1, dataNode2]
207             mockCpsQueryService.countAnchorsForDataspaceAndCpsPath(dataspaceName, cpsPath) >> 2
208         when: 'query data nodes API is invoked'
209             def response =
210                 mvc.perform(
211                         get(dataNodeEndpoint)
212                                 .param('cps-path', cpsPath)
213                                 .param('descendants', "all")
214                                 .param(parameterName, "1"))
215                         .andReturn().response
216         then: 'the response contains the the datanode in json format'
217             assert response.status == HttpStatus.OK.value()
218             assert Integer.valueOf(response.getHeaderValue("total-pages")) == 1
219             assert response.getContentAsString().contains('{"xpath":{"leaf":"value","leafList":["leaveListElement1","leaveListElement2"]}}')
220             assert response.getContentAsString().contains('{"xpath":{"leaf":"value","leafList":["leaveListElement3","leaveListElement4"]}}')
221         where:
222             scenario           | parameterName
223             'only page size'   | 'pageSize'
224             'only page index'  | 'pageIndex'
225     }
226 }