2 * ============LICENSE_START=======================================================
3 * Copyright (C) 2021-2023 Nordix Foundation
4 * Modifications Copyright (C) 2021 Pantheon.tech
5 * Modifications Copyright (C) 2021-2022 Bell Canada.
6 * Modifications Copyright (C) 2022-2023 TechMahindra Ltd.
7 * Modifications Copyright (C) 2022 Deutsche Telekom AG
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
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.
20 * SPDX-License-Identifier: Apache-2.0
21 * ============LICENSE_END=========================================================
24 package org.onap.cps.api.impl
26 import org.onap.cps.TestUtils
27 import org.onap.cps.api.CpsAdminService
28 import org.onap.cps.notification.NotificationService
29 import org.onap.cps.notification.Operation
30 import org.onap.cps.spi.CpsDataPersistenceService
31 import org.onap.cps.spi.FetchDescendantsOption
32 import org.onap.cps.spi.exceptions.DataValidationException
33 import org.onap.cps.spi.model.Anchor
34 import org.onap.cps.spi.model.DataNode
35 import org.onap.cps.spi.model.DataNodeBuilder
36 import org.onap.cps.utils.ContentType
37 import org.onap.cps.utils.TimedYangParser
38 import org.onap.cps.yang.YangTextSchemaSourceSet
39 import org.onap.cps.yang.YangTextSchemaSourceSetBuilder
40 import spock.lang.Specification
41 import org.onap.cps.spi.utils.CpsValidator
43 import java.time.OffsetDateTime
44 import java.util.stream.Collectors
46 class CpsDataServiceImplSpec extends Specification {
47 def mockCpsDataPersistenceService = Mock(CpsDataPersistenceService)
48 def mockCpsAdminService = Mock(CpsAdminService)
49 def mockYangTextSchemaSourceSetCache = Mock(YangTextSchemaSourceSetCache)
50 def mockNotificationService = Mock(NotificationService)
51 def mockCpsValidator = Mock(CpsValidator)
52 def timedYangParser = new TimedYangParser()
54 def objectUnderTest = new CpsDataServiceImpl(mockCpsDataPersistenceService, mockCpsAdminService,
55 mockYangTextSchemaSourceSetCache, mockNotificationService, mockCpsValidator, timedYangParser)
58 mockCpsAdminService.getAnchor(dataspaceName, anchorName) >> anchor
61 def dataspaceName = 'some-dataspace'
62 def anchorName = 'some-anchor'
63 def schemaSetName = 'some-schema-set'
64 def anchor = Anchor.builder().name(anchorName).dataspaceName(dataspaceName).schemaSetName(schemaSetName).build()
65 def observedTimestamp = OffsetDateTime.now()
67 def 'Saving multicontainer json data.'() {
68 given: 'schema set for given anchor and dataspace references test-tree model'
69 setupSchemaSetMocks('multipleDataTree.yang')
70 when: 'save data method is invoked with test-tree json data'
71 def jsonData = TestUtils.getResourceFileContent('multiple-object-data.json')
72 objectUnderTest.saveData(dataspaceName, anchorName, jsonData, observedTimestamp)
73 then: 'the persistence service method is invoked with correct parameters'
74 1 * mockCpsDataPersistenceService.storeDataNodes(dataspaceName, anchorName,
75 { dataNode -> dataNode.xpath[index] == xpath })
76 and: 'the CpsValidator is called on the dataspaceName and AnchorName'
77 1 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName)
78 and: 'data updated event is sent to notification service'
79 1 * mockNotificationService.processDataUpdatedEvent(anchor, '/', Operation.CREATE, observedTimestamp)
82 0 | '/first-container'
87 def 'Saving #scenario data.'() {
88 given: 'schema set for given anchor and dataspace references test-tree model'
89 setupSchemaSetMocks('test-tree.yang')
90 when: 'save data method is invoked with test-tree #scenario data'
91 def data = TestUtils.getResourceFileContent(dataFile)
92 objectUnderTest.saveData(dataspaceName, anchorName, data, observedTimestamp, contentType)
93 then: 'the persistence service method is invoked with correct parameters'
94 1 * mockCpsDataPersistenceService.storeDataNodes(dataspaceName, anchorName,
95 { dataNode -> dataNode.xpath[0] == '/test-tree' })
96 and: 'the CpsValidator is called on the dataspaceName and AnchorName'
97 1 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName)
98 and: 'data updated event is sent to notification service'
99 1 * mockNotificationService.processDataUpdatedEvent(anchor, '/', Operation.CREATE, observedTimestamp)
100 where: 'given parameters'
101 scenario | dataFile | contentType
102 'json' | 'test-tree.json' | ContentType.JSON
103 'xml' | 'test-tree.xml' | ContentType.XML
106 def 'Saving #scenarioDesired data with invalid data.'() {
107 given: 'schema set for given anchor and dataspace references test-tree model'
108 setupSchemaSetMocks('test-tree.yang')
109 when: 'save data method is invoked with test-tree json data'
110 objectUnderTest.saveData(dataspaceName, anchorName, invalidData, observedTimestamp, contentType)
111 then: 'a data validation exception is thrown'
112 thrown(DataValidationException)
113 where: 'given parameters'
114 scenarioDesired | invalidData | contentType
115 'json' | '{invalid json' | ContentType.XML
116 'xml' | '<invalid xml' | ContentType.JSON
120 def 'Saving child data fragment under existing node.'() {
121 given: 'schema set for given anchor and dataspace references test-tree model'
122 setupSchemaSetMocks('test-tree.yang')
123 when: 'save data method is invoked with test-tree json data'
124 def jsonData = '{"branch": [{"name": "New"}]}'
125 objectUnderTest.saveData(dataspaceName, anchorName, '/test-tree', jsonData, observedTimestamp)
126 then: 'the persistence service method is invoked with correct parameters'
127 1 * mockCpsDataPersistenceService.addChildDataNodes(dataspaceName, anchorName, '/test-tree',
128 { dataNode -> dataNode.xpath[0] == '/test-tree/branch[@name=\'New\']' })
129 and: 'the CpsValidator is called on the dataspaceName and AnchorName'
130 1 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName)
131 and: 'data updated event is sent to notification service'
132 1 * mockNotificationService.processDataUpdatedEvent(anchor, '/test-tree', Operation.CREATE, observedTimestamp)
135 def 'Saving list element data fragment under existing node.'() {
136 given: 'schema set for given anchor and dataspace references test-tree model'
137 setupSchemaSetMocks('test-tree.yang')
138 when: 'save data method is invoked with list element json data'
139 def jsonData = '{"branch": [{"name": "A"}, {"name": "B"}]}'
140 objectUnderTest.saveListElements(dataspaceName, anchorName, '/test-tree', jsonData, observedTimestamp)
141 then: 'the persistence service method is invoked with correct parameters'
142 1 * mockCpsDataPersistenceService.addListElements(dataspaceName, anchorName, '/test-tree',
143 { dataNodeCollection ->
145 assert dataNodeCollection.size() == 2
146 assert dataNodeCollection.collect { it.getXpath() }
147 .containsAll(['/test-tree/branch[@name=\'A\']', '/test-tree/branch[@name=\'B\']'])
151 and: 'the CpsValidator is called on the dataspaceName and AnchorName'
152 1 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName)
153 and: 'data updated event is sent to notification service'
154 1 * mockNotificationService.processDataUpdatedEvent(anchor, '/test-tree', Operation.UPDATE, observedTimestamp)
157 def 'Saving collection of a batch with data fragment under existing node.'() {
158 given: 'schema set for given anchor and dataspace references test-tree model'
159 setupSchemaSetMocks('test-tree.yang')
160 when: 'save data method is invoked with list element json data'
161 def jsonData = '{"branch": [{"name": "A"}, {"name": "B"}]}'
162 objectUnderTest.saveListElementsBatch(dataspaceName, anchorName, '/test-tree', [jsonData], observedTimestamp)
163 then: 'the persistence service method is invoked with correct parameters'
164 1 * mockCpsDataPersistenceService.addMultipleLists(dataspaceName, anchorName, '/test-tree',_) >> {
166 def listElementsCollection = args[3] as Collection<Collection<DataNode>>
167 assert listElementsCollection.size() == 1
168 def listOfXpaths = listElementsCollection.stream().flatMap(x -> x.stream()).map(it-> it.xpath).collect(Collectors.toList())
169 assert listOfXpaths.size() == 2
170 assert listOfXpaths.containsAll(['/test-tree/branch[@name=\'B\']','/test-tree/branch[@name=\'A\']'])
173 and: 'data updated event is sent to notification service'
174 1 * mockNotificationService.processDataUpdatedEvent(anchor, '/test-tree', Operation.UPDATE, observedTimestamp)
177 def 'Saving empty list element data fragment.'() {
178 given: 'schema set for given anchor and dataspace references test-tree model'
179 setupSchemaSetMocks('test-tree.yang')
180 when: 'save data method is invoked with an empty list'
181 def jsonData = '{"branch": []}'
182 objectUnderTest.saveListElements(dataspaceName, anchorName, '/test-tree', jsonData, observedTimestamp)
183 then: 'invalid data exception is thrown'
184 thrown(DataValidationException)
187 def 'Get all data nodes #scenario.'() {
188 given: 'persistence service returns data for GET request'
189 mockCpsDataPersistenceService.getDataNodes(dataspaceName, anchorName, xpath, fetchDescendantsOption) >> dataNode
190 expect: 'service returns same data if using same parameters'
191 objectUnderTest.getDataNodes(dataspaceName, anchorName, xpath, fetchDescendantsOption) == dataNode
192 where: 'following parameters were used'
193 scenario | xpath | fetchDescendantsOption | dataNode
194 'with root node xpath and descendants' | '/' | FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS | [new DataNodeBuilder().withXpath('/xpath-1').build(), new DataNodeBuilder().withXpath('/xpath-2').build()]
195 'with root node xpath and no descendants' | '/' | FetchDescendantsOption.OMIT_DESCENDANTS | [new DataNodeBuilder().withXpath('/xpath-1').build(), new DataNodeBuilder().withXpath('/xpath-2').build()]
196 'with valid xpath and descendants' | '/xpath'| FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS | [new DataNodeBuilder().withXpath('/xpath').build()]
197 'with valid xpath and no descendants' | '/xpath'| FetchDescendantsOption.OMIT_DESCENDANTS | [new DataNodeBuilder().withXpath('/xpath').build()]
200 def 'Get all data nodes over multiple xpaths with option #fetchDescendantsOption.'() {
201 def xpath1 = '/xpath-1'
202 def xpath2 = '/xpath-2'
203 def dataNode = [new DataNodeBuilder().withXpath(xpath1).build(), new DataNodeBuilder().withXpath(xpath2).build()]
204 given: 'persistence service returns data for get data request'
205 mockCpsDataPersistenceService.getDataNodesForMultipleXpaths(dataspaceName, anchorName, [xpath1, xpath2], fetchDescendantsOption) >> dataNode
206 expect: 'service returns same data if uses same parameters'
207 objectUnderTest.getDataNodesForMultipleXpaths(dataspaceName, anchorName, [xpath1, xpath2], fetchDescendantsOption) == dataNode
208 where: 'all fetch options are supported'
209 fetchDescendantsOption << [FetchDescendantsOption.OMIT_DESCENDANTS, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS]
212 def 'Update data node leaves: #scenario.'() {
213 given: 'schema set for given anchor and dataspace references test-tree model'
214 setupSchemaSetMocks('test-tree.yang')
215 when: 'update data method is invoked with json data #jsonData and parent node xpath #parentNodeXpath'
216 objectUnderTest.updateNodeLeaves(dataspaceName, anchorName, parentNodeXpath, jsonData, observedTimestamp)
217 then: 'the persistence service method is invoked with correct parameters'
218 1 * mockCpsDataPersistenceService.batchUpdateDataLeaves(dataspaceName, anchorName, {dataNode -> dataNode.keySet()[0] == expectedNodeXpath})
219 and: 'the CpsValidator is called on the dataspaceName and AnchorName'
220 1 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName)
221 and: 'data updated event is sent to notification service'
222 1 * mockNotificationService.processDataUpdatedEvent(anchor, parentNodeXpath, Operation.UPDATE, observedTimestamp)
223 where: 'following parameters were used'
224 scenario | parentNodeXpath | jsonData || expectedNodeXpath
225 'top level node' | '/' | '{"test-tree": {"branch": []}}' || '/test-tree'
226 'level 2 node' | '/test-tree' | '{"branch": [{"name":"Name"}]}' || '/test-tree/branch[@name=\'Name\']'
229 def 'Update list-element data node with : #scenario.'() {
230 given: 'schema set for given anchor and dataspace references bookstore model'
231 setupSchemaSetMocks('bookstore.yang')
232 when: 'update data method is invoked with json data #jsonData and parent node xpath'
233 objectUnderTest.updateNodeLeaves(dataspaceName, anchorName, '/bookstore/categories[@code=2]',
234 jsonData, observedTimestamp)
235 then: 'the persistence service method is invoked with correct parameters'
236 thrown(DataValidationException)
237 where: 'following parameters were used'
239 'multiple expectedLeaves' | '{"code": "01","name": "some-name"}'
240 'one leaf' | '{"name": "some-name"}'
243 def 'Update multiple data nodes' () {
244 given: 'schema set for given dataspace and anchor refers multipleDataTree model'
245 setupSchemaSetMocks('multipleDataTree.yang')
246 and: 'json string with multiple data trees'
247 def parentNodeXpath = '/'
248 def updatedJsonData = '{"first-container":{"a-leaf":"a-new-Value"},"last-container":{"x-leaf":"x-new-value"}}'
249 when: 'update operation is performed on multiple data nodes'
250 objectUnderTest.updateNodeLeaves(dataspaceName, anchorName, parentNodeXpath, updatedJsonData, observedTimestamp)
251 then: 'the persistence service method is invoked with correct parameters'
252 1 * mockCpsDataPersistenceService.batchUpdateDataLeaves(dataspaceName, anchorName, {dataNode -> dataNode.keySet()[index] == expectedNodeXpath})
253 and: 'the CpsValidator is called on the dataspaceName and AnchorName'
254 1 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName)
255 and: 'data updated event is sent to notification service'
256 1 * mockNotificationService.processDataUpdatedEvent(anchor, parentNodeXpath, Operation.UPDATE, observedTimestamp)
257 where: 'the following parameters were used'
258 index | expectedNodeXpath
259 0 | '/first-container'
260 1 | '/last-container'
264 def 'Update Bookstore node leaves' () {
265 given: 'a DMI registry model'
266 setupSchemaSetMocks('bookstore.yang')
267 and: 'the expected json string'
268 def jsonData = '{"categories":[{"code":01,"name":"Romance"}]}'
269 when: 'update data method is invoked with json data and parent node xpath'
270 objectUnderTest.updateNodeLeavesAndExistingDescendantLeaves(dataspaceName, anchorName,
271 '/bookstore', jsonData, observedTimestamp)
272 then: 'the persistence service method is invoked with correct parameters'
273 1 * mockCpsDataPersistenceService.batchUpdateDataLeaves(dataspaceName, anchorName,
274 {updatedDataNodesPerXPath -> updatedDataNodesPerXPath.keySet()
275 .iterator().next() == "/bookstore/categories[@code='01']"})
276 and: 'the CpsValidator is called on the dataspaceName and AnchorName'
277 1 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName)
278 and: 'the data updated event is sent to the notification service'
279 1 * mockNotificationService.processDataUpdatedEvent(anchor, '/bookstore', Operation.UPDATE, observedTimestamp)
282 def 'Replace data node using singular data node: #scenario.'() {
283 given: 'schema set for given anchor and dataspace references test-tree model'
284 setupSchemaSetMocks('test-tree.yang')
285 when: 'replace data method is invoked with json data #jsonData and parent node xpath #parentNodeXpath'
286 objectUnderTest.updateDataNodeAndDescendants(dataspaceName, anchorName, parentNodeXpath, jsonData, observedTimestamp)
287 then: 'the persistence service method is invoked with correct parameters'
288 1 * mockCpsDataPersistenceService.updateDataNodesAndDescendants(dataspaceName, anchorName,
289 { dataNode -> dataNode.xpath[0] == expectedNodeXpath })
290 and: 'data updated event is sent to notification service'
291 1 * mockNotificationService.processDataUpdatedEvent(anchor, parentNodeXpath, Operation.UPDATE, observedTimestamp)
292 and: 'the CpsValidator is called on the dataspaceName and AnchorName'
293 1 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName)
294 where: 'following parameters were used'
295 scenario | parentNodeXpath | jsonData || expectedNodeXpath
296 'top level node' | '/' | '{"test-tree": {"branch": []}}' || '/test-tree'
297 'level 2 node' | '/test-tree' | '{"branch": [{"name":"Name"}]}' || '/test-tree/branch[@name=\'Name\']'
300 def 'Replace data node using multiple data nodes: #scenario.'() {
301 given: 'schema set for given anchor and dataspace references test-tree model'
302 setupSchemaSetMocks('test-tree.yang')
303 when: 'replace data method is invoked with a map of xpaths and json data'
304 objectUnderTest.updateDataNodesAndDescendants(dataspaceName, anchorName, nodesJsonData, observedTimestamp)
305 then: 'the persistence service method is invoked with correct parameters'
306 1 * mockCpsDataPersistenceService.updateDataNodesAndDescendants(dataspaceName, anchorName,
307 { dataNode -> dataNode.xpath == expectedNodeXpath})
308 and: 'data updated event is sent to notification service'
309 1 * mockNotificationService.processDataUpdatedEvent(anchor, nodesJsonData.keySet()[0], Operation.UPDATE, observedTimestamp)
310 1 * mockNotificationService.processDataUpdatedEvent(anchor, nodesJsonData.keySet()[1], Operation.UPDATE, observedTimestamp)
311 and: 'the CpsValidator is called on the dataspaceName and AnchorName'
312 1 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName)
313 where: 'following parameters were used'
314 scenario | nodesJsonData || expectedNodeXpath
315 'top level node' | ['/' : '{"test-tree": {"branch": []}}', '/test-tree' : '{"branch": [{"name":"Name"}]}'] || ["/test-tree", "/test-tree/branch[@name='Name']"]
316 'level 2 node' | ['/test-tree' : '{"branch": [{"name":"Name"}]}', '/test-tree/branch[@name=\'Name\']':'{"nest":{"name":"nestName"}}'] || ["/test-tree/branch[@name='Name']", "/test-tree/branch[@name='Name']/nest"]
319 def 'Replace list content data fragment under parent node.'() {
320 given: 'schema set for given anchor and dataspace references test-tree model'
321 setupSchemaSetMocks('test-tree.yang')
322 when: 'replace list data method is invoked with list element json data'
323 def jsonData = '{"branch": [{"name": "A"}, {"name": "B"}]}'
324 objectUnderTest.replaceListContent(dataspaceName, anchorName, '/test-tree', jsonData, observedTimestamp)
325 then: 'the persistence service method is invoked with correct parameters'
326 1 * mockCpsDataPersistenceService.replaceListContent(dataspaceName, anchorName, '/test-tree',
327 { dataNodeCollection ->
329 assert dataNodeCollection.size() == 2
330 assert dataNodeCollection.collect { it.getXpath() }
331 .containsAll(['/test-tree/branch[@name=\'A\']', '/test-tree/branch[@name=\'B\']'])
335 and: 'the CpsValidator is called on the dataspaceName and AnchorName twice'
336 2 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName)
337 and: 'data updated event is sent to notification service'
338 1 * mockNotificationService.processDataUpdatedEvent(anchor, '/test-tree', Operation.UPDATE, observedTimestamp)
341 def 'Replace whole list content with empty list element.'() {
342 given: 'schema set for given anchor and dataspace references test-tree model'
343 setupSchemaSetMocks('test-tree.yang')
344 when: 'replace list data method is invoked with empty list'
345 def jsonData = '{"branch": []}'
346 objectUnderTest.replaceListContent(dataspaceName, anchorName, '/test-tree', jsonData, observedTimestamp)
347 then: 'invalid data exception is thrown'
348 thrown(DataValidationException)
351 def 'Delete list element under existing node.'() {
352 given: 'schema set for given anchor and dataspace references test-tree model'
353 setupSchemaSetMocks('test-tree.yang')
354 when: 'delete list data method is invoked with list element json data'
355 objectUnderTest.deleteListOrListElement(dataspaceName, anchorName, '/test-tree/branch', observedTimestamp)
356 then: 'the persistence service method is invoked with correct parameters'
357 1 * mockCpsDataPersistenceService.deleteListDataNode(dataspaceName, anchorName, '/test-tree/branch')
358 and: 'the CpsValidator is called on the dataspaceName and AnchorName'
359 1 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName)
360 and: 'data updated event is sent to notification service'
361 1 * mockNotificationService.processDataUpdatedEvent(anchor, '/test-tree/branch', Operation.DELETE, observedTimestamp)
364 def 'Delete multiple list elements under existing node.'() {
365 given: 'schema set for given anchor and dataspace references test-tree model'
366 setupSchemaSetMocks('test-tree.yang')
367 when: 'delete multiple list data method is invoked with list element json data'
368 objectUnderTest.deleteDataNodes(dataspaceName, anchorName, ['/test-tree/branch[@name="A"]', '/test-tree/branch[@name="B"]'], observedTimestamp)
369 then: 'the persistence service method is invoked with correct parameters'
370 1 * mockCpsDataPersistenceService.deleteDataNodes(dataspaceName, anchorName, ['/test-tree/branch[@name="A"]', '/test-tree/branch[@name="B"]'])
371 and: 'the CpsValidator is called on the dataspaceName and AnchorName'
372 1 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName)
373 and: 'two data updated events are sent to notification service'
374 2 * mockNotificationService.processDataUpdatedEvent(anchor, _, Operation.DELETE, observedTimestamp)
377 def 'Delete data node under anchor and dataspace.'() {
378 given: 'schema set for given anchor and dataspace references test tree model'
379 setupSchemaSetMocks('test-tree.yang')
380 when: 'delete data node method is invoked with correct parameters'
381 objectUnderTest.deleteDataNode(dataspaceName, anchorName, '/data-node', observedTimestamp)
382 then: 'the persistence service method is invoked with the correct parameters'
383 1 * mockCpsDataPersistenceService.deleteDataNode(dataspaceName, anchorName, '/data-node')
384 and: 'the CpsValidator is called on the dataspaceName and AnchorName'
385 1 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName)
386 and: 'data updated event is sent to notification service'
387 1 * mockNotificationService.processDataUpdatedEvent(anchor, '/data-node', Operation.DELETE, observedTimestamp)
390 def 'Delete all data nodes for a given anchor and dataspace.'() {
391 given: 'schema set for given anchor and dataspace references test tree model'
392 setupSchemaSetMocks('test-tree.yang')
393 when: 'delete data node method is invoked with correct parameters'
394 objectUnderTest.deleteDataNodes(dataspaceName, anchorName, observedTimestamp)
395 then: 'data updated event is sent to notification service before the delete'
396 1 * mockNotificationService.processDataUpdatedEvent(anchor, '/', Operation.DELETE, observedTimestamp)
397 and: 'the CpsValidator is called on the dataspaceName and AnchorName'
398 1 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName)
399 and: 'the persistence service method is invoked with the correct parameters'
400 1 * mockCpsDataPersistenceService.deleteDataNodes(dataspaceName, anchorName)
403 def 'Delete all data nodes for given dataspace and multiple anchors.'() {
404 given: 'schema set for given anchors and dataspace references test tree model'
405 setupSchemaSetMocks('test-tree.yang')
406 mockCpsAdminService.getAnchors(dataspaceName, ['anchor1', 'anchor2']) >>
407 [new Anchor(name: 'anchor1', dataspaceName: dataspaceName),
408 new Anchor(name: 'anchor2', dataspaceName: dataspaceName)]
409 when: 'delete data node method is invoked with correct parameters'
410 objectUnderTest.deleteDataNodes(dataspaceName, ['anchor1', 'anchor2'], observedTimestamp)
411 then: 'data updated events are sent to notification service before the delete'
412 2 * mockNotificationService.processDataUpdatedEvent(_, '/', Operation.DELETE, observedTimestamp)
413 and: 'the CpsValidator is called on the dataspace name and the anchor names'
414 2 * mockCpsValidator.validateNameCharacters(_)
415 and: 'the persistence service method is invoked with the correct parameters'
416 1 * mockCpsDataPersistenceService.deleteDataNodes(dataspaceName, _ as Collection<String>)
419 def setupSchemaSetMocks(String... yangResources) {
420 def mockYangTextSchemaSourceSet = Mock(YangTextSchemaSourceSet)
421 mockYangTextSchemaSourceSetCache.get(dataspaceName, schemaSetName) >> mockYangTextSchemaSourceSet
422 def yangResourceNameToContent = TestUtils.getYangResourcesAsMap(yangResources)
423 def schemaContext = YangTextSchemaSourceSetBuilder.of(yangResourceNameToContent).getSchemaContext()
424 mockYangTextSchemaSourceSet.getSchemaContext() >> schemaContext
427 def 'start session'() {
428 when: 'start session method is called'
429 objectUnderTest.startSession()
430 then: 'the persistence service method to start session is invoked'
431 1 * mockCpsDataPersistenceService.startSession()
434 def 'close session'(){
435 given: 'session Id from calling the start session method'
436 def sessionId = objectUnderTest.startSession()
437 when: 'close session method is called'
438 objectUnderTest.closeSession(sessionId)
439 then: 'the persistence service method to close session is invoked'
440 1 * mockCpsDataPersistenceService.closeSession(sessionId)
443 def 'lock anchor with no timeout parameter'(){
444 when: 'lock anchor method with no timeout parameter with details of anchor entity to lock'
445 objectUnderTest.lockAnchor('some-sessionId', 'some-dataspaceName', 'some-anchorName')
446 then: 'the persistence service method to lock anchor is invoked with default timeout'
447 1 * mockCpsDataPersistenceService.lockAnchor('some-sessionId', 'some-dataspaceName',
448 'some-anchorName', 300L)
451 def 'lock anchor with timeout parameter'(){
452 when: 'lock anchor method with timeout parameter is called with details of anchor entity to lock'
453 objectUnderTest.lockAnchor('some-sessionId', 'some-dataspaceName',
454 'some-anchorName', 250L)
455 then: 'the persistence service method to lock anchor is invoked with the given timeout'
456 1 * mockCpsDataPersistenceService.lockAnchor('some-sessionId', 'some-dataspaceName',
457 'some-anchorName', 250L)