2 * ============LICENSE_START=======================================================
3 * Copyright (C) 2021-2022 Nordix Foundation
4 * Modifications Copyright (C) 2021 Pantheon.tech
5 * Modifications Copyright (C) 2021-2022 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
11 * 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.api.impl
25 import org.onap.cps.TestUtils
26 import org.onap.cps.api.CpsAdminService
27 import org.onap.cps.notification.NotificationService
28 import org.onap.cps.notification.Operation
29 import org.onap.cps.spi.CpsDataPersistenceService
30 import org.onap.cps.spi.FetchDescendantsOption
31 import org.onap.cps.spi.exceptions.DataValidationException
32 import org.onap.cps.spi.model.Anchor
33 import org.onap.cps.spi.model.DataNode
34 import org.onap.cps.spi.model.DataNodeBuilder
35 import org.onap.cps.yang.YangTextSchemaSourceSet
36 import org.onap.cps.yang.YangTextSchemaSourceSetBuilder
37 import spock.lang.Specification
39 import java.time.OffsetDateTime
41 class CpsDataServiceImplSpec extends Specification {
42 def mockCpsDataPersistenceService = Mock(CpsDataPersistenceService)
43 def mockCpsAdminService = Mock(CpsAdminService)
44 def mockYangTextSchemaSourceSetCache = Mock(YangTextSchemaSourceSetCache)
45 def mockNotificationService = Mock(NotificationService)
47 def objectUnderTest = new CpsDataServiceImpl(mockCpsDataPersistenceService, mockCpsAdminService,
48 mockYangTextSchemaSourceSetCache, mockNotificationService)
51 mockCpsAdminService.getAnchor(dataspaceName, anchorName) >> anchor
54 def dataspaceName = 'some-dataspace'
55 def anchorName = 'some-anchor'
56 def schemaSetName = 'some-schema-set'
57 def anchor = Anchor.builder().name(anchorName).schemaSetName(schemaSetName).build()
58 def observedTimestamp = OffsetDateTime.now()
60 def 'Saving json data.'() {
61 given: 'schema set for given anchor and dataspace references test-tree model'
62 setupSchemaSetMocks('test-tree.yang')
63 when: 'save data method is invoked with test-tree json data'
64 def jsonData = TestUtils.getResourceFileContent('test-tree.json')
65 objectUnderTest.saveData(dataspaceName, anchorName, jsonData, observedTimestamp)
66 then: 'the persistence service method is invoked with correct parameters'
67 1 * mockCpsDataPersistenceService.storeDataNode(dataspaceName, anchorName,
68 { dataNode -> dataNode.xpath == '/test-tree' })
69 and: 'data updated event is sent to notification service'
70 1 * mockNotificationService.processDataUpdatedEvent(dataspaceName, anchorName, '/', Operation.CREATE, observedTimestamp)
73 def 'Saving json data with invalid #scenario.'() {
74 when: 'save data method is invoked with invalid #scenario'
75 objectUnderTest.saveData(dataspaceName, anchorName, _ as String, observedTimestamp)
76 then: 'a data validation exception is thrown'
77 thrown(DataValidationException)
78 and: 'the persistence service method is not invoked'
79 0 * mockCpsDataPersistenceService.storeDataNode(*_)
80 and: 'data updated event is not sent to notification service'
81 0 * mockNotificationService.processDataUpdatedEvent(*_)
82 where: 'the following parameters are used'
83 scenario | dataspaceName | anchorName
84 'dataspace name' | 'dataspace names with spaces' | 'anchorName'
85 'anchor name' | 'dataspaceName' | 'anchor name with spaces'
86 'dataspace and anchor name' | 'dataspace name with spaces' | 'anchor name with spaces'
89 def 'Saving child data fragment under existing node.'() {
90 given: 'schema set for given anchor and dataspace references test-tree model'
91 setupSchemaSetMocks('test-tree.yang')
92 when: 'save data method is invoked with test-tree json data'
93 def jsonData = '{"branch": [{"name": "New"}]}'
94 objectUnderTest.saveData(dataspaceName, anchorName, '/test-tree', jsonData, observedTimestamp)
95 then: 'the persistence service method is invoked with correct parameters'
96 1 * mockCpsDataPersistenceService.addChildDataNode(dataspaceName, anchorName, '/test-tree',
97 { dataNode -> dataNode.xpath == '/test-tree/branch[@name=\'New\']' })
98 and: 'data updated event is sent to notification service'
99 1 * mockNotificationService.processDataUpdatedEvent(dataspaceName, anchorName, '/test-tree', Operation.CREATE, observedTimestamp)
102 def 'Saving child data fragment under existing node with invalid #scenario.'() {
103 when: 'save data method is invoked with test-tree and an invalid #scenario'
104 objectUnderTest.saveData(dataspaceName, anchorName, '/test-tree', _ as String, observedTimestamp)
105 then: 'a data validation exception is thrown'
106 thrown(DataValidationException)
107 and: 'the persistence service method is not invoked'
108 0 * mockCpsDataPersistenceService.addChildDataNode(*_)
109 and: 'data updated event is not sent to notification service'
110 0 * mockNotificationService.processDataUpdatedEvent(*_)
111 where: 'the following parameters are used'
112 scenario | dataspaceName | anchorName
113 'dataspace name' | 'dataspace names with spaces' | 'anchorName'
114 'anchor name' | 'dataspaceName' | 'anchor name with spaces'
115 'dataspace and anchor name' | 'dataspace name with spaces' | 'anchor name with spaces'
118 def 'Saving list element data fragment under existing node.'() {
119 given: 'schema set for given anchor and dataspace references test-tree model'
120 setupSchemaSetMocks('test-tree.yang')
121 when: 'save data method is invoked with list element json data'
122 def jsonData = '{"branch": [{"name": "A"}, {"name": "B"}]}'
123 objectUnderTest.saveListElements(dataspaceName, anchorName, '/test-tree', jsonData, observedTimestamp)
124 then: 'the persistence service method is invoked with correct parameters'
125 1 * mockCpsDataPersistenceService.addListElements(dataspaceName, anchorName, '/test-tree',
126 { dataNodeCollection ->
128 assert dataNodeCollection.size() == 2
129 assert dataNodeCollection.collect { it.getXpath() }
130 .containsAll(['/test-tree/branch[@name=\'A\']', '/test-tree/branch[@name=\'B\']'])
134 and: 'data updated event is sent to notification service'
135 1 * mockNotificationService.processDataUpdatedEvent(dataspaceName, anchorName, '/test-tree', Operation.UPDATE, observedTimestamp)
138 def 'Saving empty list element data fragment.'() {
139 given: 'schema set for given anchor and dataspace references test-tree model'
140 setupSchemaSetMocks('test-tree.yang')
141 when: 'save data method is invoked with an empty list'
142 def jsonData = '{"branch": []}'
143 objectUnderTest.saveListElements(dataspaceName, anchorName, '/test-tree', jsonData, observedTimestamp)
144 then: 'invalid data exception is thrown'
145 thrown(DataValidationException)
148 def 'Saving list element data fragment with invalid #scenario.'() {
149 when: 'save data method is invoked with an invalid #scenario'
150 objectUnderTest.saveListElements(dataspaceName, anchorName, '/test-tree', _ as String, observedTimestamp)
151 then: 'a data validation exception is thrown'
152 thrown(DataValidationException)
153 and: 'add list elements persistence method is not invoked'
154 0 * mockCpsDataPersistenceService.addListElements(*_)
155 where: 'the following parameters are used'
156 scenario | dataspaceName | anchorName
157 'dataspace name' | 'dataspace names with spaces' | 'anchorName'
158 'anchor name' | 'dataspaceName' | 'anchor name with spaces'
159 'dataspace and anchor name' | 'dataspace name with spaces' | 'anchor name with spaces'
162 def 'Get data node with option #fetchDescendantsOption.'() {
164 def dataNode = new DataNodeBuilder().withXpath(xpath).build()
165 given: 'persistence service returns data for get data request'
166 mockCpsDataPersistenceService.getDataNode(dataspaceName, anchorName, xpath, fetchDescendantsOption) >> dataNode
167 expect: 'service returns same data if uses same parameters'
168 objectUnderTest.getDataNode(dataspaceName, anchorName, xpath, fetchDescendantsOption) == dataNode
169 where: 'all fetch options are supported'
170 fetchDescendantsOption << FetchDescendantsOption.values()
173 def 'Get data node with option invalid #scenario.'() {
174 when: 'get data node is invoked with #scenario'
175 objectUnderTest.getDataNode(dataspaceName, anchorName, '/test-tree', FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS)
176 then: 'a data validation exception is thrown'
177 thrown(DataValidationException)
178 and: 'get data node persistence service is not invoked'
179 0 * mockCpsDataPersistenceService.getDataNode(*_)
180 where: 'the following parameters are used'
181 scenario | dataspaceName | anchorName
182 'dataspace name' | 'dataspace names with spaces' | 'anchorName'
183 'anchor name' | 'dataspaceName' | 'anchor name with spaces'
184 'dataspace and anchor name' | 'dataspace name with spaces' | 'anchor name with spaces'
187 def 'Update data node leaves: #scenario.'() {
188 given: 'schema set for given anchor and dataspace references test-tree model'
189 setupSchemaSetMocks('test-tree.yang')
190 when: 'update data method is invoked with json data #jsonData and parent node xpath #parentNodeXpath'
191 objectUnderTest.updateNodeLeaves(dataspaceName, anchorName, parentNodeXpath, jsonData, observedTimestamp)
192 then: 'the persistence service method is invoked with correct parameters'
193 1 * mockCpsDataPersistenceService.updateDataLeaves(dataspaceName, anchorName, expectedNodeXpath, leaves)
194 and: 'data updated event is sent to notification service'
195 1 * mockNotificationService.processDataUpdatedEvent(dataspaceName, anchorName, parentNodeXpath, Operation.UPDATE, observedTimestamp)
196 where: 'following parameters were used'
197 scenario | parentNodeXpath | jsonData || expectedNodeXpath | leaves
198 'top level node' | '/' | '{"test-tree": {"branch": []}}' || '/test-tree' | Collections.emptyMap()
199 'level 2 node' | '/test-tree' | '{"branch": [{"name":"Name"}]}' || '/test-tree/branch[@name=\'Name\']' | ['name': 'Name']
202 def 'Update data node with invalid #scenario.'() {
203 when: 'update data method is invoked with json data #jsonData and parent node xpath #parentNodeXpath'
204 objectUnderTest.updateNodeLeaves(dataspaceName, anchorName, '/', '{"test-tree": {"branch": []}}', observedTimestamp)
205 then: 'a data validation exception is thrown'
206 thrown(DataValidationException)
207 and: 'the persistence service method is not invoked'
208 0 * mockCpsDataPersistenceService.updateDataLeaves(*_)
209 and: 'data updated event is not sent to notification service'
210 0 * mockNotificationService.processDataUpdatedEvent(*_)
211 where: 'the following parameters are used'
212 scenario | dataspaceName | anchorName
213 'dataspace name' | 'dataspace names with spaces' | 'anchorName'
214 'anchor name' | 'dataspaceName' | 'anchor name with spaces'
215 'dataspace and anchor name' | 'dataspace name with spaces' | 'anchor name with spaces'
218 def 'Update list-element data node with : #scenario.'() {
219 given: 'schema set for given anchor and dataspace references bookstore model'
220 setupSchemaSetMocks('bookstore.yang')
221 when: 'update data method is invoked with json data #jsonData and parent node xpath'
222 objectUnderTest.updateNodeLeaves(dataspaceName, anchorName, '/bookstore/categories[@code=2]',
223 jsonData, observedTimestamp)
224 then: 'the persistence service method is invoked with correct parameters'
225 thrown(DataValidationException)
226 where: 'following parameters were used'
228 'multiple expectedLeaves' | '{"code": "01","name": "some-name"}'
229 'one leaf' | '{"name": "some-name"}'
232 def 'Update Bookstore node leaves' () {
233 given: 'a DMI registry model'
234 setupSchemaSetMocks('bookstore.yang')
235 and: 'the expected json string'
236 def jsonData = '{"categories":[{"code":01,"name":"Romance"}]}'
237 when: 'update data method is invoked with json data and parent node xpath'
238 objectUnderTest.updateNodeLeavesAndExistingDescendantLeaves(dataspaceName, anchorName,
239 '/bookstore', jsonData, observedTimestamp)
240 then: 'the persistence service method is invoked with correct parameters'
241 1 * mockCpsDataPersistenceService.updateDataLeaves(dataspaceName, anchorName,
242 "/bookstore/categories[@code='01']", ['name':'Romance', 'code': '01'])
243 and: 'the data updated event is sent to the notification service'
244 1 * mockNotificationService.processDataUpdatedEvent(dataspaceName, anchorName, '/bookstore', Operation.UPDATE, observedTimestamp)
247 def 'Update Bookstore node leaves with invalid #scenario' () {
248 when: 'update data method is invoked with an invalid #scenario'
249 objectUnderTest.updateNodeLeavesAndExistingDescendantLeaves(dataspaceName, anchorName,
250 '/bookstore', _ as String, observedTimestamp)
251 then: 'a data validation exception is thrown'
252 thrown(DataValidationException)
253 and: 'the persistence service method is not invoked'
254 0 * mockCpsDataPersistenceService.updateDataLeaves(*_)
255 and: 'the data updated event is not sent to the notification service'
256 0 * mockNotificationService.processDataUpdatedEvent(*_)
257 where: 'the following parameters are used'
258 scenario | dataspaceName | anchorName
259 'dataspace name' | 'dataspace names with spaces' | 'anchorName'
260 'anchor name' | 'dataspaceName' | 'anchor name with spaces'
261 'dataspace and anchor name' | 'dataspace name with spaces' | 'anchor name with spaces'
265 def 'Replace data node using singular data node: #scenario.'() {
266 given: 'schema set for given anchor and dataspace references test-tree model'
267 setupSchemaSetMocks('test-tree.yang')
268 when: 'replace data method is invoked with json data #jsonData and parent node xpath #parentNodeXpath'
269 objectUnderTest.updateDataNodeAndDescendants(dataspaceName, anchorName, parentNodeXpath, jsonData, observedTimestamp)
270 then: 'the persistence service method is invoked with correct parameters'
271 1 * mockCpsDataPersistenceService.updateDataNodeAndDescendants(dataspaceName, anchorName,
272 { dataNode -> dataNode.xpath == expectedNodeXpath })
273 and: 'data updated event is sent to notification service'
274 1 * mockNotificationService.processDataUpdatedEvent(dataspaceName, anchorName, parentNodeXpath, Operation.UPDATE, observedTimestamp)
275 where: 'following parameters were used'
276 scenario | parentNodeXpath | jsonData || expectedNodeXpath
277 'top level node' | '/' | '{"test-tree": {"branch": []}}' || '/test-tree'
278 'level 2 node' | '/test-tree' | '{"branch": [{"name":"Name"}]}' || '/test-tree/branch[@name=\'Name\']'
281 def 'Replace data node using multiple data nodes: #scenario.'() {
282 given: 'schema set for given anchor and dataspace references test-tree model'
283 setupSchemaSetMocks('test-tree.yang')
284 when: 'replace data method is invoked with a map of xpaths and json data'
285 objectUnderTest.updateDataNodesAndDescendants(dataspaceName, anchorName, nodesJsonData, observedTimestamp)
286 then: 'the persistence service method is invoked with correct parameters'
287 1 * mockCpsDataPersistenceService.updateDataNodesAndDescendants(dataspaceName, anchorName,
288 { dataNode -> dataNode.xpath == expectedNodeXpath})
289 and: 'data updated event is sent to notification service'
290 1 * mockNotificationService.processDataUpdatedEvent(dataspaceName, anchorName, nodesJsonData.keySet()[0], Operation.UPDATE, observedTimestamp)
291 1 * mockNotificationService.processDataUpdatedEvent(dataspaceName, anchorName, nodesJsonData.keySet()[1], Operation.UPDATE, observedTimestamp)
292 where: 'following parameters were used'
293 scenario | nodesJsonData || expectedNodeXpath
294 'top level node' | ['/' : '{"test-tree": {"branch": []}}', '/test-tree' : '{"branch": [{"name":"Name"}]}'] || ["/test-tree", "/test-tree/branch[@name='Name']"]
295 '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"]
298 def 'Replace data node using singular data node with invalid #scenario.'() {
299 when: 'replace data method is invoked with invalid #scenario'
300 objectUnderTest.updateDataNodeAndDescendants(dataspaceName, anchorName, '/', _ as String, observedTimestamp)
301 then: 'a data validation exception is thrown'
302 thrown(DataValidationException)
303 and: 'the persistence service method is not invoked'
304 0 * mockCpsDataPersistenceService.updateDataNodeAndDescendants(*_)
305 and: 'data updated event is not sent to notification service'
306 0 * mockNotificationService.processDataUpdatedEvent(*_)
307 where: 'the following parameters are used'
308 scenario | dataspaceName | anchorName
309 'dataspace name' | 'dataspace names with spaces' | 'anchorName'
310 'anchor name' | 'dataspaceName' | 'anchor name with spaces'
311 'dataspace and anchor name' | 'dataspace name with spaces' | 'anchor name with spaces'
314 def 'Replace data node using multiple data nodes with invalid #scenario.'() {
315 when: 'replace data method is invoked with invalid #scenario'
316 objectUnderTest.updateDataNodesAndDescendants(dataspaceName, anchorName, ['/': _ as String], observedTimestamp)
317 then: 'a data validation exception is thrown'
318 thrown(DataValidationException)
319 and: 'the persistence service method is not invoked'
320 0 * mockCpsDataPersistenceService.updateDataNodesAndDescendants(*_)
321 and: 'data updated event is not sent to notification service'
322 0 * mockNotificationService.processDataUpdatedEvent(*_)
323 where: 'the following parameters are used'
324 scenario | dataspaceName | anchorName
325 'dataspace name' | 'dataspace names with spaces' | 'anchorName'
326 'anchor name' | 'dataspaceName' | 'anchor name with spaces'
327 'dataspace and anchor name' | 'dataspace name with spaces' | 'anchor name with spaces'
330 def 'Replace list content data fragment under parent node.'() {
331 given: 'schema set for given anchor and dataspace references test-tree model'
332 setupSchemaSetMocks('test-tree.yang')
333 when: 'replace list data method is invoked with list element json data'
334 def jsonData = '{"branch": [{"name": "A"}, {"name": "B"}]}'
335 objectUnderTest.replaceListContent(dataspaceName, anchorName, '/test-tree', jsonData, observedTimestamp)
336 then: 'the persistence service method is invoked with correct parameters'
337 1 * mockCpsDataPersistenceService.replaceListContent(dataspaceName, anchorName, '/test-tree',
338 { dataNodeCollection ->
340 assert dataNodeCollection.size() == 2
341 assert dataNodeCollection.collect { it.getXpath() }
342 .containsAll(['/test-tree/branch[@name=\'A\']', '/test-tree/branch[@name=\'B\']'])
346 and: 'data updated event is sent to notification service'
347 1 * mockNotificationService.processDataUpdatedEvent(dataspaceName, anchorName, '/test-tree', Operation.UPDATE, observedTimestamp)
350 def 'Replace whole list content with empty list element.'() {
351 given: 'schema set for given anchor and dataspace references test-tree model'
352 setupSchemaSetMocks('test-tree.yang')
353 when: 'replace list data method is invoked with empty list'
354 def jsonData = '{"branch": []}'
355 objectUnderTest.replaceListContent(dataspaceName, anchorName, '/test-tree', jsonData, observedTimestamp)
356 then: 'invalid data exception is thrown'
357 thrown(DataValidationException)
360 def 'Replace whole list content with an invalid #scenario.'() {
361 when: 'replace list data method is invoked with invalid #scenario'
362 objectUnderTest.replaceListContent(dataspaceName, anchorName, '/test-tree', _ as Collection<DataNode>, observedTimestamp)
363 then: 'a data validation exception is thrown'
364 thrown(DataValidationException)
365 and: 'the persistence service method is not invoked'
366 0 * mockCpsDataPersistenceService.replaceListContent(*_)
367 and: 'data updated event is not sent to notification service'
368 0 * mockNotificationService.processDataUpdatedEvent(*_)
369 where: 'the following parameters are used'
370 scenario | dataspaceName | anchorName
371 'dataspace name' | 'dataspace names with spaces' | 'anchorName'
372 'anchor name' | 'dataspaceName' | 'anchor name with spaces'
373 'dataspace and anchor name' | 'dataspace name with spaces' | 'anchor name with spaces'
376 def 'Delete list element under existing node.'() {
377 given: 'schema set for given anchor and dataspace references test-tree model'
378 setupSchemaSetMocks('test-tree.yang')
379 when: 'delete list data method is invoked with list element json data'
380 objectUnderTest.deleteListOrListElement(dataspaceName, anchorName, '/test-tree/branch', observedTimestamp)
381 then: 'the persistence service method is invoked with correct parameters'
382 1 * mockCpsDataPersistenceService.deleteListDataNode(dataspaceName, anchorName, '/test-tree/branch')
383 and: 'data updated event is sent to notification service'
384 1 * mockNotificationService.processDataUpdatedEvent(dataspaceName, anchorName, '/test-tree/branch', Operation.DELETE, observedTimestamp)
388 def 'Delete list element with an invalid #scenario.'() {
389 when: 'delete list data method is invoked with with invalid #scenario'
390 objectUnderTest.deleteDataNode(dataspaceName, anchorName, '/data-node', observedTimestamp)
391 then: 'a data validation exception is thrown'
392 thrown(DataValidationException)
393 and: 'the persistence service method is not invoked'
394 0 * mockCpsDataPersistenceService.deleteListDataNode(*_)
395 and: 'data updated event is not sent to notification service'
396 0 * mockNotificationService.processDataUpdatedEvent(*_)
397 where: 'the following parameters are used'
398 scenario | dataspaceName | anchorName
399 'dataspace name' | 'dataspace names with spaces' | 'anchorName'
400 'anchor name' | 'dataspaceName' | 'anchor name with spaces'
401 'dataspace and anchor name' | 'dataspace name with spaces' | 'anchor name with spaces'
404 def 'Delete data node under anchor and dataspace.'() {
405 given: 'schema set for given anchor and dataspace references test tree model'
406 setupSchemaSetMocks('test-tree.yang')
407 when: 'delete data node method is invoked with correct parameters'
408 objectUnderTest.deleteDataNode(dataspaceName, anchorName, '/data-node', observedTimestamp)
409 then: 'the persistence service method is invoked with the correct parameters'
410 1 * mockCpsDataPersistenceService.deleteDataNode(dataspaceName, anchorName, '/data-node')
411 and: 'data updated event is sent to notification service'
412 1 * mockNotificationService.processDataUpdatedEvent(dataspaceName, anchorName, '/data-node', Operation.DELETE, observedTimestamp)
415 def 'Delete data node with an invalid #scenario.'() {
416 when: 'delete data node method is invoked with invalid #scenario'
417 objectUnderTest.deleteDataNode(dataspaceName, anchorName, '/data-node', observedTimestamp)
418 then: 'a data validation exception is thrown'
419 thrown(DataValidationException)
420 and: 'the persistence service method is not invoked'
421 0 * mockCpsDataPersistenceService.deleteDataNode(*_)
422 and: 'data updated event is not sent to notification service'
423 0 * mockNotificationService.processDataUpdatedEvent(*_)
424 where: 'the following parameters are used'
425 scenario | dataspaceName | anchorName
426 'dataspace name' | 'dataspace names with spaces' | 'anchorName'
427 'anchor name' | 'dataspaceName' | 'anchor name with spaces'
428 'dataspace and anchor name' | 'dataspace name with spaces' | 'anchor name with spaces'
431 def 'Delete all data nodes for a given anchor and dataspace.'() {
432 given: 'schema set for given anchor and dataspace references test tree model'
433 setupSchemaSetMocks('test-tree.yang')
434 when: 'delete data node method is invoked with correct parameters'
435 objectUnderTest.deleteDataNodes(dataspaceName, anchorName, observedTimestamp)
436 then: 'data updated event is sent to notification service before the delete'
437 1 * mockNotificationService.processDataUpdatedEvent(dataspaceName, anchorName, '/', Operation.DELETE, observedTimestamp)
438 and: 'the persistence service method is invoked with the correct parameters'
439 1 * mockCpsDataPersistenceService.deleteDataNodes(dataspaceName, anchorName)
442 def setupSchemaSetMocks(String... yangResources) {
443 def mockYangTextSchemaSourceSet = Mock(YangTextSchemaSourceSet)
444 mockYangTextSchemaSourceSetCache.get(dataspaceName, schemaSetName) >> mockYangTextSchemaSourceSet
445 def yangResourceNameToContent = TestUtils.getYangResourcesAsMap(yangResources)
446 def schemaContext = YangTextSchemaSourceSetBuilder.of(yangResourceNameToContent).getSchemaContext()
447 mockYangTextSchemaSourceSet.getSchemaContext() >> schemaContext
450 def 'start session'() {
451 when: 'start session method is called'
452 objectUnderTest.startSession()
453 then: 'the persistence service method to start session is invoked'
454 1 * mockCpsDataPersistenceService.startSession()
457 def 'close session'(){
458 given: 'session Id from calling the start session method'
459 def sessionId = objectUnderTest.startSession()
460 when: 'close session method is called'
461 objectUnderTest.closeSession(sessionId)
462 then: 'the persistence service method to close session is invoked'
463 1 * mockCpsDataPersistenceService.closeSession(sessionId)
466 def 'lock anchor with no timeout parameter'(){
467 when: 'lock anchor method with no timeout parameter with details of anchor entity to lock'
468 objectUnderTest.lockAnchor('some-sessionId', 'some-dataspaceName', 'some-anchorName')
469 then: 'the persistence service method to lock anchor is invoked with default timeout'
470 1 * mockCpsDataPersistenceService.lockAnchor('some-sessionId', 'some-dataspaceName',
471 'some-anchorName', 300L)
474 def 'lock anchor with timeout parameter'(){
475 when: 'lock anchor method with timeout parameter is called with details of anchor entity to lock'
476 objectUnderTest.lockAnchor('some-sessionId', 'some-dataspaceName',
477 'some-anchorName', 250L)
478 then: 'the persistence service method to lock anchor is invoked with the given timeout'
479 1 * mockCpsDataPersistenceService.lockAnchor('some-sessionId', 'some-dataspaceName',
480 'some-anchorName', 250L)