-import org.springframework.beans.factory.annotation.Autowired
-import org.springframework.dao.DataIntegrityViolationException
-import org.springframework.test.context.jdbc.Sql
-import spock.lang.Unroll
-
-import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS
-import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS
-
-class CpsDataPersistenceServiceSpec extends CpsPersistenceSpecBase {
-
- @Autowired
- CpsDataPersistenceService objectUnderTest
-
- static final String SET_DATA = '/data/fragment.sql'
- static final long ID_DATA_NODE_WITH_DESCENDANTS = 4001
- static final String XPATH_DATA_NODE_WITH_DESCENDANTS = '/parent-1'
- static final String XPATH_DATA_NODE_WITH_LEAVES = '/parent-100'
-
- static final DataNode newDataNode = new DataNodeBuilder().build()
- static DataNode existingDataNode
- static DataNode existingChildDataNode
-
- static Map<String, Map<String, Object>> expectedLeavesByXpathMap = [
- '/parent-100' : ["x": "y"],
- '/parent-100/child-001' : ["a": "b", "c": ["d", "e", "f"]],
- '/parent-100/child-002' : ["g": "h", "i": ["j", "k"]],
- '/parent-100/child-002/grand-child': ["l": "m", "n": ["o", "p"]]
- ]
-
- static {
- existingDataNode = createDataNodeTree(XPATH_DATA_NODE_WITH_DESCENDANTS)
- existingChildDataNode = createDataNodeTree('/parent-1/child-1')
- }
-
- @Sql([CLEAR_DATA, SET_DATA])
- def 'StoreDataNode with descendants.'() {
- when: 'a fragment with descendants is stored'
- def parentXpath = "/parent-new"
- def childXpath = "/parent-new/child-new"
- def grandChildXpath = "/parent-new/child-new/grandchild-new"
- objectUnderTest.storeDataNode(DATASPACE_NAME, ANCHOR_NAME1,
- createDataNodeTree(parentXpath, childXpath, grandChildXpath))
- then: 'it can be retrieved by its xpath'
- def parentFragment = getFragmentByXpath(DATASPACE_NAME, ANCHOR_NAME1, parentXpath)
- and: 'it contains the children'
- parentFragment.childFragments.size() == 1
- def childFragment = parentFragment.childFragments[0]
- childFragment.xpath == childXpath
- and: "and its children's children"
- childFragment.childFragments.size() == 1
- def grandchildFragment = childFragment.childFragments[0]
- grandchildFragment.xpath == grandChildXpath
- }
-
- @Sql([CLEAR_DATA, SET_DATA])
- def 'Store datanode error scenario: #scenario.'() {
- when: 'attempt to store a data node with #scenario'
- objectUnderTest.storeDataNode(dataspaceName, anchorName, dataNode)
- then: 'a #expectedException is thrown'
- thrown(expectedException)
- where: 'the following data is used'
- scenario | dataspaceName | anchorName | dataNode || expectedException
- 'dataspace does not exist' | 'unknown' | 'not-relevant' | newDataNode || DataspaceNotFoundException
- 'schema set does not exist' | DATASPACE_NAME | 'unknown' | newDataNode || AnchorNotFoundException
- 'anchor already exists' | DATASPACE_NAME | ANCHOR_NAME1 | existingDataNode || DataIntegrityViolationException
- }
-
- @Sql([CLEAR_DATA, SET_DATA])
- def 'Add a child to a Fragment that already has a child.'() {
- given: ' a new child node'
- def newChild = createDataNodeTree('xpath for new child')
- when: 'the child is added to an existing parent with 1 child'
- objectUnderTest.addChildDataNode(DATASPACE_NAME, ANCHOR_NAME1, XPATH_DATA_NODE_WITH_DESCENDANTS, newChild)
- then: 'the parent is now has to 2 children'
- def expectedExistingChildPath = '/parent-1/child-1'
- def parentFragment = fragmentRepository.findById(ID_DATA_NODE_WITH_DESCENDANTS).orElseThrow()
- parentFragment.getChildFragments().size() == 2
- and: 'it still has the old child'
- parentFragment.getChildFragments().find({ it.xpath == expectedExistingChildPath })
- and: 'it has the new child'
- parentFragment.getChildFragments().find({ it.xpath == newChild.xpath })
- }
-
- @Sql([CLEAR_DATA, SET_DATA])
- def 'Add child error scenario: #scenario.'() {
- when: 'attempt to add a child data node with #scenario'
- objectUnderTest.addChildDataNode(DATASPACE_NAME, ANCHOR_NAME1, parentXpath, dataNode)
- then: 'a #expectedException is thrown'
- thrown(expectedException)
- where: 'the following data is used'
- scenario | parentXpath | dataNode || expectedException
- 'parent does not exist' | 'unknown' | newDataNode || DataNodeNotFoundException
- 'already existing child' | XPATH_DATA_NODE_WITH_DESCENDANTS | existingChildDataNode || DataIntegrityViolationException
- }
-
- static def createDataNodeTree(String... xpaths) {
- def dataNodeBuilder = new DataNodeBuilder().withXpath(xpaths[0])
- if (xpaths.length > 1) {
- def xPathsDescendant = Arrays.copyOfRange(xpaths, 1, xpaths.length)
- def childDataNode = createDataNodeTree(xPathsDescendant)
- dataNodeBuilder.withChildDataNodes(ImmutableSet.of(childDataNode))
+import org.onap.cps.spi.repository.AnchorRepository
+import org.onap.cps.spi.repository.DataspaceRepository
+import org.onap.cps.spi.repository.FragmentRepository
+import org.onap.cps.spi.utils.SessionManager
+import org.onap.cps.utils.JsonObjectMapper
+import spock.lang.Specification
+
+class CpsDataPersistenceServiceSpec extends Specification {
+
+ def mockDataspaceRepository = Mock(DataspaceRepository)
+ def mockAnchorRepository = Mock(AnchorRepository)
+ def mockFragmentRepository = Mock(FragmentRepository)
+ def jsonObjectMapper = new JsonObjectMapper(new ObjectMapper())
+ def mockSessionManager = Mock(SessionManager)
+
+ def objectUnderTest = new CpsDataPersistenceServiceImpl(
+ mockDataspaceRepository, mockAnchorRepository, mockFragmentRepository, jsonObjectMapper,mockSessionManager)
+
+ def 'Handling of StaleStateException (caused by concurrent updates) during data node tree update.'() {
+
+ def parentXpath = 'parent-01'
+ def myDataspaceName = 'my-dataspace'
+ def myAnchorName = 'my-anchor'
+
+ given: 'data node object'
+ def submittedDataNode = new DataNodeBuilder()
+ .withXpath(parentXpath)
+ .withLeaves(['leaf-name': 'leaf-value'])
+ .build()
+ and: 'fragment to be updated'
+ mockFragmentRepository.getByDataspaceAndAnchorAndXpath(_, _, _) >> {
+ def fragmentEntity = new FragmentEntity()
+ fragmentEntity.setXpath(parentXpath)
+ fragmentEntity.setChildFragments(Collections.emptySet())
+ return fragmentEntity