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'
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() {
']}}'
}
- 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())
}
}
--- /dev/null
+/*
+ * ============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())
+ }
+}