aboutsummaryrefslogtreecommitdiffstats
path: root/cps-service/src/test/groovy
diff options
context:
space:
mode:
authorArpit Singh <as00745003@techmahindra.com>2024-08-09 12:23:49 +0530
committerArpit Singh <as00745003@techmahindra.com>2024-10-09 09:28:30 +0530
commit07acbb4ddd713f74406b156cbcac2507f96f3b08 (patch)
tree8f4122b9c3ecb994b9bb203cdd8b3fd1ad191f64 /cps-service/src/test/groovy
parente2517a8b993ed884edb251b91ce600d0a1a9fefe (diff)
Implementation of Data validation feature in Create a Node API
Added support to validate JSON/XML data without the need of persisting it in the databse. - added "dryRunInQuery" flag as a new query parameter - added new method as part of CpsDataService layer to perform data validation - added new method in yang parser "validateData" to validate data without persisting it Issue-ID: CPS-2361 Change-Id: I43dd33cc6120576d0fac606d5c4b0168d107311d Signed-off-by: Arpit Singh <as00745003@techmahindra.com>
Diffstat (limited to 'cps-service/src/test/groovy')
-rw-r--r--cps-service/src/test/groovy/org/onap/cps/api/impl/CpsDataServiceImplSpec.groovy14
-rwxr-xr-xcps-service/src/test/groovy/org/onap/cps/api/impl/E2ENetworkSliceSpec.groovy2
-rw-r--r--cps-service/src/test/groovy/org/onap/cps/spi/model/DataNodeBuilderSpec.groovy13
-rw-r--r--cps-service/src/test/groovy/org/onap/cps/utils/YangParserHelperSpec.groovy33
-rw-r--r--cps-service/src/test/groovy/org/onap/cps/utils/YangParserSpec.groovy48
5 files changed, 91 insertions, 19 deletions
diff --git a/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsDataServiceImplSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsDataServiceImplSpec.groovy
index 9846b30158..8c208a1cf8 100644
--- a/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsDataServiceImplSpec.groovy
+++ b/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsDataServiceImplSpec.groovy
@@ -546,6 +546,20 @@ class CpsDataServiceImplSpec extends Specification {
1 * mockDataUpdateEventsService.publishCpsDataUpdateEvent(anchor2, '/', DELETE, observedTimestamp)
}
+ def "Validating #scenario when dry run is enabled."() {
+ given: 'schema set for given anchors and dataspace references bookstore model'
+ setupSchemaSetMocks('bookstore.yang')
+ when: 'validating the data with the given parameters'
+ objectUnderTest.validateData(dataspaceName, anchorName, parentNodeXpath, data,contentType)
+ then: 'the appropriate yang parser method is invoked with correct parameters'
+ yangParser.validateData(contentType, data, anchor, xpath)
+ where: 'the following parameters were used'
+ scenario | parentNodeXpath | xpath | contentType | data
+ 'JSON data with root node xpath' | '/' | '' | ContentType.JSON | '{"bookstore":{"bookstore-name":"Easons"}}'
+ 'JSON data with specific xpath' | '/bookstore' | '/bookstore' | ContentType.JSON | '{"bookstore-name":"Easons"}'
+ 'XML data with specific xpath' | '/bookstore' | '/bookstore' | ContentType.XML | '<bookstore-name>Easons</bookstore-name>'
+ }
+
def 'Start session.'() {
when: 'start session method is called'
objectUnderTest.startSession()
diff --git a/cps-service/src/test/groovy/org/onap/cps/api/impl/E2ENetworkSliceSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/api/impl/E2ENetworkSliceSpec.groovy
index 05c8983fc2..9f3456280e 100755
--- a/cps-service/src/test/groovy/org/onap/cps/api/impl/E2ENetworkSliceSpec.groovy
+++ b/cps-service/src/test/groovy/org/onap/cps/api/impl/E2ENetworkSliceSpec.groovy
@@ -171,6 +171,6 @@ class E2ENetworkSliceSpec extends Specification {
expect: 'schema context is built with no exception indicating the schema set being valid '
def schemaContext = YangTextSchemaSourceSetBuilder.of(yangResourcesNameToContentMap).getSchemaContext()
and: 'data is parsed with no exception indicating the model match'
- new YangParserHelper().parseData(ContentType.JSON, jsonData, schemaContext, '') != null
+ new YangParserHelper().parseData(ContentType.JSON, jsonData, schemaContext, '', false) != null
}
}
diff --git a/cps-service/src/test/groovy/org/onap/cps/spi/model/DataNodeBuilderSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/spi/model/DataNodeBuilderSpec.groovy
index e305abee86..f028d5d5d9 100644
--- a/cps-service/src/test/groovy/org/onap/cps/spi/model/DataNodeBuilderSpec.groovy
+++ b/cps-service/src/test/groovy/org/onap/cps/spi/model/DataNodeBuilderSpec.groovy
@@ -35,6 +35,7 @@ class DataNodeBuilderSpec extends Specification {
def objectUnderTest = new DataNodeBuilder()
def yangParserHelper = new YangParserHelper()
+ def validateAndParse = false
def expectedLeavesByXpathMap = [
'/test-tree' : [],
@@ -60,7 +61,7 @@ class DataNodeBuilderSpec extends Specification {
def schemaContext = YangTextSchemaSourceSetBuilder.of(yangResourceNameToContent) getSchemaContext()
and: 'the json data parsed into container node object'
def jsonData = TestUtils.getResourceFileContent('test-tree.json')
- def containerNode = yangParserHelper.parseData(ContentType.JSON, jsonData, schemaContext, '')
+ def containerNode = yangParserHelper.parseData(ContentType.JSON, jsonData, schemaContext, '', validateAndParse)
when: 'the container node is converted to a data node'
def result = objectUnderTest.withContainerNode(containerNode).build()
def mappedResult = TestUtils.getFlattenMapByXpath(result)
@@ -80,7 +81,7 @@ class DataNodeBuilderSpec extends Specification {
def schemaContext = YangTextSchemaSourceSetBuilder.of(yangResourceNameToContent) getSchemaContext()
and: 'the json data parsed into container node object'
def jsonData = '{ "branch": [{ "name": "Branch", "nest": { "name": "Nest", "birds": ["bird"] } }] }'
- def containerNode = yangParserHelper.parseData(ContentType.JSON, jsonData, schemaContext, '/test-tree')
+ def containerNode = yangParserHelper.parseData(ContentType.JSON, jsonData, schemaContext, '/test-tree', validateAndParse)
when: 'the container node is converted to a data node with parent node xpath defined'
def result = objectUnderTest.withContainerNode(containerNode).withParentNodeXpath('/test-tree').build()
def mappedResult = TestUtils.getFlattenMapByXpath(result)
@@ -96,7 +97,7 @@ class DataNodeBuilderSpec extends Specification {
def schemaContext = YangTextSchemaSourceSetBuilder.of(yangResourceNameToContent) getSchemaContext()
and: 'the json data parsed into container node object'
def jsonData = TestUtils.getResourceFileContent('ietf/data/ietf-network-topology-sample-rfc8345.json')
- def containerNode = yangParserHelper.parseData(ContentType.JSON, jsonData, schemaContext, '')
+ def containerNode = yangParserHelper.parseData(ContentType.JSON, jsonData, schemaContext, '', validateAndParse)
when: 'the container node is converted to a data node '
def result = objectUnderTest.withContainerNode(containerNode).build()
def mappedResult = TestUtils.getFlattenMapByXpath(result)
@@ -129,7 +130,7 @@ class DataNodeBuilderSpec extends Specification {
def parentNodeXpath = "/networks/network[@network-id='otn-hc']/link[@link-id='D1,1-2-1,D2,2-1-1']"
and: 'the json data fragment parsed into container node object for given parent node xpath'
def jsonData = '{"source": {"source-node": "D1", "source-tp": "1-2-1"}}'
- def containerNode = yangParserHelper.parseData(ContentType.JSON, jsonData, schemaContext,parentNodeXpath)
+ def containerNode = yangParserHelper.parseData(ContentType.JSON, jsonData, schemaContext,parentNodeXpath, validateAndParse)
when: 'the container node is converted to a data node with given parent node xpath'
def result = objectUnderTest.withContainerNode(containerNode).withParentNodeXpath(parentNodeXpath).build()
then: 'the resulting data node represents a child of augmentation node'
@@ -144,7 +145,7 @@ class DataNodeBuilderSpec extends Specification {
def schemaContext = YangTextSchemaSourceSetBuilder.of(yangResourceNameToContent) getSchemaContext()
and: 'the json data fragment parsed into container node object'
def jsonData = TestUtils.getResourceFileContent('data-with-choice-node.json')
- def containerNode = yangParserHelper.parseData(ContentType.JSON, jsonData, schemaContext, '')
+ def containerNode = yangParserHelper.parseData(ContentType.JSON, jsonData, schemaContext, '', validateAndParse)
when: 'the container node is converted to a data node'
def result = objectUnderTest.withContainerNode(containerNode).build()
def mappedResult = TestUtils.getFlattenMapByXpath(result)
@@ -162,7 +163,7 @@ class DataNodeBuilderSpec extends Specification {
and: 'parent node xpath referencing parent of list element'
def parentNodeXpath = '/test-tree'
and: 'the json data fragment (list element) parsed into container node object'
- def containerNode = yangParserHelper.parseData(ContentType.JSON, jsonData, schemaContext, parentNodeXpath)
+ def containerNode = yangParserHelper.parseData(ContentType.JSON, jsonData, schemaContext, parentNodeXpath, validateAndParse)
when: 'the container node is converted to a data node collection'
def result = objectUnderTest.withContainerNode(containerNode).withParentNodeXpath(parentNodeXpath).buildCollection()
def resultXpaths = result.collect { it.getXpath() }
diff --git a/cps-service/src/test/groovy/org/onap/cps/utils/YangParserHelperSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/utils/YangParserHelperSpec.groovy
index 073383113d..e1490c28ab 100644
--- a/cps-service/src/test/groovy/org/onap/cps/utils/YangParserHelperSpec.groovy
+++ b/cps-service/src/test/groovy/org/onap/cps/utils/YangParserHelperSpec.groovy
@@ -30,6 +30,8 @@ import spock.lang.Specification
class YangParserHelperSpec extends Specification {
def objectUnderTest = new YangParserHelper()
+ def validateOnly = true
+ def validateAndParse = false
def 'Parsing a valid multicontainer Json String.'() {
given: 'a yang model (file)'
@@ -38,7 +40,7 @@ class YangParserHelperSpec extends Specification {
def yangResourceNameToContent = TestUtils.getYangResourcesAsMap('multipleDataTree.yang')
def schemaContext = YangTextSchemaSourceSetBuilder.of(yangResourceNameToContent).getSchemaContext()
when: 'the json data is parsed'
- def result = objectUnderTest.parseData(ContentType.JSON, jsonData, schemaContext, '')
+ def result = objectUnderTest.parseData(ContentType.JSON, jsonData, schemaContext, '', validateAndParse)
then: 'a ContainerNode holding collection of normalized nodes is returned'
result.body().getAt(index) instanceof NormalizedNode == true
then: 'qualified name of children created is as expected'
@@ -56,7 +58,7 @@ class YangParserHelperSpec extends Specification {
def yangResourceNameToContent = TestUtils.getYangResourcesAsMap('bookstore.yang')
def schemaContext = YangTextSchemaSourceSetBuilder.of(yangResourceNameToContent).getSchemaContext()
when: 'the data is parsed'
- NormalizedNode result = objectUnderTest.parseData(contentType, fileData, schemaContext, '')
+ NormalizedNode result = objectUnderTest.parseData(contentType, fileData, schemaContext, '', validateAndParse)
then: 'the result is a normalized node of the correct type'
if (revision) {
result.identifier.nodeType == QName.create(namespace, revision, localName)
@@ -74,7 +76,7 @@ class YangParserHelperSpec extends Specification {
def yangResourceNameToContent = TestUtils.getYangResourcesAsMap('bookstore.yang')
def schemaContext = YangTextSchemaSourceSetBuilder.of(yangResourceNameToContent).getSchemaContext()
when: 'invalid data is parsed'
- objectUnderTest.parseData(contentType, invalidData, schemaContext, '')
+ objectUnderTest.parseData(contentType, invalidData, schemaContext, '', validateAndParse)
then: 'an exception is thrown'
thrown(DataValidationException)
where: 'the following invalid data is provided'
@@ -92,7 +94,7 @@ class YangParserHelperSpec extends Specification {
def yangResourcesMap = TestUtils.getYangResourcesAsMap('test-tree.yang')
def schemaContext = YangTextSchemaSourceSetBuilder.of(yangResourcesMap).getSchemaContext()
when: 'json string is parsed'
- def result = objectUnderTest.parseData(contentType, nodeData, schemaContext, parentNodeXpath)
+ def result = objectUnderTest.parseData(contentType, nodeData, schemaContext, parentNodeXpath, validateAndParse)
then: 'a ContainerNode holding collection of normalized nodes is returned'
result.body().getAt(0) instanceof NormalizedNode == true
then: 'result represents a node of expected type'
@@ -112,7 +114,7 @@ class YangParserHelperSpec extends Specification {
def yangResourcesMap = TestUtils.getYangResourcesAsMap('test-tree.yang')
def schemaContext = YangTextSchemaSourceSetBuilder.of(yangResourcesMap).getSchemaContext()
when: 'json string is parsed'
- objectUnderTest.parseData(ContentType.JSON, '{"nest": {"name" : "Nest", "birds": ["bird"]}}', schemaContext, parentNodeXpath)
+ objectUnderTest.parseData(ContentType.JSON, '{"nest": {"name" : "Nest", "birds": ["bird"]}}', schemaContext, parentNodeXpath, validateAndParse)
then: 'expected exception is thrown'
thrown(DataValidationException)
where:
@@ -129,7 +131,7 @@ class YangParserHelperSpec extends Specification {
def yangResourcesMap = TestUtils.getYangResourcesAsMap('bookstore.yang')
def schemaContext = YangTextSchemaSourceSetBuilder.of(yangResourcesMap).getSchemaContext()
when: 'malformed json string is parsed'
- objectUnderTest.parseData(ContentType.JSON, invalidJson, schemaContext, '')
+ objectUnderTest.parseData(ContentType.JSON, invalidJson, schemaContext, '', validateAndParse)
then: 'an exception is thrown'
thrown(DataValidationException)
where: 'the following malformed json is provided'
@@ -145,7 +147,7 @@ class YangParserHelperSpec extends Specification {
and: 'some json data with space in the array elements'
def jsonDataWithSpacesInArrayElement = TestUtils.getResourceFileContent('bookstore.json')
when: 'that json data is parsed'
- objectUnderTest.parseData(ContentType.JSON, jsonDataWithSpacesInArrayElement, schemaContext, '')
+ objectUnderTest.parseData(ContentType.JSON, jsonDataWithSpacesInArrayElement, schemaContext, '', validateAndParse)
then: 'no exception thrown'
noExceptionThrown()
}
@@ -162,5 +164,22 @@ class YangParserHelperSpec extends Specification {
'xpath contains list attributes with /' | '/test-tree/branch[@name=\'/Branch\']/categories[@id=\'/broken\']' || ['test-tree','branch','categories']
}
+ def 'Validating #scenario xpath String.'() {
+ given: 'a data model (file) is provided'
+ def fileData = TestUtils.getResourceFileContent(contentFile)
+ and: 'the schema context is built for that data model'
+ def yangResourceNameToContent = TestUtils.getYangResourcesAsMap('bookstore.yang')
+ def schemaContext = YangTextSchemaSourceSetBuilder.of(yangResourceNameToContent).getSchemaContext()
+ when: 'the data is parsed to be validated'
+ objectUnderTest.parseData(contentType, fileData, schemaContext, parentNodeXpath, validateOnly)
+ then: 'no exception is thrown'
+ noExceptionThrown()
+ where:
+ scenario | parentNodeXpath | contentFile | contentType
+ 'JSON without parent node' | '' | 'bookstore.json' | ContentType.JSON
+ 'JSON with parent node' | '/bookstore' | 'bookstore-categories-data.json' | ContentType.JSON
+ 'XML without parent node' | '' | 'bookstore.xml' | ContentType.XML
+ 'XML with parent node' | '/bookstore' | 'bookstore-categories-data.xml' | ContentType.XML
+ }
}
diff --git a/cps-service/src/test/groovy/org/onap/cps/utils/YangParserSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/utils/YangParserSpec.groovy
index 18d0502e30..6c52becbe1 100644
--- a/cps-service/src/test/groovy/org/onap/cps/utils/YangParserSpec.groovy
+++ b/cps-service/src/test/groovy/org/onap/cps/utils/YangParserSpec.groovy
@@ -26,7 +26,6 @@ import org.onap.cps.spi.exceptions.DataValidationException
import org.onap.cps.spi.model.Anchor
import org.onap.cps.yang.TimedYangTextSchemaSourceSetBuilder
import org.onap.cps.yang.YangTextSchemaSourceSet
-import org.onap.cps.yang.YangTextSchemaSourceSetBuilder
import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode
import org.opendaylight.yangtools.yang.model.api.SchemaContext
import spock.lang.Specification
@@ -47,6 +46,8 @@ class YangParserSpec extends Specification {
def containerNodeFromYangUtils = Mock(ContainerNode)
def noParent = ''
+ def validateOnly = true
+ def validateAndParse = false
def setup() {
mockYangTextSchemaSourceSetCache.get('my dataspace', 'my schema') >> mockYangTextSchemaSourceSet
@@ -55,7 +56,7 @@ class YangParserSpec extends Specification {
def 'Parsing data.'() {
given: 'the yang parser (utility) always returns a container node'
- mockYangParserHelper.parseData(ContentType.JSON, 'some json', mockSchemaContext, noParent) >> containerNodeFromYangUtils
+ mockYangParserHelper.parseData(ContentType.JSON, 'some json', mockSchemaContext, noParent, validateAndParse) >> containerNodeFromYangUtils
when: 'parsing some json data'
def result = objectUnderTest.parseData(ContentType.JSON, 'some json', anchor, noParent)
then: 'the schema source set for the correct dataspace and schema set is retrieved form the cache'
@@ -68,7 +69,7 @@ class YangParserSpec extends Specification {
def 'Parsing data with exception on first attempt.'() {
given: 'the yang parser throws an exception on the first attempt only'
- mockYangParserHelper.parseData(ContentType.JSON, 'some json', mockSchemaContext, noParent) >> { throw new DataValidationException(noParent, noParent) } >> containerNodeFromYangUtils
+ mockYangParserHelper.parseData(ContentType.JSON, 'some json', mockSchemaContext, noParent, validateAndParse) >> { throw new DataValidationException(noParent, noParent) } >> containerNodeFromYangUtils
when: 'attempt to parse some data'
def result = objectUnderTest.parseData(ContentType.JSON, 'some json', anchor, noParent)
then: 'the cache is cleared for the correct dataspace and schema'
@@ -79,7 +80,7 @@ class YangParserSpec extends Specification {
def 'Parsing data with exception on all attempts.'() {
given: 'the yang parser always throws an exception'
- mockYangParserHelper.parseData(ContentType.JSON, 'some json', mockSchemaContext, noParent) >> { throw new DataValidationException(noParent, noParent) }
+ mockYangParserHelper.parseData(ContentType.JSON, 'some json', mockSchemaContext, noParent, validateAndParse) >> { throw new DataValidationException(noParent, noParent) }
when: 'attempt to parse some data'
objectUnderTest.parseData(ContentType.JSON, 'some json', anchor, noParent)
then: 'a data validation exception is thrown'
@@ -94,9 +95,46 @@ class YangParserSpec extends Specification {
when: 'parsing some json data'
def result = objectUnderTest.parseData(ContentType.JSON, 'some json', yangResourcesNameToContentMap, noParent)
then: 'the yang parser helper always returns a container node'
- 1 * mockYangParserHelper.parseData(ContentType.JSON, 'some json', mockSchemaContext, noParent) >> containerNodeFromYangUtils
+ 1 * mockYangParserHelper.parseData(ContentType.JSON, 'some json', mockSchemaContext, noParent, validateAndParse) >> containerNodeFromYangUtils
and: 'the result is the same container node as return from yang utils'
assert result == containerNodeFromYangUtils
}
+ def 'Validating #scenario data using Yang parser with cache retrieval.'() {
+ given: 'the yang parser (utility) is set up and schema context is available'
+ mockYangParserHelper.parseData(contentType, 'some json', mockSchemaContext, noParent, validateOnly)
+ when: 'attempt to parse data with no parent node xpath'
+ objectUnderTest.validateData(contentType, 'some json or xml data', anchor, noParent)
+ then: 'the correct schema set is retrieved from the cache for the dataspace and schema'
+ 1 * mockYangTextSchemaSourceSetCache.get('my dataspace', 'my schema') >> mockYangTextSchemaSourceSet
+ and: 'no cache entries are removed during validation'
+ 0 * mockYangTextSchemaSourceSetCache.removeFromCache(*_)
+ where:
+ scenario | contentType
+ 'JSON' | ContentType.JSON
+ 'XML' | ContentType.XML
+ }
+
+ def 'Validating data when parsing fails on first attempt and recovers.'() {
+ given: 'the Yang parser throws an exception on the first attempt but succeeds on the second'
+ mockYangParserHelper.parseData(ContentType.JSON, 'some json', mockSchemaContext, noParent, validateOnly) >> { throw new DataValidationException(noParent, noParent) } >> null
+ when: 'attempting to parse JSON data'
+ objectUnderTest.validateData(ContentType.JSON, 'some json', anchor, noParent)
+ then: 'the cache is cleared for the correct dataspace and schema after the first failure'
+ 1 * mockYangTextSchemaSourceSetCache.removeFromCache('my dataspace', 'my schema')
+ and: 'no exceptions are thrown after the second attempt'
+ noExceptionThrown()
+ }
+
+ def 'Validating data with repeated parsing failures leading to exception.'() {
+ given: 'the yang parser throws an exception on the first attempt only'
+ mockYangParserHelper.parseData(ContentType.JSON, 'some json', mockSchemaContext, noParent, validateOnly) >> { throw new DataValidationException(noParent, noParent) }
+ when: 'attempting to parse JSON data'
+ objectUnderTest.validateData(ContentType.JSON, 'some json', anchor, noParent)
+ then: 'a data validation exception is thrown'
+ thrown(DataValidationException)
+ and: 'the cache is cleared for the correct dataspace and schema after the failure'
+ 1 * mockYangTextSchemaSourceSetCache.removeFromCache('my dataspace', 'my schema')
+ }
+
}