2 * ============LICENSE_START=======================================================
3 * Copyright (C) 2023 Nordix Foundation
4 * ================================================================================
5 * Licensed under the Apache License, Version 2.0 (the 'License');
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an 'AS IS' BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
17 * SPDX-License-Identifier: Apache-2.0
18 * ============LICENSE_END=========================================================
21 package org.onap.cps.integration.performance.cps
23 import java.time.OffsetDateTime
24 import org.onap.cps.api.CpsDataService
25 import org.onap.cps.integration.performance.base.CpsPerfTestBase
27 import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS
29 class UpdatePerfTest extends CpsPerfTestBase {
30 static final def UPDATE_TEST_ANCHOR = 'updateTestAnchor'
31 static final def INNER_NODE_JSON = readResourceDataFile('openroadm/innerNode.json')
33 CpsDataService objectUnderTest
34 def now = OffsetDateTime.now()
36 def setup() { objectUnderTest = cpsDataService }
38 def 'Test setup for CPS Update API.'() {
39 given: 'an anchor and empty container node for OpenROADM devices'
40 cpsAnchorService.createAnchor(CPS_PERFORMANCE_TEST_DATASPACE, LARGE_SCHEMA_SET, UPDATE_TEST_ANCHOR)
41 cpsDataService.saveData(CPS_PERFORMANCE_TEST_DATASPACE, UPDATE_TEST_ANCHOR,
42 '{ "openroadm-devices": { "openroadm-device": []}}', now)
43 expect: 'no device nodes exist yet'
44 assert 0 == countDataNodes('/openroadm-devices/openroadm-device')
47 def 'JVM warm up for update tests: #scenario.'() {
48 given: 'replacement JSON for node containing list of device nodes'
49 def jsonData = '{ "openroadm-devices": ' + generateJsonForOpenRoadmDevices(startId, totalNodes, changeLeaves) + '}'
50 when: 'the container node is updated'
51 objectUnderTest.updateDataNodeAndDescendants(CPS_PERFORMANCE_TEST_DATASPACE, UPDATE_TEST_ANCHOR, '/', jsonData, now)
52 then: 'there are the expected number of total nodes'
53 assert totalNodes == countDataNodes('/openroadm-devices/openroadm-device')
55 scenario | totalNodes | startId | changeLeaves
56 'Replace 0 nodes with 100' | 100 | 1 | false
57 'Replace 100 using same data' | 100 | 1 | false
58 'Replace 100 with new leaf values' | 100 | 1 | true
59 'Replace 100 with 100 new nodes' | 100 | 101 | false
60 'Replace 50 existing and 50 new' | 100 | 151 | true
61 'Replace 100 nodes with 0' | 0 | 1 | false
64 def 'Replace single data node and descendants: #scenario.'() {
65 given: 'replacement JSON for node containing list of device nodes'
66 def jsonData = '{ "openroadm-devices": ' + generateJsonForOpenRoadmDevices(startId, totalNodes, changeLeaves) + '}'
67 when: 'the container node is updated'
69 objectUnderTest.updateDataNodeAndDescendants(CPS_PERFORMANCE_TEST_DATASPACE, UPDATE_TEST_ANCHOR, '/', jsonData, now)
71 then: 'there are the expected number of total nodes'
72 assert totalNodes == countDataNodes('/openroadm-devices/openroadm-device')
73 and: 'data leaves have expected values'
74 assert totalNodes == countDataNodes(changeLeaves? '/openroadm-devices/openroadm-device[@status="fail"]'
75 : '/openroadm-devices/openroadm-device[@status="success"]')
76 and: 'update completes within expected time and memory used is within limit'
77 recordAndAssertResourceUsage(scenario,
78 timeLimit, resourceMeter.getTotalTimeInSeconds(),
79 memoryLimit, resourceMeter.getTotalMemoryUsageInMB())
81 scenario | totalNodes | startId | changeLeaves || timeLimit | memoryLimit
82 'Replace 0 nodes with 100' | 100 | 1 | false || 7 | 250
83 'Replace 100 using same data' | 100 | 1 | false || 5 | 250
84 'Replace 100 with new leaf values' | 100 | 1 | true || 5 | 250
85 'Replace 100 with 100 new nodes' | 100 | 101 | false || 12 | 300
86 'Replace 50 existing and 50 new' | 100 | 151 | true || 8 | 250
87 'Replace 100 nodes with 0' | 0 | 1 | false || 5 | 250
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)
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 memory used is within limit'
103 recordAndAssertResourceUsage(scenario,
104 timeLimit, resourceMeter.getTotalTimeInSeconds(),
105 memoryLimit, resourceMeter.getTotalMemoryUsageInMB())
107 scenario | totalNodes | startId | changeLeaves || timeLimit | memoryLimit
108 'Replace list of 0 with 100' | 100 | 1 | false || 7 | 250
109 'Replace list of 100 using same data' | 100 | 1 | false || 5 | 250
110 'Replace list of 100 with new leaf values' | 100 | 1 | true || 5 | 250
111 'Replace list with 100 new nodes' | 100 | 101 | false || 12 | 300
112 'Replace list with 50 existing and 50 new' | 100 | 151 | true || 8 | 250
113 'Replace list of 100 nodes with 1' | 1 | 1 | false || 5 | 250
116 def 'Update leaves for 100 data nodes.'() {
117 given: 'there are 200 existing data nodes'
118 def jsonListData = generateJsonForOpenRoadmDevices(1, 200, false)
119 objectUnderTest.replaceListContent(CPS_PERFORMANCE_TEST_DATASPACE, UPDATE_TEST_ANCHOR, '/openroadm-devices', jsonListData, now)
120 and: 'JSON for updated data leaves of 100 nodes'
121 def jsonDataUpdated = "{'openroadm-device':[" + (1..100).collect {"{'device-id':'C201-7-1A-" + it + "','status':'fail','ne-state':'jeopardy'}" }.join(",") + "]}"
122 when: 'update is performed for leaves'
123 resourceMeter.start()
124 objectUnderTest.updateNodeLeaves(CPS_PERFORMANCE_TEST_DATASPACE, UPDATE_TEST_ANCHOR, "/openroadm-devices", jsonDataUpdated, now)
126 then: 'data leaves have expected values'
127 assert 100 == countDataNodes('/openroadm-devices/openroadm-device[@status="fail"]')
128 and: 'update completes within expected time and memory used is within limit'
129 recordAndAssertResourceUsage('Update leaves for 100 data nodes',
130 0.5, resourceMeter.getTotalTimeInSeconds(),
131 120, resourceMeter.getTotalMemoryUsageInMB())
134 def 'Clean up for CPS Update API.'() {
135 cleanup: 'test anchor and data nodes'
136 cpsAnchorService.deleteAnchor(CPS_PERFORMANCE_TEST_DATASPACE, UPDATE_TEST_ANCHOR)
139 def generateJsonForOpenRoadmDevices(int startId, int totalNodes, boolean changeLeaves) {
140 return '{ "openroadm-device": [' + (0..<totalNodes).collect {makeInnerNodeJson(it + startId, changeLeaves) }.join(',') + ']}}'
143 def makeInnerNodeJson(int nodeId, boolean changeLeaf) {
144 def nodeJson = INNER_NODE_JSON.replace('NODE_ID_HERE', nodeId.toString())
146 nodeJson = nodeJson.replace('"status": "success"', '"status": "fail"')
151 def countDataNodes(String cpsPathQuery) {
152 return cpsQueryService.queryDataNodes(CPS_PERFORMANCE_TEST_DATASPACE, UPDATE_TEST_ANCHOR, cpsPathQuery, OMIT_DESCENDANTS).size()