Merge "Add integration test for extending API: Get Module Definitions"
[cps.git] / integration-test / src / test / groovy / org / onap / cps / integration / functional / CpsDataServiceIntegrationSpec.groovy
index b107a87..6499653 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  ============LICENSE_START=======================================================
- *  Copyright (C) 2023 Nordix Foundation
+ *  Copyright (C) 2023-2024 Nordix Foundation
  *  Modifications Copyright (C) 2023 TechMahindra Ltd.
  *  ================================================================================
  *  Licensed under the Apache License, Version 2.0 (the 'License');
@@ -276,12 +276,11 @@ class CpsDataServiceIntegrationSpec extends FunctionalSpecBase {
             assert originalCountBookstoreChildNodes == countDataNodesInBookstore()
     }
 
-    def 'Add and Delete a batch of lists (element) data nodes.'() {
-        given: 'two new (categories) data nodes in two separate batches'
-            def json1 = '{"categories": [ {"code":"new1"} ] }'
-            def json2 = '{"categories": [ {"code":"new2"} ] } '
+    def 'Add and Delete a batch of list element data nodes.'() {
+        given: 'two new (categories) data nodes in a single batch'
+            def json = '{"categories": [ {"code":"new1"}, {"code":"new2"} ] }'
         when: 'the batches of new list element(s) are saved'
-            objectUnderTest.saveListElementsBatch(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1 , '/bookstore', [json1, json2], now)
+            objectUnderTest.saveListElements(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1 , '/bookstore', json, now)
         then: 'they can be retrieved by their xpaths'
             assert objectUnderTest.getDataNodes(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, '/bookstore/categories[@code="new1"]', DIRECT_CHILDREN_ONLY).size() == 1
             assert objectUnderTest.getDataNodes(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, '/bookstore/categories[@code="new2"]', DIRECT_CHILDREN_ONLY).size() == 1
@@ -294,12 +293,11 @@ class CpsDataServiceIntegrationSpec extends FunctionalSpecBase {
             assert originalCountBookstoreChildNodes == countDataNodesInBookstore()
     }
 
-    def 'Add and Delete a batch of lists (element) data nodes with partial success.'() {
-        given: 'two new (categories) data nodes in two separate batches'
-            def jsonNewElement = '{"categories": [ {"code":"new1"} ] }'
-            def jsonExistingElement = '{"categories": [ {"code":"1"} ] } '
+    def 'Add and Delete a batch of list element data nodes with partial success.'() {
+        given: 'one existing and one new (categories) data nodes in a single batch'
+            def json = '{"categories": [ {"code":"new1"}, {"code":"1"} ] }'
         when: 'the batches of new list element(s) are saved'
-            objectUnderTest.saveListElementsBatch(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1 , '/bookstore', [jsonNewElement, jsonExistingElement], now)
+            objectUnderTest.saveListElements(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1 , '/bookstore', json, now)
         then: 'an already defined (batch) exception is thrown for the existing path'
             def exceptionThrown = thrown(AlreadyDefinedException)
             assert exceptionThrown.alreadyDefinedObjectNames ==  ['/bookstore/categories[@code=\'1\']' ] as Set
@@ -433,40 +431,30 @@ class CpsDataServiceIntegrationSpec extends FunctionalSpecBase {
 
     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)
+            def result = objectUnderTest.getDeltaByDataspaceAndAnchors(FUNCTIONAL_TEST_DATASPACE_3, BOOKSTORE_ANCHOR_3, BOOKSTORE_ANCHOR_5, '/', OMIT_DESCENDANTS)
         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))
+            result.size() == 3
+        and: 'delta report contains UPDATE action with expected xpath'
+            assert result[0].getAction() == 'update'
+            assert result[0].getXpath() == '/bookstore'
+        and: 'delta report contains REMOVE action with expected xpath'
+            assert result[1].getAction() == 'remove'
+            assert result[1].getXpath() == "/bookstore-address[@bookstore-name='Easons-1']"
+        and: 'delta report contains ADD action with expected xpath'
+            assert result[2].getAction() == 'add'
+            assert result[2].getXpath() == "/bookstore-address[@bookstore-name='Crossword Bookstores']"
     }
 
     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)
