summaryrefslogtreecommitdiffstats
path: root/cps-ri/src
diff options
context:
space:
mode:
Diffstat (limited to 'cps-ri/src')
-rw-r--r--cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java1
-rw-r--r--cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceQueryDataNodeSpec.groovy128
-rw-r--r--cps-ri/src/test/groovy/org/onap/cps/spi/performance/CpsDataPersistenceServicePerfTest.groovy58
3 files changed, 2 insertions, 185 deletions
diff --git a/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java b/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java
index aa631d1b1a..369e5289b1 100644
--- a/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java
+++ b/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java
@@ -458,6 +458,7 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService
return new DataNodeBuilder()
.withXpath(fragmentEntity.getXpath())
.withLeaves(leaves)
+ .withDataspace(fragmentEntity.getAnchor().getDataspace().getName())
.withAnchor(fragmentEntity.getAnchor().getName())
.withChildDataNodes(childDataNodes).build();
}
diff --git a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceQueryDataNodeSpec.groovy b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceQueryDataNodeSpec.groovy
index 60aaa81140..d5a6be4e80 100644
--- a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceQueryDataNodeSpec.groovy
+++ b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceQueryDataNodeSpec.groovy
@@ -1,6 +1,6 @@
/*
* ============LICENSE_START=======================================================
- * Copyright (C) 2021-2022 Nordix Foundation
+ * Copyright (C) 2021-2023 Nordix Foundation
* Modifications Copyright (C) 2021 Pantheon.tech
* Modifications Copyright (C) 2021 Bell Canada.
* Modifications Copyright (C) 2023 TechMahindra Ltd.
@@ -23,13 +23,10 @@
package org.onap.cps.spi.impl
import org.onap.cps.spi.CpsDataPersistenceService
-import org.onap.cps.spi.FetchDescendantsOption
import org.onap.cps.spi.exceptions.CpsPathException
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.test.context.jdbc.Sql
-import java.util.stream.Collectors
-
import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS
import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS
@@ -41,96 +38,6 @@ class CpsDataPersistenceQueryDataNodeSpec extends CpsPersistenceSpecBase {
static final String SET_DATA = '/data/cps-path-query.sql'
@Sql([CLEAR_DATA, SET_DATA])
- def 'Cps Path query for leaf value(s) with : #scenario.'() {
- when: 'a query is executed to get a data node by the given cps path'
- def result = objectUnderTest.queryDataNodes(DATASPACE_NAME, ANCHOR_FOR_SHOP_EXAMPLE, cpsPath, fetchDescendantsOption)
- then: 'the correct number of parent nodes are returned'
- result.size() == expectedNumberOfParentNodes
- then: 'the correct data is returned'
- result.each {
- assert it.getChildDataNodes().size() == expectedNumberOfChildNodes
- }
- where: 'the following data is used'
- scenario | cpsPath | fetchDescendantsOption || expectedNumberOfParentNodes | expectedNumberOfChildNodes
- 'String and no descendants' | '/shops/shop[@id=1]/categories[@code=1]/book[@title="Dune"]' | OMIT_DESCENDANTS || 1 | 0
- 'Integer and descendants' | '/shops/shop[@id=1]/categories[@code=1]/book[@price=5]' | INCLUDE_ALL_DESCENDANTS || 1 | 1
- 'No condition no descendants' | '/shops/shop[@id=1]/categories' | OMIT_DESCENDANTS || 3 | 0
- 'Integer and level 1 descendants' | '/shops' | new FetchDescendantsOption(1) || 1 | 5
- 'Integer and level 2 descendants' | '/shops/shop[@id=1]' | new FetchDescendantsOption(2) || 1 | 3
- }
-
- @Sql([CLEAR_DATA, SET_DATA])
- def 'Query for attribute by cps path with cps paths that return no data because of #scenario.'() {
- when: 'a query is executed to get data nodes for the given cps path'
- def result = objectUnderTest.queryDataNodes(DATASPACE_NAME, ANCHOR_FOR_SHOP_EXAMPLE, cpsPath, OMIT_DESCENDANTS)
- then: 'no data is returned'
- result.isEmpty()
- where: 'following cps queries are performed'
- scenario | cpsPath
- 'cps path is incomplete' | '/shops[@title="Dune"]'
- 'leaf value does not exist' | '/shops/shop[@id=1]/categories[@code=1]/book[@title=\'does not exist\']'
- 'incomplete end of xpath prefix' | '/shops/shop[@id=1]/categories/book[@price=15]'
- }
-
- @Sql([CLEAR_DATA, SET_DATA])
- def 'Cps Path query using descendant anywhere and #type (further) descendants.'() {
- when: 'a query is executed to get a data node by the given cps path'
- def cpsPath = '//categories[@code=1]'
- def result = objectUnderTest.queryDataNodes(DATASPACE_NAME, ANCHOR_FOR_SHOP_EXAMPLE, cpsPath, includeDescendantsOption)
- then: 'the data node has the correct number of children'
- def dataNode = result.stream().findFirst().get()
- dataNode.getChildDataNodes().size() == expectedNumberOfChildNodes
- where: 'the following data is used'
- type | includeDescendantsOption || expectedNumberOfChildNodes
- 'omit' | OMIT_DESCENDANTS || 0
- 'include' | INCLUDE_ALL_DESCENDANTS || 1
- }
-
- @Sql([CLEAR_DATA, SET_DATA])
- def 'Cps Path query using descendant anywhere with #scenario '() {
- when: 'a query is executed to get a data node by the given cps path'
- def result = objectUnderTest.queryDataNodes(DATASPACE_NAME, ANCHOR_FOR_SHOP_EXAMPLE, cpsPath, OMIT_DESCENDANTS)
- then: 'the correct number of data nodes are retrieved'
- result.size() == expectedXPaths.size()
- and: 'xpaths of the retrieved data nodes are as expected'
- for (int i = 0; i < result.size(); i++) {
- assert result[i].getXpath() == expectedXPaths[i]
- }
- where: 'the following data is used'
- scenario | cpsPath || expectedXPaths
- 'fully unique descendant name' | '//categories[@code=2]' || ["/shops/shop[@id='1']/categories[@code='2']", "/shops/shop[@id='2']/categories[@code='1']", "/shops/shop[@id='2']/categories[@code='2']"]
- 'descendant name match end of other node' | '//book' || ["/shops/shop[@id='1']/categories[@code='1']/book", "/shops/shop[@id='1']/categories[@code='2']/book"]
- 'descendant with text condition on leaf' | '//book/title[text()="Chapters"]' || ["/shops/shop[@id='1']/categories[@code='2']/book"]
- 'descendant with text condition case mismatch' | '//book/title[text()="chapters"]' || []
- 'descendant with text condition on int leaf' | '//book/price[text()="5"]' || ["/shops/shop[@id='1']/categories[@code='1']/book"]
- 'descendant with text condition on leaf-list' | '//book/labels[text()="special offer"]' || ["/shops/shop[@id='1']/categories[@code='1']/book"]
- 'descendant with text condition partial match' | '//book/labels[text()="special"]' || []
- 'descendant with text condition (existing) empty string' | '//book/labels[text()=""]' || ["/shops/shop[@id='1']/categories[@code='1']/book"]
- 'descendant with text condition on int leaf-list' | '//book/editions[text()="2000"]' || ["/shops/shop[@id='1']/categories[@code='2']/book"]
- 'descendant name match of leaf containing /' | '//categories/type[text()="text/with/slash"]' || ["/shops/shop[@id='1']/categories[@code='string/with/slash/']"]
- 'descendant with text condition on leaf containing /' | '//categories[@code=\'string/with/slash\']' || ["/shops/shop[@id='1']/categories[@code='string/with/slash/']"]
- 'descendant with text condition on leaf containing [' | '//book/author[@Address="String[with]square[bracket]"]'|| []
- }
-
- @Sql([CLEAR_DATA, SET_DATA])
- def 'Cps Path query using descendant anywhere with #scenario condition(s) for a container element.'() {
- when: 'a query is executed to get a data node by the given cps path'
- def result = objectUnderTest.queryDataNodes(DATASPACE_NAME, ANCHOR_FOR_SHOP_EXAMPLE, cpsPath, OMIT_DESCENDANTS)
- then: 'the correct number of data nodes are retrieved'
- result.size() == expectedXPaths.size()
- and: 'xpaths of the retrieved data nodes are as expected'
- for (int i = 0; i < result.size(); i++) {
- assert result[i].getXpath() == expectedXPaths[i]
- }
- where: 'the following data is used'
- scenario | cpsPath || expectedXPaths
- 'one leaf' | '//author[@FirstName="Joe"]' || ["/shops/shop[@id='1']/categories[@code='1']/book/author[@FirstName='Joe' and @Surname='Bloggs']", "/shops/shop[@id='1']/categories[@code='2']/book/author[@FirstName='Joe' and @Surname='Smith']"]
- 'more than one leaf' | '//author[@FirstName="Joe" and @Surname="Bloggs"]' || ["/shops/shop[@id='1']/categories[@code='1']/book/author[@FirstName='Joe' and @Surname='Bloggs']"]
- 'leaves reversed in order' | '//author[@Surname="Bloggs" and @FirstName="Joe"]' || ["/shops/shop[@id='1']/categories[@code='1']/book/author[@FirstName='Joe' and @Surname='Bloggs']"]
- 'leaf and text condition' | '//author[@FirstName="Joe"]/Surname[text()="Bloggs"]' || ["/shops/shop[@id='1']/categories[@code='1']/book/author[@FirstName='Joe' and @Surname='Bloggs']"]
- }
-
- @Sql([CLEAR_DATA, SET_DATA])
def 'Cps Path query using descendant anywhere with #scenario condition(s) for a list element.'() {
when: 'a query is executed to get a data node by the given cps path'
def result = objectUnderTest.queryDataNodes(DATASPACE_NAME, ANCHOR_FOR_SHOP_EXAMPLE, cpsPath, OMIT_DESCENDANTS)
@@ -148,39 +55,6 @@ class CpsDataPersistenceQueryDataNodeSpec extends CpsPersistenceSpecBase {
}
@Sql([CLEAR_DATA, SET_DATA])
- def 'Query for attribute by cps path of type ancestor with #scenario.'() {
- when: 'the given cps path is parsed'
- def result = objectUnderTest.queryDataNodes(DATASPACE_NAME, ANCHOR_FOR_SHOP_EXAMPLE, cpsPath, INCLUDE_ALL_DESCENDANTS)
- then: 'the xpaths of the retrieved data nodes are as expected'
- result.size() == expectedXPaths.size()
- if (result.size() > 0) {
- def resultXpaths = result.stream().map(it -> it.xpath).collect(Collectors.toSet())
- resultXpaths.containsAll(expectedXPaths)
- result.each {
- assert it.childDataNodes.size() == expectedNumberOfChildren
- }
- }
- where: 'the following data is used'
- scenario | cpsPath || expectedXPaths || expectedNumberOfChildren
- 'multiple list-ancestors' | '//book/ancestor::categories' || ["/shops/shop[@id='1']/categories[@code='2']", "/shops/shop[@id='1']/categories[@code='1']"] || 1
- 'one ancestor with list value' | '//book/ancestor::categories[@code=1]' || ["/shops/shop[@id='1']/categories[@code='1']"] || 1
- 'top ancestor' | '//shop[@id=1]/ancestor::shops' || ['/shops'] || 5
- 'list with index value in the xpath prefix' | '//categories[@code=1]/book/ancestor::shop[@id=1]' || ["/shops/shop[@id='1']"] || 3
- 'ancestor with parent list' | '//book/ancestor::shop[@id=1]/categories[@code=2]' || ["/shops/shop[@id='1']/categories[@code='2']"] || 1
- 'ancestor with parent' | '//phonenumbers[@type="mob"]/ancestor::info/contact' || ["/shops/shop[@id='3']/info/contact"] || 3
- 'ancestor combined with text condition' | '//book/title[text()="Dune"]/ancestor::shop' || ["/shops/shop[@id='1']"] || 3
- 'ancestor with parent that does not exist' | '//book/ancestor::parentDoesNoExist/categories' || [] || null
- 'ancestor does not exist' | '//book/ancestor::ancestorDoesNotExist' || [] || null
- }
-
- def 'Cps Path query with syntax error throws a CPS Path Exception.'() {
- when: 'trying to execute a query with a syntax (parsing) error'
- objectUnderTest.queryDataNodes(DATASPACE_NAME, ANCHOR_FOR_SHOP_EXAMPLE, 'cpsPath that cannot be parsed' , OMIT_DESCENDANTS)
- then: 'a cps path exception is thrown'
- thrown(CpsPathException)
- }
-
- @Sql([CLEAR_DATA, SET_DATA])
def 'Cps Path query across anchors for leaf value(s) with : #scenario.'() {
when: 'a query is executed to get a data node by the given cps path'
def result = objectUnderTest.queryDataNodesAcrossAnchors(DATASPACE_NAME, cpsPath, includeDescendantsOption)
diff --git a/cps-ri/src/test/groovy/org/onap/cps/spi/performance/CpsDataPersistenceServicePerfTest.groovy b/cps-ri/src/test/groovy/org/onap/cps/spi/performance/CpsDataPersistenceServicePerfTest.groovy
index 7f1fb20f72..2628e9697f 100644
--- a/cps-ri/src/test/groovy/org/onap/cps/spi/performance/CpsDataPersistenceServicePerfTest.groovy
+++ b/cps-ri/src/test/groovy/org/onap/cps/spi/performance/CpsDataPersistenceServicePerfTest.groovy
@@ -47,7 +47,6 @@ class CpsDataPersistenceServicePerfTest extends CpsPersistencePerfSpecBase {
static def NUMBER_OF_CHILDREN = 200
static def NUMBER_OF_GRAND_CHILDREN = 50
- static def TOTAL_NUMBER_OF_NODES = 1 + NUMBER_OF_CHILDREN + (NUMBER_OF_CHILDREN * NUMBER_OF_GRAND_CHILDREN) // Parent + Children + Grand-children
@Sql([CLEAR_DATA, PERF_TEST_DATA])
def 'Create a node with many descendants (please note, subsequent tests depend on this running first).'() {
@@ -60,63 +59,6 @@ class CpsDataPersistenceServicePerfTest extends CpsPersistencePerfSpecBase {
recordAndAssertPerformance('Setup', 10000, setupDurationInMillis)
}
- def 'Get data node with many descendants by xpath #scenario'() {
- when: 'get parent is executed with all descendants'
- stopWatch.start()
- def result = objectUnderTest.getDataNodes(PERF_DATASPACE, PERF_ANCHOR, xpath, INCLUDE_ALL_DESCENDANTS)
- stopWatch.stop()
- def readDurationInMillis = stopWatch.getTotalTimeMillis()
- then: 'read duration is under #allowedDuration milliseconds'
- recordAndAssertPerformance("Get ${scenario}", allowedDuration, readDurationInMillis)
- and: 'data node is returned with all the descendants populated'
- assert countDataNodes(result[0]) == TOTAL_NUMBER_OF_NODES
- where: 'the following xPaths are used'
- scenario | xpath || allowedDuration
- 'parent' | PERF_TEST_PARENT || 500
- 'root' | '/' || 500
- }
-
- def 'Query parent data node with many descendants by cps-path'() {
- when: 'query is executed with all descendants'
- stopWatch.start()
- def result = objectUnderTest.queryDataNodes(PERF_DATASPACE, PERF_ANCHOR, '//perf-parent-1' , INCLUDE_ALL_DESCENDANTS)
- stopWatch.stop()
- def readDurationInMillis = stopWatch.getTotalTimeMillis()
- then: 'read duration is under 350 milliseconds'
- recordAndAssertPerformance('Query with many descendants', 350, readDurationInMillis)
- and: 'data node is returned with all the descendants populated'
- assert countDataNodes(result) == TOTAL_NUMBER_OF_NODES
- }
-
- def 'Performance of finding multiple xpaths'() {
- when: 'we query for all grandchildren (except 1 for fun) with the new native method'
- xpathsToAllGrandChildren.remove(0)
- stopWatch.start()
- def result = objectUnderTest.getDataNodesForMultipleXpaths(PERF_DATASPACE, PERF_ANCHOR, xpathsToAllGrandChildren, INCLUDE_ALL_DESCENDANTS)
- stopWatch.stop()
- def readDurationInMillis = stopWatch.getTotalTimeMillis()
- then: 'the returned number of entities equal to the number of children * number of grandchildren'
- assert result.size() == xpathsToAllGrandChildren.size()
- and: 'it took less then 1000ms'
- recordAndAssertPerformance('Find multiple xpaths', 1000, readDurationInMillis)
- }
-
- def 'Query many descendants by cps-path with #scenario'() {
- when: 'query is executed with all descendants'
- stopWatch.start()
- def result = objectUnderTest.queryDataNodes(PERF_DATASPACE, PERF_ANCHOR, '//perf-test-grand-child-1', descendantsOption)
- stopWatch.stop()
- def readDurationInMillis = stopWatch.getTotalTimeMillis()
- then: 'read duration is under #allowedDuration milliseconds'
- recordAndAssertPerformance("Query many descendants by cpspath (${scenario})", allowedDuration, readDurationInMillis)
- and: 'data node is returned with all the descendants populated'
- assert result.size() == NUMBER_OF_CHILDREN
- where: 'the following options are used'
- scenario | descendantsOption || allowedDuration
- 'omit descendants ' | OMIT_DESCENDANTS || 150
- 'include descendants (although there are none)' | INCLUDE_ALL_DESCENDANTS || 150
- }
-
def 'Update data nodes with descendants'() {
given: 'a list of xpaths to data nodes with descendants (xpath for each child)'
def xpaths = (1..20).collect {