bf86e13c874f058cf4d86a0663688be444c2f260
[cps.git] / integration-test / src / test / groovy / org / onap / cps / integration / functional / CpsDataServiceIntegrationSpec.groovy
1 /*
2  *  ============LICENSE_START=======================================================
3  *  Copyright (C) 2023 Nordix Foundation
4  *  Modifications Copyright (C) 2023 TechMahindra Ltd.
5  *  ================================================================================
6  *  Licensed under the Apache License, Version 2.0 (the 'License');
7  *  you may not use this file except in compliance with the License.
8  *  You may obtain a copy of the License at
9  *
10  *        http://www.apache.org/licenses/LICENSE-2.0
11  *
12  *  Unless required by applicable law or agreed to in writing, software
13  *  distributed under the License is distributed on an 'AS IS' BASIS,
14  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  *  See the License for the specific language governing permissions and
16  *  limitations under the License.
17  *
18  *  SPDX-License-Identifier: Apache-2.0
19  *  ============LICENSE_END=========================================================
20  */
21
22 package org.onap.cps.integration.functional
23
24 import org.onap.cps.api.CpsDataService
25 import org.onap.cps.integration.base.FunctionalSpecBase
26 import org.onap.cps.spi.FetchDescendantsOption
27 import org.onap.cps.spi.exceptions.AnchorNotFoundException
28 import org.onap.cps.spi.exceptions.DataValidationException
29 import org.onap.cps.spi.exceptions.DataspaceNotFoundException
30
31 import java.time.OffsetDateTime
32
33 import java.time.OffsetDateTime
34
35 class CpsDataServiceIntegrationSpec extends FunctionalSpecBase {
36
37     CpsDataService objectUnderTest
38     def originalCountBookstoreChildNodes
39
40     def setup() {
41         objectUnderTest = cpsDataService
42         originalCountBookstoreChildNodes = countDataNodesInTree(objectUnderTest.getDataNodes(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, '/bookstore', FetchDescendantsOption.DIRECT_CHILDREN_ONLY))
43     }
44
45     def 'Read bookstore top-level container(s) using #fetchDescendantsOption.'() {
46         when: 'get data nodes for bookstore container'
47             def result = objectUnderTest.getDataNodes(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, '/bookstore', fetchDescendantsOption)
48         then: 'the tree consist ouf of #expectNumberOfDataNodes data nodes'
49             assert countDataNodesInTree(result) == expectNumberOfDataNodes
50         and: 'the top level data node has the expected attribute and value'
51             assert result.leaves['bookstore-name'] == ['Easons']
52         and: 'they are from the correct dataspace'
53             assert result.dataspace == [FUNCTIONAL_TEST_DATASPACE_1]
54         and: 'they are from the correct anchor'
55             assert result.anchorName == [BOOKSTORE_ANCHOR_1]
56         where: 'the following option is used'
57             fetchDescendantsOption                         || expectNumberOfDataNodes
58             FetchDescendantsOption.OMIT_DESCENDANTS        || 1
59             FetchDescendantsOption.DIRECT_CHILDREN_ONLY    || 6
60             FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS || 17
61             new FetchDescendantsOption(2)                  || 17
62     }
63
64     def 'Add and Delete a (container) datanode.'() {
65         given: 'new (webinfo) datanode'
66             def json = '{"webinfo": {"domain-name":"ourbookstore.com" ,"contact-email":"info@ourbookstore.com" }}'
67         when: 'the new datanode is saved'
68             objectUnderTest.saveData(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1 , '/bookstore', json, OffsetDateTime.now())
69         then: 'it can be retrieved by its xpath'
70             def result = objectUnderTest.getDataNodes(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, '/bookstore/webinfo', FetchDescendantsOption.DIRECT_CHILDREN_ONLY)
71             assert result.size() == 1
72             assert result[0].xpath == '/bookstore/webinfo'
73         and: 'there is now one extra datanode'
74             assert originalCountBookstoreChildNodes + 1 == countDataNodesInTree(objectUnderTest.getDataNodes(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, '/bookstore', FetchDescendantsOption.DIRECT_CHILDREN_ONLY))
75         when: 'the new datanode is deleted'
76             objectUnderTest.deleteDataNode(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, '/bookstore/webinfo', OffsetDateTime.now())
77         then: 'the original number of datanodes is restored'
78             assert originalCountBookstoreChildNodes == countDataNodesInTree(objectUnderTest.getDataNodes(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, '/bookstore', FetchDescendantsOption.DIRECT_CHILDREN_ONLY))
79     }
80
81     def 'Add and Delete list (element) datanodes.'() {
82         given: 'two new (categories) datanodes'
83             def json = '{"categories": [ {"code":"new1"}, {"code":"new2" } ] }'
84         when: 'the new list elements are saved'
85             objectUnderTest.saveListElements(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1 , '/bookstore', json, OffsetDateTime.now())
86         then: 'they can be retrieved by their xpaths'
87             objectUnderTest.getDataNodes(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, '/bookstore/categories[@code="new1"]', FetchDescendantsOption.DIRECT_CHILDREN_ONLY).size() == 1
88             objectUnderTest.getDataNodes(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, '/bookstore/categories[@code="new2"]', FetchDescendantsOption.DIRECT_CHILDREN_ONLY).size() == 1
89         and: 'there are now two extra datanodes'
90             assert originalCountBookstoreChildNodes + 2 == countDataNodesInTree(objectUnderTest.getDataNodes(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, '/bookstore', FetchDescendantsOption.DIRECT_CHILDREN_ONLY))
91         when: 'the new elements are deleted'
92             objectUnderTest.deleteDataNode(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, '/bookstore/categories[@code="new1"]', OffsetDateTime.now())
93             objectUnderTest.deleteDataNode(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, '/bookstore/categories[@code="new2"]', OffsetDateTime.now())
94         then: 'the original number of datanodes is restored'
95             assert originalCountBookstoreChildNodes == countDataNodesInTree(objectUnderTest.getDataNodes(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, '/bookstore', FetchDescendantsOption.DIRECT_CHILDREN_ONLY))
96     }
97
98     def 'Add and Delete a batch of lists (element) datanodes.'() {
99         given: 'two new (categories) datanodes in two separate batches'
100             def json1 = '{"categories": [ {"code":"new1"} ] }'
101             def json2 = '{"categories": [ {"code":"new2"} ] }'
102         when: 'the batches of new list element(s) are saved'
103             objectUnderTest.saveListElementsBatch(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1 , '/bookstore', [json1, json2], OffsetDateTime.now())
104         then: 'they can be retrieved by their xpaths'
105             objectUnderTest.getDataNodes(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, '/bookstore/categories[@code="new1"]', FetchDescendantsOption.DIRECT_CHILDREN_ONLY).size() == 1
106             objectUnderTest.getDataNodes(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, '/bookstore/categories[@code="new2"]', FetchDescendantsOption.DIRECT_CHILDREN_ONLY).size() == 1
107         and: 'there are now two extra datanodes'
108             assert originalCountBookstoreChildNodes + 2 == countDataNodesInTree(objectUnderTest.getDataNodes(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, '/bookstore', FetchDescendantsOption.DIRECT_CHILDREN_ONLY))
109         when: 'the new elements are deleted'
110             objectUnderTest.deleteDataNode(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, '/bookstore/categories[@code="new1"]', OffsetDateTime.now())
111             objectUnderTest.deleteDataNode(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, '/bookstore/categories[@code="new2"]', OffsetDateTime.now())
112         then: 'the original number of datanodes is restored'
113             assert originalCountBookstoreChildNodes == countDataNodesInTree(objectUnderTest.getDataNodes(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, '/bookstore', FetchDescendantsOption.DIRECT_CHILDREN_ONLY))
114     }
115
116     def 'Update multiple data node leaves.'() {
117         given: 'Updated json for bookstore data'
118             def jsonData =  "{'book-store:books':{'lang':'English/French','price':100,'title':'Matilda','authors':['RoaldDahl']}}"
119         when: 'update is performed for leaves'
120             objectUnderTest.updateNodeLeaves(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_FOR_PATCH, "/bookstore/categories[@code='1']", jsonData, OffsetDateTime.now())
121         then: 'the updated data nodes are retrieved'
122             def result = cpsDataService.getDataNodes(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_FOR_PATCH, "/bookstore/categories[@code=1]/books[@title='Matilda']", FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS)
123         and: 'the leaf values are updated as expected'
124             assert result.leaves['lang'] == ['English/French']
125             assert result.leaves['price'] == [100]
126     }
127
128     def 'Update multiple data leaves error scenario: #scenario.'() {
129         given: 'Updated json for bookstore data'
130             def jsonData =  "{'book-store:books':{'lang':'English/French','price':100,'title':'Matilda','authors':['RoaldDahl'],'pub_year':1988}}"
131         when: 'attempt to update data node for #scenario'
132             objectUnderTest.updateNodeLeaves(dataspaceName, anchorName, xpath, jsonData, OffsetDateTime.now())
133         then: 'a #expectedException is thrown'
134             thrown(expectedException)
135         where: 'the following data is used'
136             scenario                 | dataspaceName                  | anchorName                 | xpath                 || expectedException
137             'invalid dataspace name' | 'INVALID DATAsPACE'            | 'not-relevant'             | '/not relevant'       || DataValidationException
138             'invalid anchor name'    | FUNCTIONAL_TEST_DATASPACE_1    | 'INVALID ANCHOR'           | '/not relevant'       || DataValidationException
139             'non-existing dataspace' | 'non-existing-dataspace'       | 'not-relevant'             | '/not relevant'       || DataspaceNotFoundException
140             'non-existing anchor'    | FUNCTIONAL_TEST_DATASPACE_1    | 'non-existing-anchor'      | '/not relevant'       || AnchorNotFoundException
141             'non-existing-xpath'     | FUNCTIONAL_TEST_DATASPACE_1    | BOOKSTORE_ANCHOR_FOR_PATCH | '/non-existing'       || DataValidationException
142     }
143 }