Performance tests for CPS Delta APIs 23/141623/13
authorArpit Singh <AS00745003@techmahindra.com>
Tue, 19 Aug 2025 11:39:48 +0000 (17:09 +0530)
committerArpit Singh <AS00745003@techmahindra.com>
Wed, 1 Oct 2025 07:21:40 +0000 (12:51 +0530)
- Performance tests for two Delta APIs
- Delta Between 2 anchors
- Delta between an anchor and JSON payload
- Tests are inclusive of old and new delta report formats.
- Added a method to generate anchors with modified openroadm data
- Added a method to reset test data for an anchor

Issue-ID: CPS-2839
Change-Id: I8ec3a265c5fa02b8cd9060b65fee79a6ca9366e0
Signed-off-by: Arpit Singh <AS00745003@techmahindra.com>
integration-test/src/test/groovy/org/onap/cps/integration/performance/base/CpsPerfTestBase.groovy
integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/DeltaPerfTest.groovy [new file with mode: 0644]

index f2d3efd..0a1badf 100644 (file)
 
 package org.onap.cps.integration.performance.base
 
+import groovy.json.JsonOutput
+import groovy.json.JsonSlurper
+import org.onap.cps.api.parameters.FetchDescendantsOption
 import org.onap.cps.integration.ResourceMeter
 import org.onap.cps.rest.utils.MultipartFileUtil
-import org.onap.cps.api.parameters.FetchDescendantsOption
 import org.onap.cps.utils.ContentType
 import org.springframework.web.multipart.MultipartFile
 
