Sending Data Updated Event to kafka
[cps.git] / cps-service / src / test / groovy / org / onap / cps / api / impl / CpsDataServiceImplSpec.groovy
1 /*
2  * ============LICENSE_START=======================================================
3  *  Copyright (C) 2021 Nordix Foundation
4  *  Modifications Copyright (C) 2021 Pantheon.tech
5  *  Modifications Copyright (C) 2021 Bell Canada.
6  *  ================================================================================
7  *  Licensed under the Apache License, Version 2.0 (the "License");
8  *  you may not use this file except in compliance with the License.
9  *  You may obtain a copy of the License at
10  *
11  *        http://www.apache.org/licenses/LICENSE-2.0
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.api.impl
23
24 import org.onap.cps.TestUtils
25 import org.onap.cps.api.CpsAdminService
26 import org.onap.cps.api.CpsModuleService
27 import org.onap.cps.notification.NotificationService
28 import org.onap.cps.spi.CpsDataPersistenceService
29 import org.onap.cps.spi.FetchDescendantsOption
30 import org.onap.cps.spi.exceptions.DataValidationException
31 import org.onap.cps.spi.model.Anchor
32 import org.onap.cps.spi.model.DataNodeBuilder
33 import org.onap.cps.yang.YangTextSchemaSourceSet
34 import org.onap.cps.yang.YangTextSchemaSourceSetBuilder
35 import spock.lang.Specification
36
37 class CpsDataServiceImplSpec extends Specification {
38     def mockCpsDataPersistenceService = Mock(CpsDataPersistenceService)
39     def mockCpsAdminService = Mock(CpsAdminService)
40     def mockCpsModuleService = Mock(CpsModuleService)
41     def mockYangTextSchemaSourceSetCache = Mock(YangTextSchemaSourceSetCache)
42     def mockNotificationService = Mock(NotificationService)
43
44     def objectUnderTest = new CpsDataServiceImpl()
45
46     def setup() {
47         objectUnderTest.cpsDataPersistenceService = mockCpsDataPersistenceService
48         objectUnderTest.cpsAdminService = mockCpsAdminService
49         objectUnderTest.cpsModuleService = mockCpsModuleService
50         objectUnderTest.yangTextSchemaSourceSetCache = mockYangTextSchemaSourceSetCache
51         objectUnderTest.notificationService = mockNotificationService
52     }
53
54     def dataspaceName = 'some dataspace'
55     def anchorName = 'some anchor'
56     def schemaSetName = 'some schema set'
57
58     def 'Saving json data.'() {
59         given: 'schema set for given anchor and dataspace references test-tree model'
60             setupSchemaSetMocks('test-tree.yang')
61         when: 'save data method is invoked with test-tree json data'
62             def jsonData = TestUtils.getResourceFileContent('test-tree.json')
63             objectUnderTest.saveData(dataspaceName, anchorName, jsonData)
64         then: 'the persistence service method is invoked with correct parameters'
65             1 * mockCpsDataPersistenceService.storeDataNode(dataspaceName, anchorName,
66                     { dataNode -> dataNode.xpath == '/test-tree' })
67         and: 'data updated event is sent to notification service'
68             1 * mockNotificationService.processDataUpdatedEvent(dataspaceName, anchorName)
69     }
70
71     def 'Saving child data fragment under existing node.'() {
72         given: 'schema set for given anchor and dataspace references test-tree model'
73             setupSchemaSetMocks('test-tree.yang')
74         when: 'save data method is invoked with test-tree json data'
75             def jsonData = '{"branch": [{"name": "New"}]}'
76             objectUnderTest.saveData(dataspaceName, anchorName, '/test-tree', jsonData)
77         then: 'the persistence service method is invoked with correct parameters'
78             1 * mockCpsDataPersistenceService.addChildDataNode(dataspaceName, anchorName, '/test-tree',
79                     { dataNode -> dataNode.xpath == '/test-tree/branch[@name=\'New\']' })
80         and: 'data updated event is sent to notification service'
81             1 * mockNotificationService.processDataUpdatedEvent(dataspaceName, anchorName)
82     }
83
84     def 'Saving list-node data fragment under existing node.'() {
85         given: 'schema set for given anchor and dataspace references test-tree model'
86             setupSchemaSetMocks('test-tree.yang')
87         when: 'save data method is invoked with list-node json data'
88             def jsonData = '{"branch": [{"name": "A"}, {"name": "B"}]}'
89             objectUnderTest.saveListNodeData(dataspaceName, anchorName, '/test-tree', jsonData)
90         then: 'the persistence service method is invoked with correct parameters'
91             1 * mockCpsDataPersistenceService.addListDataNodes(dataspaceName, anchorName, '/test-tree',
92                     { dataNodeCollection ->
93                         {
94                             assert dataNodeCollection.size() == 2
95                             assert dataNodeCollection.collect { it.getXpath() }
96                                     .containsAll(['/test-tree/branch[@name=\'A\']', '/test-tree/branch[@name=\'B\']'])
97                         }
98                     }
99             )
100         and: 'data updated event is sent to notification service'
101             1 * mockNotificationService.processDataUpdatedEvent(dataspaceName, anchorName)
102     }
103
104     def 'Saving empty list-node data fragment.'() {
105         given: 'schema set for given anchor and dataspace references test-tree model'
106             setupSchemaSetMocks('test-tree.yang')
107         when: 'save data method is invoked with empty list-node data fragment'
108             def jsonData = '{"branch": []}'
109             objectUnderTest.saveListNodeData(dataspaceName, anchorName, '/test-tree', jsonData)
110         then: 'invalid data exception is thrown'
111             thrown(DataValidationException)
112     }
113
114     def 'Get data node with option #fetchDescendantsOption.'() {
115         def xpath = '/xpath'
116         def dataNode = new DataNodeBuilder().withXpath(xpath).build()
117         given: 'persistence service returns data for get data request'
118             mockCpsDataPersistenceService.getDataNode(dataspaceName, anchorName, xpath, fetchDescendantsOption) >> dataNode
119         expect: 'service returns same data if uses same parameters'
120             objectUnderTest.getDataNode(dataspaceName, anchorName, xpath, fetchDescendantsOption) == dataNode
121         where: 'all fetch options are supported'
122             fetchDescendantsOption << FetchDescendantsOption.values()
123     }
124
125     def 'Update data node leaves: #scenario.'() {
126         given: 'schema set for given anchor and dataspace references test-tree model'
127             setupSchemaSetMocks('test-tree.yang')
128         when: 'update data method is invoked with json data #jsonData and parent node xpath #parentNodeXpath'
129             objectUnderTest.updateNodeLeaves(dataspaceName, anchorName, parentNodeXpath, jsonData)
130         then: 'the persistence service method is invoked with correct parameters'
131             1 * mockCpsDataPersistenceService.updateDataLeaves(dataspaceName, anchorName, expectedNodeXpath, leaves)
132         and: 'data updated event is sent to notification service'
133             1 * mockNotificationService.processDataUpdatedEvent(dataspaceName, anchorName)
134         where: 'following parameters were used'
135             scenario         | parentNodeXpath | jsonData                        || expectedNodeXpath                   | leaves
136             'top level node' | '/'             | '{"test-tree": {"branch": []}}' || '/test-tree'                        | Collections.emptyMap()
137             'level 2 node'   | '/test-tree'    | '{"branch": [{"name":"Name"}]}' || '/test-tree/branch[@name=\'Name\']' | ['name': 'Name']
138     }
139
140     def 'Update list-element data node with : #scenario.'() {
141         given: 'schema set for given anchor and dataspace references bookstore model'
142             setupSchemaSetMocks('bookstore.yang')
143         when: 'update data method is invoked with json data #jsonData and parent node xpath'
144             objectUnderTest.updateNodeLeaves(dataspaceName, anchorName, '/bookstore/categories[@code=2]', jsonData)
145         then: 'the persistence service method is invoked with correct parameters'
146             thrown(DataValidationException)
147         where: 'following parameters were used'
148             scenario          | jsonData
149             'multiple leaves' | '{"code": "01","name": "some-name"}'
150             'one leaf'        | '{"name": "some-name"}'
151     }
152
153     def 'Replace data node: #scenario.'() {
154         given: 'schema set for given anchor and dataspace references test-tree model'
155             setupSchemaSetMocks('test-tree.yang')
156         when: 'replace data method is invoked with json data #jsonData and parent node xpath #parentNodeXpath'
157             objectUnderTest.replaceNodeTree(dataspaceName, anchorName, parentNodeXpath, jsonData)
158         then: 'the persistence service method is invoked with correct parameters'
159             1 * mockCpsDataPersistenceService.replaceDataNodeTree(dataspaceName, anchorName,
160                     { dataNode -> dataNode.xpath == expectedNodeXpath })
161         and: 'data updated event is sent to notification service'
162             1 * mockNotificationService.processDataUpdatedEvent(dataspaceName, anchorName)
163         where: 'following parameters were used'
164             scenario         | parentNodeXpath | jsonData                        || expectedNodeXpath
165             'top level node' | '/'             | '{"test-tree": {"branch": []}}' || '/test-tree'
166             'level 2 node'   | '/test-tree'    | '{"branch": [{"name":"Name"}]}' || '/test-tree/branch[@name=\'Name\']'
167     }
168
169     def 'Replace list-node data fragment under existing node.'() {
170         given: 'schema set for given anchor and dataspace references test-tree model'
171             setupSchemaSetMocks('test-tree.yang')
172         when: 'replace list data method is invoked with list-node json data'
173             def jsonData = '{"branch": [{"name": "A"}, {"name": "B"}]}'
174             objectUnderTest.replaceListNodeData(dataspaceName, anchorName, '/test-tree', jsonData)
175         then: 'the persistence service method is invoked with correct parameters'
176             1 * mockCpsDataPersistenceService.replaceListDataNodes(dataspaceName, anchorName, '/test-tree',
177                     { dataNodeCollection ->
178                         {
179                             assert dataNodeCollection.size() == 2
180                             assert dataNodeCollection.collect { it.getXpath() }
181                                     .containsAll(['/test-tree/branch[@name=\'A\']', '/test-tree/branch[@name=\'B\']'])
182                         }
183                     }
184             )
185         and: 'data updated event is sent to notification service'
186             1 * mockNotificationService.processDataUpdatedEvent(dataspaceName, anchorName)
187     }
188
189     def 'Replace with empty list-node data fragment.'() {
190         given: 'schema set for given anchor and dataspace references test-tree model'
191             setupSchemaSetMocks('test-tree.yang')
192         when: 'replace list data method is invoked with empty list-node data fragment'
193             def jsonData = '{"branch": []}'
194             objectUnderTest.replaceListNodeData(dataspaceName, anchorName, '/test-tree', jsonData)
195         then: 'invalid data exception is thrown'
196             thrown(DataValidationException)
197     }
198
199     def setupSchemaSetMocks(String... yangResources) {
200         def anchor = Anchor.builder().name(anchorName).schemaSetName(schemaSetName).build()
201         mockCpsAdminService.getAnchor(dataspaceName, anchorName) >> anchor
202         def mockYangTextSchemaSourceSet = Mock(YangTextSchemaSourceSet)
203         mockYangTextSchemaSourceSetCache.get(dataspaceName, schemaSetName) >> mockYangTextSchemaSourceSet
204         def yangResourceNameToContent = TestUtils.getYangResourcesAsMap(yangResources)
205         def schemaContext = YangTextSchemaSourceSetBuilder.of(yangResourceNameToContent).getSchemaContext()
206         mockYangTextSchemaSourceSet.getSchemaContext() >> schemaContext
207     }
208 }