2 * ============LICENSE_START=======================================================
3 * Copyright (C) 2021 Nordix Foundation
4 * ================================================================================
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 * SPDX-License-Identifier: Apache-2.0
17 * ============LICENSE_END=========================================================
19 package org.onap.cps.spi.impl
21 import com.google.common.collect.ImmutableSet
22 import org.onap.cps.spi.CpsDataPersistenceService
23 import org.onap.cps.spi.exceptions.AnchorNotFoundException
24 import org.onap.cps.spi.exceptions.DataNodeNotFoundException
25 import org.onap.cps.spi.exceptions.DataspaceNotFoundException
26 import org.onap.cps.spi.model.DataNode
27 import org.onap.cps.spi.model.DataNodeBuilder
28 import org.springframework.beans.factory.annotation.Autowired
29 import org.springframework.dao.DataIntegrityViolationException
30 import org.springframework.test.context.jdbc.Sql
32 class CpsDataPersistenceServiceSpec extends CpsPersistenceSpecBase {
35 CpsDataPersistenceService objectUnderTest
37 static final String SET_DATA = '/data/fragment.sql'
38 static final long ID_DATA_NODE_WITH_DESCENDANTS = 4001
39 static final String XPATH_DATA_NODE_WITH_DESCENDANTS = '/parent-1'
41 static final DataNode newDataNode = new DataNodeBuilder().build()
42 static DataNode existingDataNode
43 static DataNode existingChildDataNode
46 existingDataNode = createDataNodeTree(XPATH_DATA_NODE_WITH_DESCENDANTS)
47 existingChildDataNode = createDataNodeTree('/parent-1/child-1')
50 @Sql([CLEAR_DATA, SET_DATA])
51 def 'Get fragment with descendants.'() {
53 TODO: This test is not really testing the object under test! Needs to be updated as part of CPS-71
54 Actually I think this test will become redundant once th store data node tests is asserted using
55 a new getByXpath() method in the service (object under test)
56 A lot of preloaded dat will become redundant then too
59 when: 'a fragment is retrieved from the repository'
60 def fragment = fragmentRepository.findById(ID_DATA_NODE_WITH_DESCENDANTS).orElseThrow()
61 then: 'it has the correct xpath'
62 fragment.xpath == '/parent-1'
63 and: 'it contains the children'
64 fragment.childFragments.size() == 1
65 def childFragment = fragment.childFragments[0]
66 childFragment.xpath == '/parent-1/child-1'
67 and: "and its children's children"
68 childFragment.childFragments.size() == 1
69 def grandchildFragment = childFragment.childFragments[0]
70 grandchildFragment.xpath == '/parent-1/child-1/grandchild-1'
73 @Sql([CLEAR_DATA, SET_DATA])
74 def 'StoreDataNode with descendants.'() {
75 when: 'a fragment with descendants is stored'
76 def parentXpath = "/parent-new"
77 def childXpath = "/parent-new/child-new"
78 def grandChildXpath = "/parent-new/child-new/grandchild-new"
79 objectUnderTest.storeDataNode(DATASPACE_NAME, ANCHOR_NAME1,
80 createDataNodeTree(parentXpath, childXpath, grandChildXpath))
81 then: 'it can be retrieved by its xpath'
82 def parentFragment = getFragmentByXpath(parentXpath)
83 and: 'it contains the children'
84 parentFragment.childFragments.size() == 1
85 def childFragment = parentFragment.childFragments[0]
86 childFragment.xpath == childXpath
87 and: "and its children's children"
88 childFragment.childFragments.size() == 1
89 def grandchildFragment = childFragment.childFragments[0]
90 grandchildFragment.xpath == grandChildXpath
93 @Sql([CLEAR_DATA, SET_DATA])
94 def 'Store datanode error scenario: #scenario.'() {
95 when: 'attempt to store a data node with #scenario'
96 objectUnderTest.storeDataNode(dataspaceName, anchorName, dataNode)
97 then: 'a #expectedException is thrown'
98 thrown(expectedException)
99 where: 'the following data is used'
100 scenario | dataspaceName | anchorName | dataNode || expectedException
101 'dataspace does not exist' | 'unknown' | 'not-relevant' | newDataNode || DataspaceNotFoundException
102 'schema set does not exist' | DATASPACE_NAME | 'unknown' | newDataNode || AnchorNotFoundException
103 'anchor already exists' | DATASPACE_NAME | ANCHOR_NAME1 | existingDataNode || DataIntegrityViolationException
106 @Sql([CLEAR_DATA, SET_DATA])
107 def 'Add a child to a Fragment that already has a child.'() {
108 given: ' a new child node'
109 def newChild = createDataNodeTree('xpath for new child')
110 when: 'the child is added to an existing parent with 1 child'
111 objectUnderTest.addChildDataNode(DATASPACE_NAME, ANCHOR_NAME1, XPATH_DATA_NODE_WITH_DESCENDANTS, newChild)
112 then: 'the parent is now has to 2 children'
113 def expectedExistingChildPath = '/parent-1/child-1'
114 def parentFragment = fragmentRepository.findById(ID_DATA_NODE_WITH_DESCENDANTS).orElseThrow()
115 parentFragment.getChildFragments().size() == 2
116 and : 'it still has the old child'
117 parentFragment.getChildFragments().find( {it.xpath == expectedExistingChildPath})
118 and : 'it has the new child'
119 parentFragment.getChildFragments().find( {it.xpath == newChild.xpath})
122 @Sql([CLEAR_DATA, SET_DATA])
123 def 'Add child error scenario: #scenario.'() {
124 when: 'attempt to add a child data node with #scenario'
125 objectUnderTest.addChildDataNode(DATASPACE_NAME, ANCHOR_NAME1, parentXpath, dataNode)
126 then: 'a #expectedException is thrown'
127 thrown(expectedException)
128 where: 'the following data is used'
129 scenario | parentXpath | dataNode || expectedException
130 'parent does not exist' | 'unknown' | newDataNode || DataNodeNotFoundException
131 'already existing child' | XPATH_DATA_NODE_WITH_DESCENDANTS | existingChildDataNode || DataIntegrityViolationException
134 static def createDataNodeTree(String... xpaths) {
135 def dataNodeBuilder = new DataNodeBuilder().withXpath(xpaths[0])
136 if (xpaths.length > 1) {
137 def xPathsDescendant = Arrays.copyOfRange(xpaths, 1, xpaths.length)
138 def childDataNode = createDataNodeTree(xPathsDescendant)
139 dataNodeBuilder.withChildDataNodes(ImmutableSet.of(childDataNode))
141 dataNodeBuilder.build()
144 def getFragmentByXpath = xpath -> {
145 //TODO: Remove this method when CPS-71 gets implemented
146 fragmentRepository.findAll().stream()
147 .filter(fragment -> fragment.getXpath().contains(xpath)).findAny().orElseThrow()