aboutsummaryrefslogtreecommitdiffstats
path: root/integration-test/src/test
diff options
context:
space:
mode:
Diffstat (limited to 'integration-test/src/test')
-rw-r--r--integration-test/src/test/groovy/org/onap/cps/integration/functional/CpsDataServiceIntegrationSpec.groovy64
-rw-r--r--integration-test/src/test/groovy/org/onap/cps/integration/performance/base/CpsPerfTestBase.groovy9
-rw-r--r--integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/CpsAdminServiceLimitsPerfTest.groovy (renamed from integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/CpsAdminServiceLimits.groovy)2
-rw-r--r--integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/CpsDataServiceLimits.groovy63
-rw-r--r--integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/CpsDataServiceLimitsPerfTest.groovy99
-rw-r--r--integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/DeletePerfTest.groovy20
-rw-r--r--integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/GetPerfTest.groovy36
-rw-r--r--integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/QueryPerfTest.groovy27
-rw-r--r--integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/WritePerfTest.groovy83
-rw-r--r--integration-test/src/test/resources/data/bookstore/bookstore.yang29
-rw-r--r--integration-test/src/test/resources/data/bookstore/bookstoreData.json9
11 files changed, 305 insertions, 136 deletions
diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/functional/CpsDataServiceIntegrationSpec.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/functional/CpsDataServiceIntegrationSpec.groovy
index ebaf9093cb..475d3d2fdb 100644
--- a/integration-test/src/test/groovy/org/onap/cps/integration/functional/CpsDataServiceIntegrationSpec.groovy
+++ b/integration-test/src/test/groovy/org/onap/cps/integration/functional/CpsDataServiceIntegrationSpec.groovy
@@ -113,23 +113,49 @@ class CpsDataServiceIntegrationSpec extends FunctionalSpecBase {
restoreBookstoreDataAnchor(1)
}
+ def 'Get whole list data' () {
+ def xpathForWholeList = "/bookstore/categories"
+ when: 'get data nodes for bookstore container'
+ def dataNodes = objectUnderTest.getDataNodes(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, xpathForWholeList, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS)
+ then: 'the tree consist ouf of #expectNumberOfDataNodes data nodes'
+ assert dataNodes.size() == 5
+ and: 'each datanode contains the list node xpath partially in its xpath'
+ dataNodes.each {dataNode ->
+ assert dataNode.xpath.contains(xpathForWholeList)
+ }
+ }
+
+ def 'Read (multiple) data nodes with #scenario' () {
+ when: 'attempt to get data nodes using multiple valid xpaths'
+ def dataNodes = objectUnderTest.getDataNodesForMultipleXpaths(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, xpath, OMIT_DESCENDANTS)
+ then: 'expected numer of data nodes are returned'
+ dataNodes.size() == expectedNumberOfDataNodes
+ where: 'the following data was used'
+ scenario | xpath | expectedNumberOfDataNodes
+ 'container-node xpath' | ['/bookstore'] | 1
+ 'list-item' | ['/bookstore/categories[@code=1]'] | 1
+ 'parent-list xpath' | ['/bookstore/categories'] | 5
+ 'child-list xpath' | ['/bookstore/categories[@code=1]/books'] | 2
+ 'both parent and child list xpath' | ['/bookstore/categories', '/bookstore/categories[@code=1]/books'] | 7
+ }
+
def 'Add and Delete a (container) data node using #scenario.'() {
- when: 'the new datanode is saved'
- objectUnderTest.saveData(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1 , parentXpath, json, now)
- then: 'it can be retrieved by its normalized xpath'
- def result = objectUnderTest.getDataNodes(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, normalizedXpathToNode, DIRECT_CHILDREN_ONLY)
- assert result.size() == 1
- assert result[0].xpath == normalizedXpathToNode
- and: 'there is now one extra datanode'
- assert originalCountBookstoreChildNodes + 1 == countDataNodesInBookstore()
- when: 'the new datanode is deleted'
- objectUnderTest.deleteDataNode(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, normalizedXpathToNode, now)
- then: 'the original number of data nodes is restored'
- assert originalCountBookstoreChildNodes == countDataNodesInBookstore()
- where:
- scenario | parentXpath | json || normalizedXpathToNode
- 'normalized parent xpath' | '/bookstore' | '{"webinfo": {"domain-name":"ourbookstore.com", "contact-email":"info@ourbookstore.com" }}' || "/bookstore/webinfo"
- 'non-normalized parent xpath' | '/bookstore/categories[ @code="1"]' | '{"books": {"title":"new" }}' || "/bookstore/categories[@code='1']/books[@title='new']"
+ when: 'the new datanode is saved'
+ objectUnderTest.saveData(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1 , parentXpath, json, now)
+ then: 'it can be retrieved by its normalized xpath'
+ def result = objectUnderTest.getDataNodes(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, normalizedXpathToNode, DIRECT_CHILDREN_ONLY)
+ assert result.size() == 1
+ assert result[0].xpath == normalizedXpathToNode
+ and: 'there is now one extra datanode'
+ assert originalCountBookstoreChildNodes + 1 == countDataNodesInBookstore()
+ when: 'the new datanode is deleted'
+ objectUnderTest.deleteDataNode(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, normalizedXpathToNode, now)
+ then: 'the original number of data nodes is restored'
+ assert originalCountBookstoreChildNodes == countDataNodesInBookstore()
+ where:
+ scenario | parentXpath | json || normalizedXpathToNode
+ 'normalized parent xpath' | '/bookstore' | '{"webinfo": {"domain-name":"ourbookstore.com", "contact-email":"info@ourbookstore.com" }}' || "/bookstore/webinfo"
+ 'non-normalized parent xpath' | '/bookstore/categories[ @code="1"]' | '{"books": {"title":"new" }}' || "/bookstore/categories[@code='1']/books[@title='new']"
}
def 'Attempt to create a top level data node using root.'() {
@@ -183,15 +209,15 @@ class CpsDataServiceIntegrationSpec extends FunctionalSpecBase {
def 'Add and Delete top-level list (element) data nodes with root node.'() {
given: 'a new (multiple-data-tree:invoice) datanodes'
- def json = '{"multiple-data-tree:invoice": [{"ProductID": "2","ProductName": "Mango","price": "150","stock": true}]}'
+ def json = '{"bookstore-address":[{"bookstore-name":"Scholastic","address":"Bangalore,India","postal-code":"560043"}]}'
when: 'the new list elements are saved'
objectUnderTest.saveListElements(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1 , '/', json, now)
then: 'they can be retrieved by their xpaths'
- objectUnderTest.getDataNodes(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1 , '/invoice[@ProductID ="2"]', INCLUDE_ALL_DESCENDANTS)
+ objectUnderTest.getDataNodes(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1 , '/bookstore-address[@bookstore-name="Easons"]', INCLUDE_ALL_DESCENDANTS)
and: 'there is one extra datanode'
assert originalCountBookstoreTopLevelListNodes + 1 == countTopLevelListDataNodesInBookstore()
when: 'the new elements are deleted'
- objectUnderTest.deleteDataNode(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1 , '/invoice[@ProductID ="2"]', now)
+ objectUnderTest.deleteDataNode(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1 , '/bookstore-address[@bookstore-name="Easons"]', now)
then: 'the original number of datanodes is restored'
assert originalCountBookstoreTopLevelListNodes == countTopLevelListDataNodesInBookstore()
}
diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/performance/base/CpsPerfTestBase.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/performance/base/CpsPerfTestBase.groovy
index 74070b1d83..8a3bd6d23c 100644
--- a/integration-test/src/test/groovy/org/onap/cps/integration/performance/base/CpsPerfTestBase.groovy
+++ b/integration-test/src/test/groovy/org/onap/cps/integration/performance/base/CpsPerfTestBase.groovy
@@ -26,7 +26,10 @@ import org.springframework.web.multipart.MultipartFile
class CpsPerfTestBase extends PerfTestBase {
- static def CPS_PERFORMANCE_TEST_DATASPACE = 'cpsPerformanceDataspace'
+ static final def CPS_PERFORMANCE_TEST_DATASPACE = 'cpsPerformanceDataspace'
+ static final def OPENROADM_ANCHORS = 5
+ static final def OPENROADM_DEVICES_PER_ANCHOR = 50
+ static final def OPENROADM_DATANODES_PER_DEVICE = 86
def printTitle() {
println('## C P S P E R F O R M A N C E T E S T R E S U L T S ##')
@@ -76,9 +79,9 @@ class CpsPerfTestBase extends PerfTestBase {
}
def addOpenRoadData() {
- def data = generateOpenRoadData(50)
+ def data = generateOpenRoadData(OPENROADM_DEVICES_PER_ANCHOR)
stopWatch.start()
- addAnchorsWithData(5, CPS_PERFORMANCE_TEST_DATASPACE, LARGE_SCHEMA_SET, 'openroadm', data)
+ addAnchorsWithData(OPENROADM_ANCHORS, CPS_PERFORMANCE_TEST_DATASPACE, LARGE_SCHEMA_SET, 'openroadm', data)
stopWatch.stop()
def durationInMillis = stopWatch.getTotalTimeMillis()
recordAndAssertPerformance('Creating openroadm anchors with large data tree', 20_000, durationInMillis)
diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/CpsAdminServiceLimits.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/CpsAdminServiceLimitsPerfTest.groovy
index 0034af453b..9ea7a7b53a 100644
--- a/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/CpsAdminServiceLimits.groovy
+++ b/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/CpsAdminServiceLimitsPerfTest.groovy
@@ -23,7 +23,7 @@ package org.onap.cps.integration.performance.cps
import org.onap.cps.api.CpsAdminService
import org.onap.cps.integration.performance.base.CpsPerfTestBase
-class CpsAdminServiceLimits extends CpsPerfTestBase {
+class CpsAdminServiceLimitsPerfTest extends CpsPerfTestBase {
CpsAdminService objectUnderTest
diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/CpsDataServiceLimits.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/CpsDataServiceLimits.groovy
deleted file mode 100644
index 1579470eab..0000000000
--- a/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/CpsDataServiceLimits.groovy
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * ============LICENSE_START=======================================================
- * Copyright (C) 2023 Nordix Foundation
- * ================================================================================
- * 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 java.time.OffsetDateTime
-import org.onap.cps.api.CpsDataService
-import org.onap.cps.integration.performance.base.CpsPerfTestBase
-import org.onap.cps.spi.exceptions.DataNodeNotFoundException
-
-import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS
-
-class CpsDataServiceLimits extends CpsPerfTestBase {
-
- CpsDataService objectUnderTest
-
- def setup() { objectUnderTest = cpsDataService }
-
- def 'Multiple get limit exceeded: 32,764 (~ 2^15) xpaths.'() {
- given: 'more than 32,764 xpaths'
- def xpaths = (0..40_000).collect { "/size/of/this/path/does/not/matter/for/limit[@id='" + it + "']" }
- when: 'single operation is executed to get all datanodes with given xpaths'
- objectUnderTest.getDataNodesForMultipleXpaths(CPS_PERFORMANCE_TEST_DATASPACE, 'bookstore1', xpaths, INCLUDE_ALL_DESCENDANTS)
- then: 'a database exception is not thrown'
- noExceptionThrown()
- }
-
- def 'Delete multiple datanodes limit exceeded: 32,767 (~ 2^15) xpaths.'() {
- given: 'more than 32,767 xpaths'
- def xpaths = (0..40_000).collect { "/size/of/this/path/does/not/matter/for/limit[@id='" + it + "']" }
- when: 'single operation is executed to delete all datanodes with given xpaths'
- objectUnderTest.deleteDataNodes(CPS_PERFORMANCE_TEST_DATASPACE, 'bookstore1', xpaths, OffsetDateTime.now())
- then: 'a database exception is not thrown (but a CPS DataNodeNotFoundException is thrown)'
- thrown(DataNodeNotFoundException.class)
- }
-
- def 'Delete datanodes from multiple anchors limit exceeded: 32,766 (~ 2^15) anchors.'() {
- given: 'more than 32,766 anchor names'
- def anchorNames = (0..40_000).collect { "size-of-this-name-does-not-matter-for-limit-" + it }
- when: 'single operation is executed to delete all datanodes in given anchors'
- objectUnderTest.deleteDataNodes(CPS_PERFORMANCE_TEST_DATASPACE, anchorNames, OffsetDateTime.now())
- then: 'a database exception is not thrown'
- noExceptionThrown()
- }
-
-}
diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/CpsDataServiceLimitsPerfTest.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/CpsDataServiceLimitsPerfTest.groovy
new file mode 100644
index 0000000000..9cb65ab8fd
--- /dev/null
+++ b/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/CpsDataServiceLimitsPerfTest.groovy
@@ -0,0 +1,99 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2023 Nordix Foundation
+ * ================================================================================
+ * 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 java.time.OffsetDateTime
+import org.onap.cps.api.CpsDataService
+import org.onap.cps.integration.performance.base.CpsPerfTestBase
+
+import static org.onap.cps.spi.FetchDescendantsOption.DIRECT_CHILDREN_ONLY
+import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS
+
+class CpsDataServiceLimitsPerfTest extends CpsPerfTestBase {
+
+ CpsDataService objectUnderTest
+
+ def setup() { objectUnderTest = cpsDataService }
+
+ def 'Create 33,000 books (note further tests depend on this running first).'() {
+ given: 'an anchor containing a bookstore with one category'
+ cpsAdminService.createAnchor(CPS_PERFORMANCE_TEST_DATASPACE, BOOKSTORE_SCHEMA_SET, 'limitsAnchor')
+ def parentNodeData = '{"bookstore": { "categories": [{ "code": 1, "name": "Test", "books" : [] }] }}'
+ cpsDataService.saveData(CPS_PERFORMANCE_TEST_DATASPACE, 'limitsAnchor', parentNodeData, OffsetDateTime.now())
+ when: '33,000 books are added'
+ stopWatch.start()
+ for (int i = 1; i <= 33_000; i+=100) {
+ def booksData = '{"books":[' + (i..<i+100).collect {'{ "title": "' + it + '" }' }.join(',') + ']}'
+ cpsDataService.saveData(CPS_PERFORMANCE_TEST_DATASPACE, 'limitsAnchor', '/bookstore/categories[@code=1]', booksData, OffsetDateTime.now())
+ }
+ stopWatch.stop()
+ def durationInMillis = stopWatch.getTotalTimeMillis()
+ then: 'the operation completes within 10 seconds'
+ recordAndAssertPerformance("Creating 33,000 books", 10_000, durationInMillis)
+ }
+
+ def 'Get data nodes from multiple xpaths 32K (2^15) limit exceeded.'() {
+ given: '33,000 xpaths'
+ def xpaths = (1..33_000).collect { "/bookstore/categories[@code=1]/books[@title='${it}']".toString() }
+ when: 'a single operation is executed to get all datanodes with given xpaths'
+ def results = objectUnderTest.getDataNodesForMultipleXpaths(CPS_PERFORMANCE_TEST_DATASPACE, 'limitsAnchor', xpaths, OMIT_DESCENDANTS)
+ then: '33,000 data nodes are returned'
+ assert results.size() == 33_000
+ }
+
+ def 'Delete multiple data nodes 32K (2^15) limit exceeded.'() {
+ given: 'existing data nodes'
+ def countOfDataNodesBeforeDelete = countDataNodes()
+ and: 'a list of 33,000 xpaths'
+ def xpaths = (1..33_000).collect { "/bookstore/categories[@code=1]/books[@title='${it}']".toString() }
+ when: 'a single operation is executed to delete all datanodes with given xpaths'
+ objectUnderTest.deleteDataNodes(CPS_PERFORMANCE_TEST_DATASPACE, 'limitsAnchor', xpaths, OffsetDateTime.now())
+ then: '33,000 data nodes are deleted'
+ def countOfDataNodesAfterDelete = countDataNodes()
+ assert countOfDataNodesBeforeDelete - countOfDataNodesAfterDelete == 33_000
+ }
+
+ def 'Delete data nodes from multiple anchors 32K (2^15) limit exceeded.'() {
+ given: '33,000 anchor names'
+ def anchorNames = (1..33_000).collect { "size-of-this-name-does-not-matter-for-limit-" + it }
+ when: 'a single operation is executed to delete all datanodes in given anchors'
+ objectUnderTest.deleteDataNodes(CPS_PERFORMANCE_TEST_DATASPACE, anchorNames, OffsetDateTime.now())
+ then: 'a database exception is not thrown'
+ noExceptionThrown()
+ }
+
+ def 'Clean up test data.'() {
+ when:
+ stopWatch.start()
+ cpsDataService.deleteDataNodes(CPS_PERFORMANCE_TEST_DATASPACE, 'limitsAnchor', OffsetDateTime.now())
+ cpsAdminService.deleteAnchor(CPS_PERFORMANCE_TEST_DATASPACE, 'limitsAnchor')
+ stopWatch.stop()
+ def durationInMillis = stopWatch.getTotalTimeMillis()
+ then: 'test data is deleted in 10 seconds'
+ recordAndAssertPerformance("Deleting test data", 10_000, durationInMillis)
+ }
+
+ def countDataNodes() {
+ def results = objectUnderTest.getDataNodes(CPS_PERFORMANCE_TEST_DATASPACE, 'limitsAnchor', '/bookstore/categories[@code=1]', DIRECT_CHILDREN_ONLY)
+ return results[0].childDataNodes.size()
+ }
+
+}
diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/DeletePerfTest.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/DeletePerfTest.groovy
index db36b8809b..e80a87d509 100644
--- a/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/DeletePerfTest.groovy
+++ b/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/DeletePerfTest.groovy
@@ -20,6 +20,8 @@
package org.onap.cps.integration.performance.cps
+import org.onap.cps.spi.exceptions.DataNodeNotFoundException
+
import java.time.OffsetDateTime
import org.onap.cps.api.CpsDataService
import org.onap.cps.integration.performance.base.CpsPerfTestBase
@@ -34,7 +36,7 @@ class DeletePerfTest extends CpsPerfTestBase {
when: 'multiple anchors with a node with a large number of descendants is created'
stopWatch.start()
def data = generateOpenRoadData(50)
- addAnchorsWithData(9, CPS_PERFORMANCE_TEST_DATASPACE, LARGE_SCHEMA_SET, 'delete', data)
+ addAnchorsWithData(10, CPS_PERFORMANCE_TEST_DATASPACE, LARGE_SCHEMA_SET, 'delete', data)
stopWatch.stop()
def setupDurationInMillis = stopWatch.getTotalTimeMillis()
then: 'setup duration is under 40 seconds'
@@ -155,9 +157,23 @@ class DeletePerfTest extends CpsPerfTestBase {
recordAndAssertPerformance('Delete data nodes for anchor', 300, deleteDurationInMillis)
}
+ def 'Batch delete 100 non-existing nodes'() {
+ given: 'a list of xpaths to delete'
+ def xpathsToDelete = (1..100).collect { "/path/to/non-existing/node[@id='" + it + "']" }
+ when: 'child nodes are deleted'
+ stopWatch.start()
+ try {
+ objectUnderTest.deleteDataNodes(CPS_PERFORMANCE_TEST_DATASPACE, 'delete10', xpathsToDelete, OffsetDateTime.now())
+ } catch (DataNodeNotFoundException ignored) {}
+ stopWatch.stop()
+ def deleteDurationInMillis = stopWatch.getTotalTimeMillis()
+ then: 'delete duration is under 300 milliseconds'
+ recordAndAssertPerformance('Batch delete 100 non-existing', 300, deleteDurationInMillis)
+ }
+
def 'Clean up test data'() {
given: 'a list of anchors to delete'
- def anchorNames = (1..9).collect {'delete' + it}
+ def anchorNames = (1..10).collect {'delete' + it}
when: 'data nodes are deleted'
stopWatch.start()
cpsAdminService.deleteAnchors(CPS_PERFORMANCE_TEST_DATASPACE, anchorNames)
diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/GetPerfTest.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/GetPerfTest.groovy
index eee87dd7c0..a11dc35682 100644
--- a/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/GetPerfTest.groovy
+++ b/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/GetPerfTest.groovy
@@ -45,28 +45,43 @@ class GetPerfTest extends CpsPerfTestBase {
where: 'the following parameters are used'
scenario | fetchDescendantsOption | anchor || durationLimit | expectedNumberOfDataNodes
'no descendants' | OMIT_DESCENDANTS | 'openroadm1' || 50 | 1
- 'direct descendants' | DIRECT_CHILDREN_ONLY | 'openroadm2' || 100 | 1 + 50
- 'all descendants' | INCLUDE_ALL_DESCENDANTS | 'openroadm3' || 200 | 1 + 50 * 86
+ 'direct descendants' | DIRECT_CHILDREN_ONLY | 'openroadm2' || 100 | 1 + OPENROADM_DEVICES_PER_ANCHOR
+ 'all descendants' | INCLUDE_ALL_DESCENDANTS | 'openroadm3' || 200 | 1 + OPENROADM_DEVICES_PER_ANCHOR * OPENROADM_DATANODES_PER_DEVICE
}
def 'Read data trees for multiple xpaths'() {
given: 'a collection of xpaths to get'
- def xpaths = (1..50).collect { "/openroadm-devices/openroadm-device[@device-id='C201-7-1A-" + it + "']" }
+ def xpaths = (1..OPENROADM_DEVICES_PER_ANCHOR).collect { "/openroadm-devices/openroadm-device[@device-id='C201-7-1A-" + it + "']" }
when: 'get data nodes from 1 anchor'
stopWatch.start()
def result = objectUnderTest.getDataNodesForMultipleXpaths(CPS_PERFORMANCE_TEST_DATASPACE, 'openroadm4', xpaths, INCLUDE_ALL_DESCENDANTS)
stopWatch.stop()
- assert countDataNodesInTree(result) == 50 * 86
def durationInMillis = stopWatch.getTotalTimeMillis()
- then: 'all data is read within 500 ms'
+ then: 'requested nodes and their descendants are returned'
+ assert countDataNodesInTree(result) == OPENROADM_DEVICES_PER_ANCHOR * OPENROADM_DATANODES_PER_DEVICE
+ and: 'all data is read within 200 ms'
recordAndAssertPerformance("Read datatrees for multiple xpaths", 200, durationInMillis)
}
+ def 'Read for multiple xpaths to non-existing datanodes'() {
+ given: 'a collection of xpaths to get'
+ def xpaths = (1..50).collect { "/path/to/non-existing/node[@id='" + it + "']" }
+ when: 'get data nodes from 1 anchor'
+ stopWatch.start()
+ def result = objectUnderTest.getDataNodesForMultipleXpaths(CPS_PERFORMANCE_TEST_DATASPACE, 'openroadm4', xpaths, INCLUDE_ALL_DESCENDANTS)
+ stopWatch.stop()
+ def durationInMillis = stopWatch.getTotalTimeMillis()
+ then: 'no data is returned'
+ assert result.isEmpty()
+ and: 'the operation completes within within 20 ms'
+ recordAndAssertPerformance("Read non-existing xpaths", 20, durationInMillis)
+ }
+
def 'Read complete data trees using #scenario.'() {
when: 'get data nodes for 5 anchors'
stopWatch.start()
(1..5).each {
- def result = objectUnderTest.getDataNodes(CPS_PERFORMANCE_TEST_DATASPACE, anchorPrefix + it, xpath, INCLUDE_ALL_DESCENDANTS)
+ def result = objectUnderTest.getDataNodes(CPS_PERFORMANCE_TEST_DATASPACE, 'openroadm' + it, xpath, INCLUDE_ALL_DESCENDANTS)
assert countDataNodesInTree(result) == expectedNumberOfDataNodes
}
stopWatch.stop()
@@ -74,11 +89,10 @@ class GetPerfTest extends CpsPerfTestBase {
then: 'all data is read within #durationLimit ms'
recordAndAssertPerformance("Read datatrees using ${scenario}", durationLimit, durationInMillis)
where: 'the following xpaths are used'
- scenario | anchorPrefix | xpath || durationLimit | expectedNumberOfDataNodes
- 'bookstore root' | 'bookstore' | '/' || 200 | 78
- 'bookstore top element' | 'bookstore' | '/bookstore' || 200 | 78
- 'openroadm root' | 'openroadm' | '/' || 600 | 1 + 50 * 86
- 'openroadm top element' | 'openroadm' | '/openroadm-devices' || 600 | 1 + 50 * 86
+ scenario | xpath || durationLimit | expectedNumberOfDataNodes
+ 'openroadm root' | '/' || 600 | 1 + OPENROADM_DEVICES_PER_ANCHOR * OPENROADM_DATANODES_PER_DEVICE
+ 'openroadm top element' | '/openroadm-devices' || 600 | 1 + OPENROADM_DEVICES_PER_ANCHOR * OPENROADM_DATANODES_PER_DEVICE
+ 'openroadm whole list' | '/openroadm-devices/openroadm-device' || 600 | OPENROADM_DEVICES_PER_ANCHOR * OPENROADM_DATANODES_PER_DEVICE
}
}
diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/QueryPerfTest.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/QueryPerfTest.groovy
index bad3f8afd2..afcc2eae27 100644
--- a/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/QueryPerfTest.groovy
+++ b/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/QueryPerfTest.groovy
@@ -45,10 +45,11 @@ class QueryPerfTest extends CpsPerfTestBase {
recordAndAssertPerformance("Query 1 anchor ${scenario}", durationLimit, durationInMillis)
where: 'the following parameters are used'
scenario | anchor | cpsPath || durationLimit | expectedNumberOfDataNodes
- 'top element' | 'openroadm1' | '/openroadm-devices' || 120 | 50 * 86 + 1
- 'leaf condition' | 'openroadm2' | '//openroadm-device[@ne-state="inservice"]' || 200 | 50 * 86
- 'ancestors' | 'openroadm3' | '//openroadm-device/ancestor::openroadm-devices' || 120 | 50 * 86 + 1
- 'leaf condition + ancestors' | 'openroadm4' | '//openroadm-device[@status="success"]/ancestor::openroadm-devices' || 120 | 50 * 86 + 1
+ 'top element' | 'openroadm1' | '/openroadm-devices' || 120 | OPENROADM_DEVICES_PER_ANCHOR * OPENROADM_DATANODES_PER_DEVICE + 1
+ 'leaf condition' | 'openroadm2' | '//openroadm-device[@ne-state="inservice"]' || 200 | OPENROADM_DEVICES_PER_ANCHOR * OPENROADM_DATANODES_PER_DEVICE
+ 'ancestors' | 'openroadm3' | '//openroadm-device/ancestor::openroadm-devices' || 120 | OPENROADM_DEVICES_PER_ANCHOR * OPENROADM_DATANODES_PER_DEVICE + 1
+ 'leaf condition + ancestors' | 'openroadm4' | '//openroadm-device[@status="success"]/ancestor::openroadm-devices' || 120 | OPENROADM_DEVICES_PER_ANCHOR * OPENROADM_DATANODES_PER_DEVICE + 1
+ 'non-existing data' | 'openroadm1' | '/path/to/non-existing/node[@id="1"]' || 10 | 0
}
def 'Query complete data trees across all anchors with #scenario.'() {
@@ -63,10 +64,10 @@ class QueryPerfTest extends CpsPerfTestBase {
recordAndAssertPerformance("Query across anchors ${scenario}", durationLimit, durationInMillis)
where: 'the following parameters are used'
scenario | cpspath || durationLimit | expectedNumberOfDataNodes
- 'top element' | '/openroadm-devices' || 400 | 5 * (50 * 86 + 1)
- 'leaf condition' | '//openroadm-device[@ne-state="inservice"]' || 700 | 5 * (50 * 86)
- 'ancestors' | '//openroadm-device/ancestor::openroadm-devices' || 400 | 5 * (50 * 86 + 1)
- 'leaf condition + ancestors' | '//openroadm-device[@status="success"]/ancestor::openroadm-devices' || 400 | 5 * (50 * 86 + 1)
+ 'top element' | '/openroadm-devices' || 400 | OPENROADM_ANCHORS * (OPENROADM_DEVICES_PER_ANCHOR * OPENROADM_DATANODES_PER_DEVICE + 1)
+ 'leaf condition' | '//openroadm-device[@ne-state="inservice"]' || 700 | OPENROADM_ANCHORS * (OPENROADM_DEVICES_PER_ANCHOR * OPENROADM_DATANODES_PER_DEVICE)
+ 'ancestors' | '//openroadm-device/ancestor::openroadm-devices' || 400 | OPENROADM_ANCHORS * (OPENROADM_DEVICES_PER_ANCHOR * OPENROADM_DATANODES_PER_DEVICE + 1)
+ 'leaf condition + ancestors' | '//openroadm-device[@status="success"]/ancestor::openroadm-devices' || 400 | OPENROADM_ANCHORS * (OPENROADM_DEVICES_PER_ANCHOR * OPENROADM_DATANODES_PER_DEVICE + 1)
}
def 'Query with leaf condition and #scenario.'() {
@@ -81,9 +82,9 @@ class QueryPerfTest extends CpsPerfTestBase {
recordAndAssertPerformance("Query with ${scenario}", durationLimit, durationInMillis)
where: 'the following parameters are used'
scenario | fetchDescendantsOption | anchor || durationLimit | expectedNumberOfDataNodes
- 'no descendants' | OMIT_DESCENDANTS | 'openroadm1' || 15 | 50
- 'direct descendants' | DIRECT_CHILDREN_ONLY | 'openroadm2' || 60 | 50 * 2
- 'all descendants' | INCLUDE_ALL_DESCENDANTS | 'openroadm3' || 150 | 50 * 86
+ 'no descendants' | OMIT_DESCENDANTS | 'openroadm1' || 15 | OPENROADM_DEVICES_PER_ANCHOR
+ 'direct descendants' | DIRECT_CHILDREN_ONLY | 'openroadm2' || 60 | OPENROADM_DEVICES_PER_ANCHOR * 2
+ 'all descendants' | INCLUDE_ALL_DESCENDANTS | 'openroadm3' || 150 | OPENROADM_DEVICES_PER_ANCHOR * OPENROADM_DATANODES_PER_DEVICE
}
def 'Query ancestors with #scenario.'() {
@@ -99,8 +100,8 @@ class QueryPerfTest extends CpsPerfTestBase {
where: 'the following parameters are used'
scenario | fetchDescendantsOption | anchor || durationLimit | expectedNumberOfDataNodes
'no descendants' | OMIT_DESCENDANTS | 'openroadm1' || 15 | 1
- 'direct descendants' | DIRECT_CHILDREN_ONLY | 'openroadm2' || 60 | 1 + 50
- 'all descendants' | INCLUDE_ALL_DESCENDANTS | 'openroadm3' || 150 | 1 + 50 * 86
+ 'direct descendants' | DIRECT_CHILDREN_ONLY | 'openroadm2' || 60 | 1 + OPENROADM_DEVICES_PER_ANCHOR
+ 'all descendants' | INCLUDE_ALL_DESCENDANTS | 'openroadm3' || 150 | 1 + OPENROADM_DEVICES_PER_ANCHOR * OPENROADM_DATANODES_PER_DEVICE
}
}
diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/WritePerfTest.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/WritePerfTest.groovy
new file mode 100644
index 0000000000..419ec6096b
--- /dev/null
+++ b/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/WritePerfTest.groovy
@@ -0,0 +1,83 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2023 Nordix Foundation
+ * ================================================================================
+ * 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 java.time.OffsetDateTime
+import org.onap.cps.integration.performance.base.CpsPerfTestBase
+
+class WritePerfTest extends CpsPerfTestBase {
+
+ def 'Writing openroadm data has linear time.'() {
+ given: 'an empty anchor exists for openroadm'
+ cpsAdminService.createAnchor(CPS_PERFORMANCE_TEST_DATASPACE, LARGE_SCHEMA_SET, 'writeAnchor')
+ and: 'a list of device nodes to add'
+ def jsonData = generateOpenRoadData(totalNodes)
+ when: 'device nodes are added'
+ stopWatch.start()
+ cpsDataService.saveData(CPS_PERFORMANCE_TEST_DATASPACE, 'writeAnchor', jsonData, OffsetDateTime.now())
+ stopWatch.stop()
+ def durationInMillis = stopWatch.getTotalTimeMillis()
+ then: 'the operation takes less than #expectedDuration'
+ recordAndAssertPerformance("Writing ${totalNodes} devices", expectedDuration, durationInMillis)
+ cleanup:
+ cpsDataService.deleteDataNodes(CPS_PERFORMANCE_TEST_DATASPACE, 'writeAnchor', OffsetDateTime.now())
+ cpsAdminService.deleteAnchor(CPS_PERFORMANCE_TEST_DATASPACE, 'writeAnchor')
+ where:
+ totalNodes || expectedDuration
+ 50 || 2_500
+ 100 || 4_000
+ 200 || 8_000
+ 400 || 16_000
+// 800 || 32_000
+// 1600 || 64_000
+// 3200 || 128_000
+ }
+
+ def 'Writing bookstore data has exponential time.'() {
+ given: 'an anchor containing a bookstore with a single category'
+ cpsAdminService.createAnchor(CPS_PERFORMANCE_TEST_DATASPACE, BOOKSTORE_SCHEMA_SET, 'writeAnchor')
+ def parentNodeData = '{"bookstore": { "categories": [{ "code": 1, "name": "Test", "books" : [] }] }}'
+ cpsDataService.saveData(CPS_PERFORMANCE_TEST_DATASPACE, 'writeAnchor', parentNodeData, OffsetDateTime.now())
+ and: 'a list of books to add'
+ def booksData = '{"books":[' + (1..totalBooks).collect {'{ "title": "' + it + '" }' }.join(',') + ']}'
+ when: 'books are added'
+ stopWatch.start()
+ cpsDataService.saveData(CPS_PERFORMANCE_TEST_DATASPACE, 'writeAnchor', '/bookstore/categories[@code=1]', booksData, OffsetDateTime.now())
+ stopWatch.stop()
+ def durationInMillis = stopWatch.getTotalTimeMillis()
+ then: 'the operation takes less than #expectedDuration'
+ recordAndAssertPerformance("Writing ${totalBooks} books", expectedDuration, durationInMillis)
+ cleanup:
+ cpsDataService.deleteDataNodes(CPS_PERFORMANCE_TEST_DATASPACE, 'writeAnchor', OffsetDateTime.now())
+ cpsAdminService.deleteAnchor(CPS_PERFORMANCE_TEST_DATASPACE, 'writeAnchor')
+ where:
+ totalBooks || expectedDuration
+ 400 || 200
+ 800 || 500
+ 1600 || 1_000
+ 3200 || 2_500
+ 6400 || 10_000
+// 12800 || 30_000
+// 25600 || 120_000
+// 51200 || 600_000
+ }
+
+}
diff --git a/integration-test/src/test/resources/data/bookstore/bookstore.yang b/integration-test/src/test/resources/data/bookstore/bookstore.yang
index ab384de1c4..6f60f1981c 100644
--- a/integration-test/src/test/resources/data/bookstore/bookstore.yang
+++ b/integration-test/src/test/resources/data/bookstore/bookstore.yang
@@ -15,31 +15,22 @@ module stores {
}
}
- list invoice {
- key "ProductID";
- leaf ProductID {
- type uint64;
- mandatory "true";
- description
- "Unique product ID. Example: 001";
- }
- leaf ProductName {
+ list bookstore-address {
+ key "bookstore-name";
+ leaf bookstore-name {
type string;
- mandatory "true";
description
- "Name of the Product";
+ "Name of bookstore. Example: My Bookstore";
}
- leaf price {
- type uint64;
- mandatory "true";
+ leaf address {
+ type string;
description
- "Price of book";
+ "Address of store";
}
- leaf stock {
- type boolean;
- default "false";
+ leaf postal-code {
+ type string;
description
- "Book in stock or not. Example value: true";
+ "Postal code of store";
}
}
diff --git a/integration-test/src/test/resources/data/bookstore/bookstoreData.json b/integration-test/src/test/resources/data/bookstore/bookstoreData.json
index 5f66a1d002..418acf8ef8 100644
--- a/integration-test/src/test/resources/data/bookstore/bookstoreData.json
+++ b/integration-test/src/test/resources/data/bookstore/bookstoreData.json
@@ -1,10 +1,9 @@
{
- "multiple-data-tree:invoice": [
+ "bookstore-address": [
{
- "ProductID": "1",
- "ProductName": "Apple",
- "price": "100",
- "stock": false
+ "bookstore-name": "Easons",
+ "address": "Dublin,Ireland",
+ "postal-code": "D02HA21"
}
],
"bookstore": {