+import java.time.OffsetDateTime
+
 class CpsPerfTestBase extends PerfTestBase {
 
     static final def CPS_PERFORMANCE_TEST_DATASPACE = 'cpsPerformanceDataspace'
@@ -52,6 +56,16 @@ class CpsPerfTestBase extends PerfTestBase {
     def createInitialData() {
         addOpenRoadModel()
         addOpenRoadData()
+        addModifiedOpenRoadData()
+    }
+
+    def 'CPS pre-load test data'() {
+        when: 'dummy get data nodes runs so that populating the DB does not get included in other test timings'
+            resourceMeter.start()
+            def result = cpsDataService.getDataNodes(CPS_PERFORMANCE_TEST_DATASPACE, 'openroadm1', '/', FetchDescendantsOption.OMIT_DESCENDANTS)
+            resourceMeter.stop()
+        then: 'expected data exists'
+            assert result.xpath == ['/openroadm-devices']
     }
 
     def addOpenRoadModel() {
@@ -78,13 +92,63 @@ class CpsPerfTestBase extends PerfTestBase {
             ']}}'
     }
 
-    def 'CPS pre-load test data'() {
-        when: 'dummy get data nodes runs so that populating the DB does not get included in other test timings'
-            resourceMeter.start()
-            def result = cpsDataService.getDataNodes(CPS_PERFORMANCE_TEST_DATASPACE, 'openroadm1', '/', FetchDescendantsOption.OMIT_DESCENDANTS)
-            resourceMeter.stop()
-        then: 'expected data exists'
-            assert result.xpath == ['/openroadm-devices']
+    def addModifiedOpenRoadData() {
+        def data = generateModifiedOpenRoadData(OPENROADM_DEVICES_PER_ANCHOR, 200, 200, 200)
+        resourceMeter.start()
+        addAnchorsWithData(1, CPS_PERFORMANCE_TEST_DATASPACE, LARGE_SCHEMA_SET, 'openroadm-modified', data, ContentType.JSON)
+        resourceMeter.stop()
+        def durationInSeconds = resourceMeter.getTotalTimeInSeconds()
+        recordAndAssertResourceUsage('CPS:Creating modified openroadm anchor with large data tree', 100, durationInSeconds, resourceMeter.getTotalMemoryUsageInMB(), false)
+    }
+
+    def generateModifiedOpenRoadData(numberOfNodes, removeNodesCount, addNodesCount, updateCount) {
+        def innerNode = readResourceDataFile('openroadm/innerNode.json')
+        def allIndices = (0..<numberOfNodes).toList()
+        def nodeIdsAfterRemove = removeNodes(allIndices, removeNodesCount)
+        def newNodeIds = addNodes(nodeIdsAfterRemove, addNodesCount)
+        def nodeIds = (nodeIdsAfterRemove + newNodeIds).collect {
+            innerNode.replace('NODE_ID_HERE', it.toString())
+        }
+        def updatedNodes = updateNodes(nodeIds, innerNode, updateCount)
+        return '{ "openroadm-devices": { "openroadm-device": [' +
+            updatedNodes.collect {
+                it.toString()
+            }.join(',') +
+            ']}}'
+    }
+
+    def removeNodes(allIndice, removeNodesCount) {
+        def indicesToRemove = allIndice.findAll{
+            it % 2 == 0
+        }.take(removeNodesCount)
+        return (allIndice - indicesToRemove).collect { it + 1 }
+    }
+
+    def addNodes(nodeIds, addNodesCount) {
+        def maxNodeId = nodeIds ? nodeIds.max() : 0
+        return ((maxNodeId + 1)..(maxNodeId + addNodesCount))
+    }
+
+    def updateNodes(nodeIds, innerNode, updateCount) {
+        def slurper = new JsonSlurper()
+        nodeIds.withIndex().collect { data, idx ->
+            def jsonNode = slurper.parseText(data)
+            if (idx < updateCount) {
+                jsonNode['status'] = 'fail'
+                def childNode = jsonNode['org-openroadm-device']['degree'][0]
+                if (childNode) {
+                    childNode['max-wavelengths'] += 100
+                }
+            }
+            return JsonOutput.toJson(jsonNode)
+        }
+    }
+
+    def resetTestAnchorData() {
+        cpsAnchorService.deleteAnchor(CPS_PERFORMANCE_TEST_DATASPACE, 'openroadm1')
+        def data = generateOpenRoadData(OPENROADM_DEVICES_PER_ANCHOR)
+        cpsAnchorService.createAnchor(CPS_PERFORMANCE_TEST_DATASPACE, LARGE_SCHEMA_SET, 'openroadm1')
+        cpsDataService.saveData(CPS_PERFORMANCE_TEST_DATASPACE, 'openroadm1', data, OffsetDateTime.now())
     }
 
 }
diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/DeltaPerfTest.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/DeltaPerfTest.groovy
new file mode 100644 (file)
index 0000000..02eecb6
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  Copyright (C) 2025 TechMahindra Ltd.
+ *  ================================================================================
+ *  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.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.integration.performance.cps
+
+
+import org.onap.cps.api.CpsDeltaService
+import org.onap.cps.integration.performance.base.CpsPerfTestBase
+
+import static org.onap.cps.api.parameters.FetchDescendantsOption.DIRECT_CHILDREN_ONLY
+import static org.onap.cps.api.parameters.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS
+import static org.onap.cps.api.parameters.FetchDescendantsOption.OMIT_DESCENDANTS
+
+class DeltaPerfTest extends CpsPerfTestBase{
+    CpsDeltaService objectUnderTest
+    def setup() {
+        objectUnderTest = cpsDeltaService
+    }
+
+    def jsonPayload = generateModifiedOpenRoadData(1000, 200, 200, 200)
+
+    def 'Get delta between 2 anchors wtih grouping enabled and #scenario'() {
+        when: 'attempt to get delta between two 2 anchors'
+            resourceMeter.start()
+            10.times {
+                objectUnderTest.getDeltaByDataspaceAndAnchors(CPS_PERFORMANCE_TEST_DATASPACE, 'openroadm1', 'openroadm-modified1', xpath, fetchDescendantsOption, true)
+            }
+            resourceMeter.stop()
+            def durationInSeconds = resourceMeter.getTotalTimeInSeconds()
+        then: 'the delta is returned and operation completes within expected time'
+            recordAndAssertResourceUsage('CPS:Get delta between 2 anchors', expectedDuration, durationInSeconds, resourceMeter.getTotalMemoryUsageInMB())
+        where: 'the following parameters are used'
+            scenario             | xpath                                                             | fetchDescendantsOption  || expectedDuration
+            'no descendants'     | '/openroadm-devices/openroadm-device[@device-id=\'C201-7-1A-1\']' | OMIT_DESCENDANTS        || 0.5
+            'direct descendants' | '/'                                                               | DIRECT_CHILDREN_ONLY    || 1.5
+            'all descendants'    | '/'                                                               | INCLUDE_ALL_DESCENDANTS || 15.0
+    }
+
+    def 'Get delta between 2 anchors with grouping disabled and #scenario'() {
+        when: 'attempt to get delta between two 2 anchors with grouping disabled'
+            resourceMeter.start()
+            10.times {
+                objectUnderTest.getDeltaByDataspaceAndAnchors(CPS_PERFORMANCE_TEST_DATASPACE, 'openroadm1', 'openroadm-modified1', xpath, fetchDescendantsOption, false)
+            }
+            resourceMeter.stop()
+            def durationInSeconds = resourceMeter.getTotalTimeInSeconds()
+        then: 'the delta is returned and operation completes within expected time'
+            recordAndAssertResourceUsage('CPS:Get delta between 2 anchors with grouping disabled', expectedDuration, durationInSeconds, resourceMeter.getTotalMemoryUsageInMB())
+        where: 'the following parameters are used'
+            scenario             | xpath                                                             | fetchDescendantsOption  || expectedDuration
+            'no descendants'     | '/openroadm-devices/openroadm-device[@device-id=\'C201-7-1A-1\']' | OMIT_DESCENDANTS        || 0.3
+            'direct descendants' | '/openroadm-devices'                                              | DIRECT_CHILDREN_ONLY    || 0.5
+            'all descendants'    | '/openroadm-devices'                                              | INCLUDE_ALL_DESCENDANTS || 12.0
+    }
+
+    def 'Get delta between an anchor and JSON payload with grouping enabled and #scenario'() {
+        when: 'attempt to get delta between an anchor and JSON payload with grouping enabled'
+            resourceMeter.start()
+            objectUnderTest.getDeltaByDataspaceAnchorAndPayload(CPS_PERFORMANCE_TEST_DATASPACE, 'openroadm1', '/openroadm-devices', Collections.emptyMap(), jsonPayload, fetchDescendantsOption, true)
+            resourceMeter.stop()
+            def durationInSeconds = resourceMeter.getTotalTimeInSeconds()
+        then: 'the delta is returned and operation completes within expected time'
+            recordAndAssertResourceUsage('CPS:Get delta between an anchor and JSON payload with grouping enabled', expectedDuration, durationInSeconds, resourceMeter.getTotalMemoryUsageInMB())
+        where: 'the following parameters are used'
+            scenario             | fetchDescendantsOption  || expectedDuration
+            'no descendants'     | OMIT_DESCENDANTS        || 2.0
+            'direct descendants' | DIRECT_CHILDREN_ONLY    || 2.0
+            'all descendants'    | INCLUDE_ALL_DESCENDANTS || 3.5
+    }
+
+    def 'Get delta between an anchor and JSON payload with grouping disabled'() {
+        when: 'attempt to get delta between an anchor and JSON payload with grouping disabled'
+            resourceMeter.start()
+            objectUnderTest.getDeltaByDataspaceAnchorAndPayload(CPS_PERFORMANCE_TEST_DATASPACE, 'openroadm1', '/openroadm-devices', Collections.emptyMap(), jsonPayload, INCLUDE_ALL_DESCENDANTS, false)
+            resourceMeter.stop()
+            def durationInSeconds = resourceMeter.getTotalTimeInSeconds()
+        then: 'the delta is returned and operation completes within expected time'
+            recordAndAssertResourceUsage('CPS:Get delta between an anchor and JSON payload with grouping disabled', expectedDuration, durationInSeconds, resourceMeter.getTotalMemoryUsageInMB())
+        where: 'the following parameters are used'
+            scenario             | fetchDescendantsOption  || expectedDuration
+            'no descendants'     | OMIT_DESCENDANTS        || 3.0
+            'direct descendants' | DIRECT_CHILDREN_ONLY    || 3.0
+            'all descendants'    | INCLUDE_ALL_DESCENDANTS || 3.5
+    }
+
+    def 'Apply delta report to an anchor'() {
+        given: 'a delta report between 2 anchors'
+            def deltaReport = objectUnderTest.getDeltaByDataspaceAndAnchors(CPS_PERFORMANCE_TEST_DATASPACE, 'openroadm1', 'openroadm-modified1', '/openroadm-devices', INCLUDE_ALL_DESCENDANTS, true)
+            def deltaReportAsJson = jsonObjectMapper.asJsonString(deltaReport)
+        when: 'attempt to apply the delta report to an anchor'
+            resourceMeter.start()
+            objectUnderTest.applyChangesInDeltaReport(CPS_PERFORMANCE_TEST_DATASPACE, 'openroadm1', deltaReportAsJson)
+            resourceMeter.stop()
+            resetTestAnchorData()
+            def durationInSeconds = resourceMeter.getTotalTimeInSeconds()
+        then: 'the delta is applied and operation completes within expected time'
+            recordAndAssertResourceUsage('CPS:Apply delta report to an anchor', 8.0, durationInSeconds, resourceMeter.getTotalMemoryUsageInMB())
+    }
+}