summaryrefslogtreecommitdiffstats
path: root/integration-test/src
diff options
context:
space:
mode:
authorArpit Singh <as00745003@techmahindra.com>2023-08-02 18:35:31 +0530
committerPriyank Maheshwari <priyank.maheshwari@est.tech>2023-11-14 16:42:42 +0000
commit0339c71815a4ca4cbab3d263d6c4586a112cda64 (patch)
tree89a3ed8006daa27542f464834071fc5191484668 /integration-test/src
parent0fdda53aa0dde9ec3a4c1b287b3ff8da4a75da5c (diff)
CPS Delta API 1: Delta between 2 anchors
- CPS Delta Feature Part 1: To find delta between two anchors - created new endpoint deltaByDataspaceAndAnchors - endpoint to take dataspaceName, source anchor, target anchor, xpath, descendants as input - added new service CpsDeltaService - added method to find delta between DataNodes: getDeltaReport - added method to find removed data nodes: getRemovedDeltaReports - added method to get Added DataNodes: getAddedDeltaReports - added method to get Map of xpath to DataNode: convertToXPathToDataNodesMap - added a POJO for delta report - Added new JSON data for delta feature testing - Added groovy test files CpsDeltaServiceImplSpec and DeltaReportBuilderSpec - code related to update operation, will be added in separate commit Issue-ID: CPS-1824 Signed-off-by: Arpit Singh <as00745003@techmahindra.com> Change-Id: I313f0f71d04b03878be7643f709d8af1aa6df6ba
Diffstat (limited to 'integration-test/src')
-rw-r--r--integration-test/src/test/groovy/org/onap/cps/integration/base/FunctionalSpecBase.groovy17
-rw-r--r--integration-test/src/test/groovy/org/onap/cps/integration/functional/CpsDataServiceIntegrationSpec.groovy97
-rw-r--r--integration-test/src/test/resources/data/bookstore/bookstore.yang11
-rw-r--r--integration-test/src/test/resources/data/bookstore/bookstoreDataForDeltaReport.json192
4 files changed, 317 insertions, 0 deletions
diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/base/FunctionalSpecBase.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/base/FunctionalSpecBase.groovy
index 327a39ee4..14612d6c1 100644
--- a/integration-test/src/test/groovy/org/onap/cps/integration/base/FunctionalSpecBase.groovy
+++ b/integration-test/src/test/groovy/org/onap/cps/integration/base/FunctionalSpecBase.groovy
@@ -1,6 +1,7 @@
/*
* ============LICENSE_START=======================================================
* Copyright (C) 2023 Nordix Foundation
+ * Modifications Copyright (C) 2022-2023 TechMahindra Ltd.
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the 'License');
* you may not use this file except in compliance with the License.
@@ -26,17 +27,24 @@ class FunctionalSpecBase extends CpsIntegrationSpecBase {
def static FUNCTIONAL_TEST_DATASPACE_1 = 'functionalTestDataspace1'
def static FUNCTIONAL_TEST_DATASPACE_2 = 'functionalTestDataspace2'
+ def static FUNCTIONAL_TEST_DATASPACE_3 = 'functionalTestDataspace3'
def static NUMBER_OF_ANCHORS_PER_DATASPACE_WITH_BOOKSTORE_DATA = 2
+ def static NUMBER_OF_ANCHORS_PER_DATASPACE_WITH_BOOKSTORE_DELTA_DATA = 1
def static BOOKSTORE_ANCHOR_1 = 'bookstoreAnchor1'
def static BOOKSTORE_ANCHOR_2 = 'bookstoreAnchor2'
+ def static BOOKSTORE_ANCHOR_3 = 'bookstoreSourceAnchor1'
+ def static BOOKSTORE_ANCHOR_4 = 'copyOfSourceAnchor1'
+ def static BOOKSTORE_ANCHOR_5 = 'bookstoreAnchorForDeltaReport1'
def static initialized = false
def static bookstoreJsonData = readResourceDataFile('bookstore/bookstoreData.json')
+ def static bookstoreJsonDataForDeltaReport = readResourceDataFile('bookstore/bookstoreDataForDeltaReport.json')
def setup() {
if (!initialized) {
setupBookstoreInfraStructure()
addBookstoreData()
+ addDeltaData()
initialized = true
}
}
@@ -44,9 +52,12 @@ class FunctionalSpecBase extends CpsIntegrationSpecBase {
def setupBookstoreInfraStructure() {
cpsAdminService.createDataspace(FUNCTIONAL_TEST_DATASPACE_1)
cpsAdminService.createDataspace(FUNCTIONAL_TEST_DATASPACE_2)
+ cpsAdminService.createDataspace(FUNCTIONAL_TEST_DATASPACE_3)
def bookstoreYangModelAsString = readResourceDataFile('bookstore/bookstore.yang')
cpsModuleService.createSchemaSet(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_SCHEMA_SET, [bookstore: bookstoreYangModelAsString])
cpsModuleService.createSchemaSet(FUNCTIONAL_TEST_DATASPACE_2, BOOKSTORE_SCHEMA_SET, [bookstore: bookstoreYangModelAsString])
+ cpsModuleService.createSchemaSet(FUNCTIONAL_TEST_DATASPACE_3, BOOKSTORE_SCHEMA_SET, [bookstore: bookstoreYangModelAsString])
+
}
def addBookstoreData() {
@@ -54,6 +65,12 @@ class FunctionalSpecBase extends CpsIntegrationSpecBase {
addAnchorsWithData(NUMBER_OF_ANCHORS_PER_DATASPACE_WITH_BOOKSTORE_DATA, FUNCTIONAL_TEST_DATASPACE_2, BOOKSTORE_SCHEMA_SET, 'bookstoreAnchor', bookstoreJsonData)
}
+ def addDeltaData() {
+ addAnchorsWithData(NUMBER_OF_ANCHORS_PER_DATASPACE_WITH_BOOKSTORE_DELTA_DATA, FUNCTIONAL_TEST_DATASPACE_3, BOOKSTORE_SCHEMA_SET, 'bookstoreSourceAnchor', bookstoreJsonData)
+ addAnchorsWithData(NUMBER_OF_ANCHORS_PER_DATASPACE_WITH_BOOKSTORE_DELTA_DATA, FUNCTIONAL_TEST_DATASPACE_3, BOOKSTORE_SCHEMA_SET, 'copyOfSourceAnchor', bookstoreJsonData)
+ addAnchorsWithData(NUMBER_OF_ANCHORS_PER_DATASPACE_WITH_BOOKSTORE_DELTA_DATA, FUNCTIONAL_TEST_DATASPACE_3, BOOKSTORE_SCHEMA_SET, 'bookstoreAnchorForDeltaReport', bookstoreJsonDataForDeltaReport)
+ }
+
def restoreBookstoreDataAnchor(anchorNumber) {
def anchorName = 'bookstoreAnchor' + anchorNumber
cpsAdminService.deleteAnchor(FUNCTIONAL_TEST_DATASPACE_1, anchorName)
diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/functional/CpsDataServiceIntegrationSpec.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/functional/CpsDataServiceIntegrationSpec.groovy
index 12c97ed40..017ede7de 100644
--- a/integration-test/src/test/groovy/org/onap/cps/integration/functional/CpsDataServiceIntegrationSpec.groovy
+++ b/integration-test/src/test/groovy/org/onap/cps/integration/functional/CpsDataServiceIntegrationSpec.groovy
@@ -32,6 +32,7 @@ import org.onap.cps.spi.exceptions.DataNodeNotFoundException
import org.onap.cps.spi.exceptions.DataNodeNotFoundExceptionBatch
import org.onap.cps.spi.exceptions.DataValidationException
import org.onap.cps.spi.exceptions.DataspaceNotFoundException
+import org.onap.cps.spi.model.DeltaReport
import java.time.OffsetDateTime
@@ -432,6 +433,102 @@ class CpsDataServiceIntegrationSpec extends FunctionalSpecBase {
restoreBookstoreDataAnchor(2)
}
+ def 'Get delta between 2 anchors for when #scenario'() {
+ when: 'attempt to get delta report between anchors'
+ def result = objectUnderTest.getDeltaByDataspaceAndAnchors(FUNCTIONAL_TEST_DATASPACE_3, BOOKSTORE_ANCHOR_3, BOOKSTORE_ANCHOR_5, xpath, fetchDescendantOption)
+ then: 'delta report contains expected number of changes'
+ result.size() == 2
+ and: 'delta report contains expected action'
+ assert result.get(index).getAction() == expectedActions
+ and: 'delta report contains expected xpath'
+ assert result.get(index).getXpath() == expectedXpath
+ where: 'following data was used'
+ scenario | index | xpath || expectedActions || expectedXpath | fetchDescendantOption
+ 'a node is removed' | 0 | '/' || 'remove' || "/bookstore-address[@bookstore-name='Easons-1']" | OMIT_DESCENDANTS
+ 'a node is added' | 1 | '/' || 'add' || "/bookstore-address[@bookstore-name='Crossword Bookstores']" | OMIT_DESCENDANTS
+ }
+
+ def 'Get delta between 2 anchors where child nodes are added/removed but parent node remains unchanged'() {
+ def parentNodeXpath = "/bookstore"
+ when: 'attempt to get delta report between anchors'
+ def result = objectUnderTest.getDeltaByDataspaceAndAnchors(FUNCTIONAL_TEST_DATASPACE_3, BOOKSTORE_ANCHOR_3, BOOKSTORE_ANCHOR_5, parentNodeXpath, INCLUDE_ALL_DESCENDANTS)
+ then: 'delta report contains expected number of changes'
+ result.size() == 11
+ and: 'the delta report does not contain parent node xpath'
+ def xpaths = getDeltaReportEntities(result).get('xpaths')
+ assert !(xpaths.contains(parentNodeXpath))
+ }
+
+ def 'Get delta between 2 anchors returns empty response when #scenario'() {
+ when: 'attempt to get delta report between anchors'
+ def result = objectUnderTest.getDeltaByDataspaceAndAnchors(FUNCTIONAL_TEST_DATASPACE_3, sourceAnchor, targetAnchor, xpath, INCLUDE_ALL_DESCENDANTS)
+ then: 'delta report is empty'
+ assert result.isEmpty()
+ where: 'following data was used'
+ scenario | sourceAnchor | targetAnchor | xpath
+ 'anchors with identical data are queried' | BOOKSTORE_ANCHOR_3 | BOOKSTORE_ANCHOR_4 | '/'
+ 'same anchor name is passed as parameter' | BOOKSTORE_ANCHOR_3 | BOOKSTORE_ANCHOR_3 | '/'
+ 'non existing xpath' | BOOKSTORE_ANCHOR_3 | BOOKSTORE_ANCHOR_5 | '/non-existing-xpath'
+ }
+
+ def 'Get delta between anchors error scenario: #scenario'() {
+ when: 'attempt to get delta between anchors'
+ objectUnderTest.getDeltaByDataspaceAndAnchors(dataspaceName, sourceAnchor, targetAnchor, '/some-xpath', INCLUDE_ALL_DESCENDANTS)
+ then: 'expected exception is thrown'
+ thrown(expectedException)
+ where: 'following data was used'
+ scenario | dataspaceName | sourceAnchor | targetAnchor || expectedException
+ 'invalid dataspace name' | 'Invalid dataspace' | 'not-relevant' | 'not-relevant' || DataValidationException
+ 'invalid anchor 1 name' | FUNCTIONAL_TEST_DATASPACE_3 | 'invalid anchor' | 'not-relevant' || DataValidationException
+ 'invalid anchor 2 name' | FUNCTIONAL_TEST_DATASPACE_3 | BOOKSTORE_ANCHOR_3 | 'invalid anchor' || DataValidationException
+ 'non-existing dataspace' | 'non-existing' | 'not-relevant1' | 'not-relevant2' || DataspaceNotFoundException
+ 'non-existing dataspace with same anchor name' | 'non-existing' | 'not-relevant' | 'not-relevant' || DataspaceNotFoundException
+ 'non-existing anchor 1' | FUNCTIONAL_TEST_DATASPACE_3 | 'non-existing-anchor' | 'not-relevant' || AnchorNotFoundException
+ 'non-existing anchor 2' | FUNCTIONAL_TEST_DATASPACE_3 | BOOKSTORE_ANCHOR_3 | 'non-existing-anchor' || AnchorNotFoundException
+ }
+
+ def 'Get delta between anchors for remove action, where source data node #scenario'() {
+ when: 'attempt to get delta between leaves of data nodes present in 2 anchors'
+ def result = objectUnderTest.getDeltaByDataspaceAndAnchors(FUNCTIONAL_TEST_DATASPACE_3, BOOKSTORE_ANCHOR_5, BOOKSTORE_ANCHOR_3, parentNodeXpath, INCLUDE_ALL_DESCENDANTS)
+ then: 'expected action is present in delta report'
+ assert result.get(0).getAction() == 'remove'
+ where: 'following data was used'
+ scenario | parentNodeXpath
+ 'has leaves and child nodes' | "/bookstore/categories[@code='6']"
+ 'has leaves only' | "/bookstore/categories[@code='5']/books[@title='Book 11']"
+ 'has child data node only' | "/bookstore/support-info/contact-emails"
+ 'is empty' | "/bookstore/container-without-leaves"
+ }
+
+ def 'Get delta between anchors for add action, where target data node #scenario'() {
+ when: 'attempt to get delta between leaves of data nodes present in 2 anchors'
+ def result = objectUnderTest.getDeltaByDataspaceAndAnchors(FUNCTIONAL_TEST_DATASPACE_3, BOOKSTORE_ANCHOR_3, BOOKSTORE_ANCHOR_5, parentNodeXpath, INCLUDE_ALL_DESCENDANTS)
+ then: 'the expected action is present in delta report'
+ result.get(0).getAction() == 'add'
+ and: 'the expected xapth is present in delta report'
+ result.get(0).getXpath() == parentNodeXpath
+ where: 'following data was used'
+ scenario | parentNodeXpath
+ 'has leaves and child nodes' | "/bookstore/categories[@code='6']"
+ 'has leaves only' | "/bookstore/categories[@code='5']/books[@title='Book 11']"
+ 'has child data node only' | "/bookstore/support-info/contact-emails"
+ 'is empty' | "/bookstore/container-without-leaves"
+ }
+
+ def getDeltaReportEntities(List<DeltaReport> deltaReport) {
+ def xpaths = []
+ def action = []
+ def sourcePayload = []
+ def targetPayload = []
+ deltaReport.each {
+ delta -> xpaths.add(delta.getXpath())
+ action.add(delta.getAction())
+ sourcePayload.add(delta.getSourceData())
+ targetPayload.add(delta.getTargetData())
+ }
+ return ['xpaths':xpaths, 'action':action, 'sourcePayload':sourcePayload, 'targetPayload':targetPayload]
+ }
+
def countDataNodesInBookstore() {
return countDataNodesInTree(objectUnderTest.getDataNodes(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, '/bookstore', INCLUDE_ALL_DESCENDANTS))
}
diff --git a/integration-test/src/test/resources/data/bookstore/bookstore.yang b/integration-test/src/test/resources/data/bookstore/bookstore.yang
index 6f60f1981..9c6c42e28 100644
--- a/integration-test/src/test/resources/data/bookstore/bookstore.yang
+++ b/integration-test/src/test/resources/data/bookstore/bookstore.yang
@@ -49,6 +49,17 @@ module stores {
}
}
+ container support-info {
+ leaf support-office {
+ type string;
+ }
+ container contact-emails {
+ leaf email {
+ type string;
+ }
+ }
+ }
+
container container-without-leaves { }
container premises {
diff --git a/integration-test/src/test/resources/data/bookstore/bookstoreDataForDeltaReport.json b/integration-test/src/test/resources/data/bookstore/bookstoreDataForDeltaReport.json
new file mode 100644
index 000000000..73b84fc98
--- /dev/null
+++ b/integration-test/src/test/resources/data/bookstore/bookstoreDataForDeltaReport.json
@@ -0,0 +1,192 @@
+{
+ "bookstore-address": [
+ {
+ "bookstore-name": "Crossword Bookstores",
+ "address": "Bangalore, India",
+ "postal-code": "560062"
+ }
+ ],
+ "bookstore": {
+ "bookstore-name": "Easons",
+ "premises": {
+ "addresses": [
+ {
+ "house-number": 2,
+ "street": "Main Street",
+ "town": "Killarney",
+ "county": "Kerry"
+ },
+ {
+ "house-number": 24,
+ "street": "Grafton Street",
+ "town": "Dublin",
+ "county": "Dublin"
+ }
+ ]
+ },
+ "support-info": {
+ "contact-emails": {
+ }
+ },
+ "container-without-leaves": { },
+ "categories": [
+ {
+ "code": 1,
+ "name": "Kids",
+ "books" : [
+ {
+ "title": "Matilda",
+ "lang": "English",
+ "authors": ["Roald Dahl"],
+ "editions": [1988, 2000, 2023],
+ "price": 200
+ },
+ {
+ "title": "The Gruffalo",
+ "lang": "English/German",
+ "authors": ["Julia Donaldson"],
+ "editions": [1999],
+ "price": 15
+ }
+ ]
+ },
+ {
+ "code": 2,
+ "name": "Suspense"
+ },
+ {
+ "code": 3,
+ "name": "Comedy",
+ "books" : [
+ {
+ "title": "Good Omens",
+ "lang": "English",
+ "authors": ["Neil Gaiman", "Terry Pratchett"],
+ "editions": [2006],
+ "price": 13
+ },
+ {
+ "title": "The Colour of Magic",
+ "lang": "English",
+ "authors": ["Terry Pratchett"],
+ "editions": [1983],
+ "price": 12
+ },
+ {
+ "title": "The Light Fantastic",
+ "lang": "English",
+ "authors": ["Terry Pratchett"],
+ "editions": [1986],
+ "price": 14
+ },
+ {
+ "title": "A Book with No Language",
+ "lang": "",
+ "authors": ["Joe Bloggs"],
+ "editions": [2023],
+ "price": 20
+ }
+ ]
+ },
+ {
+ "code": 5,
+ "name": "Discount books",
+ "books" : [
+ {
+ "title": "Book 1",
+ "lang": "blah",
+ "authors": [],
+ "editions": [],
+ "price": 1
+ },
+ {
+ "title": "Book 2",
+ "lang": "blah",
+ "authors": [],
+ "editions": [],
+ "price": 2
+ },
+ {
+ "title": "Book 3",
+ "lang": "blah",
+ "authors": [],
+ "editions": [],
+ "price": 3
+ },
+ {
+ "title": "Book 4",
+ "lang": "blah",
+ "authors": [],
+ "editions": [],
+ "price": 4
+ },
+ {
+ "title": "Book 5",
+ "lang": "blah",
+ "authors": [],
+ "editions": [],
+ "price": 5
+ },
+ {
+ "title": "Book 6",
+ "lang": "blah",
+ "authors": [],
+ "editions": [],
+ "price": 6
+ },
+ {
+ "title": "Book 7",
+ "lang": "blah",
+ "authors": [],
+ "editions": [],
+ "price": 7
+ },
+ {
+ "title": "Book 8",
+ "lang": "blahh",
+ "authors": [],
+ "editions": [],
+ "price": 8
+ },
+ {
+ "title": "Book 9",
+ "lang": "blah",
+ "authors": [],
+ "editions": [],
+ "price": 9
+ },
+ {
+ "title": "Book 10",
+ "lang": "blah",
+ "authors": [],
+ "editions": [],
+ "price": 10
+ },
+ {
+ "title": "Book 11",
+ "lang": "blah",
+ "authors": [],
+ "editions": [],
+ "price": 10
+ }
+ ]
+ },
+ {
+ "code": 6,
+ "name": "Random books",
+ "books": [
+ {
+ "title": "Book 1",
+ "lang": "blah",
+ "authors": [],
+ "editions": [],
+ "price": 1
+ }
+ ]
+ },
+ {
+ "code": 7
+ }
+ ]
+ }
+}