summaryrefslogtreecommitdiffstats
path: root/cps-ri/src/test/groovy/org
diff options
context:
space:
mode:
authorRenu Kumari <renu.kumari@bell.ca>2021-07-14 14:26:33 -0400
committerRenu Kumari <renu.kumari@bell.ca>2021-07-21 07:38:40 -0400
commita3e8d92c291a61395e91554004af318f70090ecf (patch)
tree33881ea8e044125b7ae4197ed530f6931ce2121f /cps-ri/src/test/groovy/org
parent6355b212de77e658b16614eb775f03c7713c8460 (diff)
Fixed inconsistent data issue with replaceNode
Issue-ID: CPS-501 Signed-off-by: Renu Kumari <renu.kumari@bell.ca> Change-Id: Ic4785d97013729b80f81aca3de4430bdaa8155fa
Diffstat (limited to 'cps-ri/src/test/groovy/org')
-rwxr-xr-xcps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceSpec.groovy50
-rw-r--r--cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceUnitSpec.groovy72
2 files changed, 118 insertions, 4 deletions
diff --git a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceSpec.groovy b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceSpec.groovy
index 0ad67d5418..3a6947379c 100755
--- a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceSpec.groovy
+++ b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceSpec.groovy
@@ -21,6 +21,8 @@
*/
package org.onap.cps.spi.impl
+import org.onap.cps.spi.exceptions.ConcurrencyException
+
import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS
import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS
@@ -305,13 +307,53 @@ class CpsDataPersistenceServiceSpec extends CpsPersistenceSpecBase {
def updatedLeaves = getLeavesMap(updatedFragment)
assert updatedLeaves.size() == 1
assert updatedLeaves.'leaf-value' == 'new'
+ and: 'existing child entry is not updated as content is same'
+ def childFragment = updatedFragment.getChildFragments().iterator().next()
+ childFragment.getXpath() == '/parent-200/child-201/grand-child'
+ def childLeaves = getLeavesMap(childFragment)
+ assert childLeaves.'leaf-value' == 'original'
+ }
+
+ @Sql([CLEAR_DATA, SET_DATA])
+ def 'Replace data node tree with same descendants but changed leaf value.'() {
+ given: 'data node object with leaves updated, having child with old content'
+ def submittedDataNode = buildDataNode("/parent-200/child-201", ['leaf-value': 'new'], [
+ buildDataNode("/parent-200/child-201/grand-child", ['leaf-value': 'new'], [])
+ ])
+ when: 'update is performed including descendants'
+ objectUnderTest.replaceDataNodeTree(DATASPACE_NAME, ANCHOR_FOR_DATA_NODES_WITH_LEAVES, submittedDataNode)
+ then: 'leaves have been updated for selected data node'
+ def updatedFragment = fragmentRepository.getOne(UPDATE_DATA_NODE_FRAGMENT_ID)
+ def updatedLeaves = getLeavesMap(updatedFragment)
+ assert updatedLeaves.size() == 1
+ assert updatedLeaves.'leaf-value' == 'new'
+ and: 'existing child entry is updated with the new content'
+ def childFragment = updatedFragment.getChildFragments().iterator().next()
+ childFragment.getXpath() == '/parent-200/child-201/grand-child'
+ def childLeaves = getLeavesMap(childFragment)
+ assert childLeaves.'leaf-value' == 'new'
+ }
+
+ @Sql([CLEAR_DATA, SET_DATA])
+ def 'Replace data node tree with different descendants xpath'() {
+ given: 'data node object with leaves updated, having child with old content'
+ def submittedDataNode = buildDataNode("/parent-200/child-201", ['leaf-value': 'new'], [
+ buildDataNode("/parent-200/child-201/grand-child-new", ['leaf-value': 'new'], [])
+ ])
+ when: 'update is performed including descendants'
+ objectUnderTest.replaceDataNodeTree(DATASPACE_NAME, ANCHOR_FOR_DATA_NODES_WITH_LEAVES, submittedDataNode)
+ then: 'leaves have been updated for selected data node'
+ def updatedFragment = fragmentRepository.getOne(UPDATE_DATA_NODE_FRAGMENT_ID)
+ def updatedLeaves = getLeavesMap(updatedFragment)
+ assert updatedLeaves.size() == 1
+ assert updatedLeaves.'leaf-value' == 'new'
and: 'previously attached child entry is removed from database'
fragmentRepository.findById(UPDATE_DATA_NODE_SUB_FRAGMENT_ID).isEmpty()
- and: 'new child entry with same content is created'
+ and: 'new child entry is persisted'
def childFragment = updatedFragment.getChildFragments().iterator().next()
+ childFragment.getXpath() == '/parent-200/child-201/grand-child-new'
def childLeaves = getLeavesMap(childFragment)
- assert childFragment.getId() != UPDATE_DATA_NODE_SUB_FRAGMENT_ID
- assert childLeaves.'leaf-value' == 'original'
+ assert childLeaves.'leaf-value' == 'new'
}
@Sql([CLEAR_DATA, SET_DATA])
@@ -320,7 +362,7 @@ class CpsDataPersistenceServiceSpec extends CpsPersistenceSpecBase {
def submittedDataNode = buildDataNode(xpath, ['leaf-name': 'leaf-value'], [])
when: 'attempt to update data node for #scenario'
objectUnderTest.replaceDataNodeTree(dataspaceName, anchorName, submittedDataNode)
- then: 'a #expectedException is thrown'
+ then: 'a #expectedException is thrown'
thrown(expectedException)
where: 'the following data is used'
scenario | dataspaceName | anchorName | xpath || expectedException
diff --git a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceUnitSpec.groovy b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceUnitSpec.groovy
new file mode 100644
index 0000000000..5257e62a6a
--- /dev/null
+++ b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceUnitSpec.groovy
@@ -0,0 +1,72 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (c) 2021 Bell Canada.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+*/
+
+package org.onap.cps.spi.impl
+
+import org.hibernate.StaleStateException
+import org.onap.cps.spi.entities.FragmentEntity
+import org.onap.cps.spi.exceptions.ConcurrencyException
+import org.onap.cps.spi.model.DataNodeBuilder
+import org.onap.cps.spi.repository.AnchorRepository
+import org.onap.cps.spi.repository.DataspaceRepository
+import org.onap.cps.spi.repository.FragmentRepository
+import spock.lang.Specification
+
+
+class CpsDataPersistenceServiceUnitSpec extends Specification {
+
+ def mockDataspaceRepository = Mock(DataspaceRepository)
+ def mockAnchorRepository = Mock(AnchorRepository)
+ def mockFragmentRepository = Mock(FragmentRepository)
+
+ def objectUnderTest = new CpsDataPersistenceServiceImpl(
+ mockDataspaceRepository, mockAnchorRepository, mockFragmentRepository)
+
+ 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
+ }
+ and: 'data node is concurrently updated by another transaction'
+ mockFragmentRepository.save(_) >> { throw new StaleStateException("concurrent updates") }
+
+ when: 'attempt to update data node'
+ objectUnderTest.replaceDataNodeTree(myDataspaceName, myAnchorName, submittedDataNode)
+
+ then: 'concurrency exception is thrown'
+ def concurrencyException = thrown(ConcurrencyException)
+ assert concurrencyException.getDetails().contains(myDataspaceName)
+ assert concurrencyException.getDetails().contains(myAnchorName)
+ assert concurrencyException.getDetails().contains(parentXpath)
+ }
+
+
+}