2 * ============LICENSE_START=======================================================
3 * Copyright (C) 2023-2025 OpenInfra Foundation Europe. All rights reserved.
4 * Modifications Copyright (C) 2024 TechMahindra Ltd.
5 * ================================================================================
6 * Licensed under the Apache License, Version 2.0 (the 'License');
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an 'AS IS' BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
18 * SPDX-License-Identifier: Apache-2.0
19 * ============LICENSE_END=========================================================
22 package org.onap.cps.integration.performance.cps
24 import java.time.OffsetDateTime
25 import org.onap.cps.api.CpsDataService
26 import org.onap.cps.utils.ContentType
27 import org.onap.cps.integration.performance.base.CpsPerfTestBase
29 import static org.onap.cps.api.parameters.FetchDescendantsOption.OMIT_DESCENDANTS
31 class UpdatePerfTest extends CpsPerfTestBase {
32 static final def UPDATE_TEST_ANCHOR = 'updateTestAnchor'
33 static final def INNER_NODE_JSON = readResourceDataFile('openroadm/innerNode.json')
35 CpsDataService objectUnderTest
36 def now = OffsetDateTime.now()
38 def setup() { objectUnderTest = cpsDataService }
40 def 'Test setup for CPS Update API.'() {
41 given: 'an anchor and empty container node for OpenROADM devices'
42 cpsAnchorService.createAnchor(CPS_PERFORMANCE_TEST_DATASPACE, LARGE_SCHEMA_SET, UPDATE_TEST_ANCHOR)
43 cpsDataService.saveData(CPS_PERFORMANCE_TEST_DATASPACE, UPDATE_TEST_ANCHOR,
44 '{ "openroadm-devices": { "openroadm-device": []}}', now)
45 expect: 'no device nodes exist yet'
46 assert 0 == countDataNodes('/openroadm-devices/openroadm-device')
49 def 'JVM warm up for update tests: #scenario.'() {
50 given: 'replacement JSON for node containing list of device nodes'
51 def jsonData = '{ "openroadm-devices": ' + generateJsonForOpenRoadmDevices(startId, numberOfNodesAfterUpdate, changeLeaves) + '}'
52 when: 'the container node is updated'
53 objectUnderTest.updateDataNodeAndDescendants(CPS_PERFORMANCE_TEST_DATASPACE, UPDATE_TEST_ANCHOR, '/', jsonData, now, ContentType.JSON)
54 then: 'there are the expected number of total nodes'
55 assert numberOfNodesAfterUpdate == countDataNodes('/openroadm-devices/openroadm-device')
57 scenario | startId | changeLeaves || numberOfNodesAfterUpdate
58 'Replace 0 nodes with 100' | 1 | false || 100
59 'Replace 100 using same data' | 1 | false || 100
60 'Replace 100 with new leaf values' | 1 | true || 100
61 'Replace 100 with 100 new nodes' | 101 | false || 100
62 'Replace 50 existing and 50 new' | 151 | true || 100
63 'Replace 100 nodes with 0' | 1 | false || 0
66 def 'Replace single data node and descendants: #scenario.'() {
67 given: 'replacement JSON for node containing list of device nodes'
68 def jsonData = '{ "openroadm-devices": ' + generateJsonForOpenRoadmDevices(startId, totalNodes, changeLeaves) + '}'
69 when: 'the container node is updated'
71 objectUnderTest.updateDataNodeAndDescendants(CPS_PERFORMANCE_TEST_DATASPACE, UPDATE_TEST_ANCHOR, '/', jsonData, now, ContentType.JSON)
73 then: 'there are the expected number of total nodes'
74 assert totalNodes == countDataNodes('/openroadm-devices/openroadm-device')
75 and: 'data leaves have expected values'
76 assert totalNodes == countDataNodes(changeLeaves? '/openroadm-devices/openroadm-device[@status="fail"]'
77 : '/openroadm-devices/openroadm-device[@status="success"]')
78 and: 'update completes within expected time and a margin of 100%'
79 recordAndAssertResourceUsage("CPS:${scenario}", expectedTime, resourceMeter.getTotalTimeInSeconds(), resourceMeter.getTotalMemoryUsageInMB(), referenceGraph)
81 scenario | totalNodes | startId | changeLeaves || expectedTime | referenceGraph
82 'Replace 0 nodes with 100' | 100 | 1 | false || 3.0 | false
83 'Replace 100 using same data' | 100 | 1 | false || 5.0 | false
84 'Replace 100 with new leaf values' | 100 | 1 | true || 5.4 | false
85 'Replace 100 with 100 new nodes' | 100 | 101 | false || 9.3 | true
86 'Replace 50 existing and 50 new' | 100 | 151 | true || 7.3 | false
87 'Replace 100 nodes with 0' | 0 | 1 | false || 6.0 | false
90 def 'Replace list content: #scenario.'() {
91 given: 'replacement JSON for list of device nodes'
92 def jsonListData = generateJsonForOpenRoadmDevices(startId, totalNodes, changeLeaves)
93 when: 'the container node is updated'
95 objectUnderTest.replaceListContent(CPS_PERFORMANCE_TEST_DATASPACE, UPDATE_TEST_ANCHOR, '/openroadm-devices', jsonListData, now, ContentType.JSON)
97 then: 'there are the expected number of total nodes'
98 assert totalNodes == countDataNodes('/openroadm-devices/openroadm-device')
99 and: 'data leaves have expected values'
100 assert totalNodes == countDataNodes(changeLeaves? '/openroadm-devices/openroadm-device[@status="fail"]'
101 : '/openroadm-devices/openroadm-device[@status="success"]')
102 and: 'update completes within expected time and a margin of 100%'
103 recordAndAssertResourceUsage("CPS:${scenario}", expectedTime, resourceMeter.getTotalTimeInSeconds(), resourceMeter.getTotalMemoryUsageInMB())
105 scenario | totalNodes | startId | changeLeaves || expectedTime
106 'Replace list of 0 with 100' | 100 | 1 | false || 3.0
107 'Replace list of 100 using same data' | 100 | 1 | false || 5.0
108 'Replace list of 100 with new leaf values' | 100 | 1 | true || 5.4
109 'Replace list with 100 new nodes' | 100 | 101 | false || 9.0
110 'Replace list with 50 existing and 50 new' | 100 | 151 | true || 7.0
111 'Replace list of 100 nodes with 1' | 1 | 1 | false || 5.9
114 def 'Update leaves for 100 data nodes.'() {
115 given: 'there are 200 existing data nodes'
116 def jsonListData = generateJsonForOpenRoadmDevices(1, 200, false)
117 objectUnderTest.replaceListContent(CPS_PERFORMANCE_TEST_DATASPACE, UPDATE_TEST_ANCHOR, '/openroadm-devices', jsonListData, now, ContentType.JSON)
118 and: 'JSON for updated data leaves of 100 nodes'
119 def jsonDataUpdated = "{'openroadm-device':[" + (1..100).collect {"{'device-id':'C201-7-1A-" + it + "','status':'fail','ne-state':'jeopardy'}" }.join(",") + "]}"
120 when: 'update is performed for leaves'
121 resourceMeter.start()
122 objectUnderTest.updateNodeLeaves(CPS_PERFORMANCE_TEST_DATASPACE, UPDATE_TEST_ANCHOR, "/openroadm-devices", jsonDataUpdated, now, ContentType.JSON)
124 then: 'data leaves have expected values'
125 assert 100 == countDataNodes('/openroadm-devices/openroadm-device[@status="fail"]')
126 and: 'update completes within expected time and a margin of 100%'
127 recordAndAssertResourceUsage('CPS:Update leaves for 100 data nodes', 0.22, resourceMeter.getTotalTimeInSeconds(), resourceMeter.getTotalMemoryUsageInMB())
130 def 'Clean up for CPS Update API.'() {
131 cleanup: 'test anchor and data nodes'
132 cpsAnchorService.deleteAnchor(CPS_PERFORMANCE_TEST_DATASPACE, UPDATE_TEST_ANCHOR)
135 def generateJsonForOpenRoadmDevices(int startId, int totalNodes, boolean changeLeaves) {
136 return '{ "openroadm-device": [' + (0..<totalNodes).collect {makeInnerNodeJson(it + startId, changeLeaves) }.join(',') + ']}}'
139 def makeInnerNodeJson(int nodeId, boolean changeLeaf) {
140 def nodeJson = INNER_NODE_JSON.replace('NODE_ID_HERE', nodeId.toString())
142 nodeJson = nodeJson.replace('"status": "success"', '"status": "fail"')
147 def countDataNodes(String cpsPathQuery) {
148 return cpsQueryService.queryDataNodes(CPS_PERFORMANCE_TEST_DATASPACE, UPDATE_TEST_ANCHOR, cpsPathQuery, OMIT_DESCENDANTS).size()