+            def result = objectUnderTest.getDeltaByDataspaceAndAnchors(FUNCTIONAL_TEST_DATASPACE_3, BOOKSTORE_ANCHOR_3, 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'
+            scenario                              | targetAnchor       | xpath
+        'anchors with identical data are queried' | BOOKSTORE_ANCHOR_4 | '/'
+        'same anchor name is passed as parameter' | BOOKSTORE_ANCHOR_3 | '/'
+        'non existing xpath'                      | BOOKSTORE_ANCHOR_5 | '/non-existing-xpath'
     }
 
     def 'Get delta between anchors error scenario: #scenario'() {
@@ -513,6 +501,63 @@ class CpsDataServiceIntegrationSpec extends FunctionalSpecBase {
             'is empty'                   | "/bookstore/container-without-leaves"
     }
 
+    def 'Get delta between anchors when leaves of existing data nodes are updated,: #scenario'() {
+        when: 'attempt to get delta between leaves of existing data nodes'
+            def result = objectUnderTest.getDeltaByDataspaceAndAnchors(FUNCTIONAL_TEST_DATASPACE_3, sourceAnchor, targetAnchor, xpath, OMIT_DESCENDANTS)
+        then: 'expected action is update'
+            assert result[0].getAction() == 'update'
+        and: 'the payload has expected leaf values'
+            def sourceData = result[0].getSourceData()
+            def targetData = result[0].getTargetData()
+            assert sourceData == expectedSourceValue
+            assert targetData == expectedTargetValue
+        where: 'following data was used'
+            scenario                           | sourceAnchor       | targetAnchor       | xpath                                                     || expectedSourceValue            | expectedTargetValue
+            'leaf is updated in target anchor' | BOOKSTORE_ANCHOR_3 | BOOKSTORE_ANCHOR_5 | '/bookstore'                                              || ['bookstore-name': 'Easons-1'] | ['bookstore-name': 'Crossword Bookstores']
+            'leaf is removed in target anchor' | BOOKSTORE_ANCHOR_3 | BOOKSTORE_ANCHOR_5 | "/bookstore/categories[@code='5']/books[@title='Book 1']" || [price:1]                      | null
+            'leaf is added in target anchor'   | BOOKSTORE_ANCHOR_5 | BOOKSTORE_ANCHOR_3 | "/bookstore/categories[@code='5']/books[@title='Book 1']" || null                           | [price:1]
+    }
+
+    def 'Get delta between anchors when child data nodes under existing parent data nodes are updated: #scenario'() {
+        when: 'attempt to get delta between leaves of existing data nodes'
+            def result = objectUnderTest.getDeltaByDataspaceAndAnchors(FUNCTIONAL_TEST_DATASPACE_3, sourceAnchor, targetAnchor, xpath, DIRECT_CHILDREN_ONLY)
+        then: 'expected action is update'
+            assert result[0].getAction() == 'update'
+        and: 'the delta report has expected child node xpaths'
+            def deltaReportEntities = getDeltaReportEntities(result)
+            def childNodeXpathsInDeltaReport = deltaReportEntities.get('xpaths')
+            assert childNodeXpathsInDeltaReport.contains(expectedChildNodeXpath)
+        where: 'following data was used'
+            scenario                                          | sourceAnchor       | targetAnchor       | xpath                 || expectedChildNodeXpath
+            'source and target anchors have child data nodes' | BOOKSTORE_ANCHOR_3 | BOOKSTORE_ANCHOR_5 | '/bookstore/premises' || '/bookstore/premises/addresses[@house-number=\'2\' and @street=\'Main Street\']'
+            'removed child data nodes in target anchor'       | BOOKSTORE_ANCHOR_5 | BOOKSTORE_ANCHOR_3 | '/bookstore'          || '/bookstore/support-info'
+            'added  child data nodes in target anchor'        | BOOKSTORE_ANCHOR_3 | BOOKSTORE_ANCHOR_5 | '/bookstore'          || '/bookstore/support-info'
+    }
+
+    def 'Get delta between anchors where source and target data nodes have leaves and child data nodes'() {
+        given: 'parent node xpath and expected data in delta report'
+            def parentNodeXpath = "/bookstore/categories[@code='1']"
+            def expectedSourceDataInParentNode = ['name':'Children']
+            def expectedTargetDataInParentNode = ['name':'Kids']
+            def expectedSourceDataInChildNode = [['lang' : 'English'],['price':20, 'editions':[1988, 2000]]]
+            def expectedTargetDataInChildNode = [['lang':'English/German'], ['price':200, 'editions':[2023, 1988, 2000]]]
+        when: 'attempt to get delta between leaves of existing data nodes'
+            def result = objectUnderTest.getDeltaByDataspaceAndAnchors(FUNCTIONAL_TEST_DATASPACE_3, BOOKSTORE_ANCHOR_3, BOOKSTORE_ANCHOR_5, parentNodeXpath, INCLUDE_ALL_DESCENDANTS)
+            def deltaReportEntities = getDeltaReportEntities(result)
+        then: 'expected action is update'
+            assert result[0].getAction() == 'update'
+        and: 'the payload has expected parent node xpath'
+            assert deltaReportEntities.get('xpaths').contains(parentNodeXpath)
+        and: 'delta report has expected source and target data'
+            assert deltaReportEntities.get('sourcePayload').contains(expectedSourceDataInParentNode)
+            assert deltaReportEntities.get('targetPayload').contains(expectedTargetDataInParentNode)
+        and: 'the delta report also has expected child node xpaths'
+            assert deltaReportEntities.get('xpaths').containsAll(["/bookstore/categories[@code='1']/books[@title='The Gruffalo']", "/bookstore/categories[@code='1']/books[@title='Matilda']"])
+        and: 'the delta report also has expected source and target data of child nodes'
+            assert deltaReportEntities.get('sourcePayload').containsAll(expectedSourceDataInChildNode)
+            //assert deltaReportEntities.get('targetPayload').containsAll(expectedTargetDataInChildNode) CPS-2057
+    }
+
     def getDeltaReportEntities(List<DeltaReport> deltaReport) {
         def xpaths = []
         def action = []