diff options
Diffstat (limited to 'cps-service/src/test')
13 files changed, 260 insertions, 31 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/ContentTypeSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/utils/ContentTypeSpec.groovy new file mode 100644 index 0000000000..cada33ef06 --- /dev/null +++ b/cps-service/src/test/groovy/org/onap/cps/utils/ContentTypeSpec.groovy @@ -0,0 +1,37 @@ +/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2024 TechMahindra Ltd
+ * ================================================================================
+ * 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.utils;
+
+import spock.lang.Specification;
+import org.springframework.http.MediaType
+
+
+class ContentTypeSpec extends Specification {
+
+ def 'Should return correct ContentType based on given input.'() {
+ given: 'contentType fromString method converts the input string as expectedContentType'
+ ContentType.fromString(contentTypeString) == expectedContentType
+ where:
+ contentTypeString || expectedContentType
+ MediaType.APPLICATION_XML_VALUE || ContentType.XML
+ MediaType.APPLICATION_JSON_VALUE || ContentType.JSON
+ }
+
+}
diff --git a/cps-service/src/test/groovy/org/onap/cps/utils/XmlFileUtilsSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/utils/XmlFileUtilsSpec.groovy index dc6027de25..9a932c9279 100644 --- a/cps-service/src/test/groovy/org/onap/cps/utils/XmlFileUtilsSpec.groovy +++ b/cps-service/src/test/groovy/org/onap/cps/utils/XmlFileUtilsSpec.groovy @@ -2,6 +2,7 @@ * ============LICENSE_START======================================================= * Copyright (C) 2022 Deutsche Telekom AG * Modifications Copyright (c) 2023-2024 Nordix Foundation + * Modifications Copyright (C) 2024 TechMahindra Ltd. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,13 +22,17 @@ package org.onap.cps.utils import org.onap.cps.TestUtils +import org.onap.cps.spi.exceptions.DataValidationException import org.onap.cps.yang.YangTextSchemaSourceSetBuilder +import org.w3c.dom.DOMException import org.xml.sax.SAXParseException import spock.lang.Specification +import static org.onap.cps.utils.XmlFileUtils.convertDataMapsToXml + class XmlFileUtilsSpec extends Specification { - def 'Parse a valid xml content #scenario'(){ + def 'Parse a valid xml content #scenario'() { given: 'YANG model schema context' def yangResourceNameToContent = TestUtils.getYangResourcesAsMap('bookstore.yang') def schemaContext = YangTextSchemaSourceSetBuilder.of(yangResourceNameToContent).getSchemaContext() @@ -36,13 +41,13 @@ class XmlFileUtilsSpec extends Specification { then: 'the result xml is wrapped by root node defined in YANG schema' assert parsedXmlContent == expectedOutput where: - scenario | xmlData || expectedOutput - 'without root data node' | '<?xml version="1.0" encoding="UTF-8"?><class> </class>' || '<?xml version="1.0" encoding="UTF-8"?><stores xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"><class> </class></stores>' - 'with root data node' | '<?xml version="1.0" encoding="UTF-8"?><stores><class> </class></stores>' || '<?xml version="1.0" encoding="UTF-8"?><stores><class> </class></stores>' - 'no xml header' | '<stores><class> </class></stores>' || '<stores><class> </class></stores>' + scenario | xmlData || expectedOutput + 'without root data node' | '<?xml version="1.0" encoding="UTF-8"?><class> </class>' || '<?xml version="1.0" encoding="UTF-8"?><stores xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"><class> </class></stores>' + 'with root data node' | '<?xml version="1.0" encoding="UTF-8"?><stores><class> </class></stores>' || '<?xml version="1.0" encoding="UTF-8"?><stores><class> </class></stores>' + 'no xml header' | '<stores><class> </class></stores>' || '<stores><class> </class></stores>' } - def 'Parse a invalid xml content'(){ + def 'Parse a invalid xml content'() { given: 'YANG model schema context' def yangResourceNameToContent = TestUtils.getYangResourcesAsMap('bookstore.yang') def schemaContext = YangTextSchemaSourceSetBuilder.of(yangResourceNameToContent).getSchemaContext() @@ -68,4 +73,56 @@ class XmlFileUtilsSpec extends Specification { 'without root data node' | '<?xml version="1.0" encoding="UTF-8"?><nest xmlns="org:onap:cps:test:test-tree"><name>Small</name><birds>Sparrow</birds></nest>' | '/test-tree/branch[@name=\'Branch\']' || '<?xml version="1.0" encoding="UTF-8"?><branch xmlns="org:onap:cps:test:test-tree"><name>Branch</name><nest><name>Small</name><birds>Sparrow</birds></nest></branch>' } -} + def 'Convert data maps to XML #scenario'() { + when: 'data maps are converted to XML' + def result = convertDataMapsToXml(dataMaps) + then: 'the result contains the expected XML' + assert result == expectedXmlOutput + where: + scenario | dataMaps || expectedXmlOutput + 'single XML branch' | [['branch': ['name': 'Left', 'nest': ['name': 'Small', 'birds': ['Sparrow', 'Owl']]]]] || '<branch><name>Left</name><nest><name>Small</name><birds>Sparrow</birds><birds>Owl</birds></nest></branch>' + 'nested XML branch' | [['test-tree': [branch: [name: 'Left', nest: [name: 'Small', birds: 'Sparrow']]]]] || '<test-tree><branch><name>Left</name><nest><name>Small</name><birds>Sparrow</birds></nest></branch></test-tree>' + 'list of branch within a test tree' | [['test-tree': [branch: [[name: 'Left', nest: [name: 'Small', birds: 'Sparrow']], [name: 'Right', nest: [name: 'Big', birds: 'Owl']]]]]] || '<test-tree><branch><name>Left</name><nest><name>Small</name><birds>Sparrow</birds></nest></branch><branch><name>Right</name><nest><name>Big</name><birds>Owl</birds></nest></branch></test-tree>' + 'list of birds under a nest' | [['nest': ['name': 'Small', 'birds': ['Sparrow']]]] || '<nest><name>Small</name><birds>Sparrow</birds></nest>' + } + + def 'Convert data maps to XML with null or empty maps and lists'() { + when: 'data maps with empty content are converted to XML' + def result = convertDataMapsToXml(dataMaps) + then: 'the result contains the expected XML or handles nulls correctly' + assert result == expectedXmlOutput + where: + scenario | dataMaps || expectedXmlOutput + 'null entry in map' | [['branch': []]] || '<branch/>' + 'XML Content list is empty' | [['nest': ['name': 'Small', 'birds': [null]]]] || '<nest><name>Small</name><birds/></nest>' + 'XML with mixed content in list' | [['branch': ['name': 'Left', 'nest': ['name': 'Small', 'birds': [null, 'Sparrow']]]]] || '<branch><name>Left</name><nest><name>Small</name><birds/><birds>Sparrow</birds></nest></branch>' + 'list with null object' | [['branch': [name: 'Left', nest: [name: 'Small', birds: [null]]]]] || '<branch><name>Left</name><nest><name>Small</name><birds/></nest></branch>' + 'list containing null values' | [['branch': [null, null, null]]] || '<branch/><branch/><branch/>' + 'nested map with null values' | [['test-tree': [branch: [name: 'Left', nest: null]]]] || '<test-tree><branch><name>Left</name><nest/></branch></test-tree>' + 'mixed list with null values' | [['branch': ['name': 'Left', 'nest': ['name': 'Small', 'birds': [null, 'Sparrow', null]]]]] || '<branch><name>Left</name><nest><name>Small</name><birds/><birds>Sparrow</birds><birds/></nest></branch>' + } + + def 'Converting data maps to xml with no data'() { + given: 'A list of maps where entry is null' + def dataMapWithNull = [null] + when: 'convert the dataMaps to XML' + convertDataMapsToXml(dataMapWithNull) + then: 'a validation exception is thrown' + def exception = thrown(DataValidationException) + and: 'the cause is a null pointer exception' + assert exception.cause instanceof NullPointerException + } + + def 'Converting data maps to xml with document syntax error'() { + given: 'A list of maps with an invalid entry' + def dataMap = [['invalid<tag>': 'value']] + when: 'convert the dataMaps to XML' + convertDataMapsToXml(dataMap) + then: 'a validation exception is thrown' + def exception = thrown(DataValidationException) + and: 'the cause is a document object model exception' + assert exception.cause instanceof DOMException + + } + +}
\ No newline at end of file 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') + } + } diff --git a/cps-service/src/test/resources/bookstore-categories-data.json b/cps-service/src/test/resources/bookstore-categories-data.json new file mode 100644 index 0000000000..7dc22b17f7 --- /dev/null +++ b/cps-service/src/test/resources/bookstore-categories-data.json @@ -0,0 +1,49 @@ +{ + "categories": [ + { + "code": "01/1", + "name": "SciFi", + "books": [ + { + "authors": [ + "Iain M. Banks" + ], + "lang": "en/it", + "price": "895", + "pub_year": "1994", + "title": "Feersum Endjinn/Endjinn Feersum" + }, + { + "authors": [ + "Ursula K. Le Guin", + "Joe Haldeman", + "Orson Scott Card", + "david Brin", + "Rober Silverberg", + "Dan Simmons", + "Greg Bear" + ], + "lang": "en", + "price": "1099", + "pub_year": "1999", + "title": "Far Horizons" + } + ] + }, + { + "name": "kids", + "code": "02", + "books": [ + { + "authors": [ + "Philip Pullman" + ], + "lang": "en", + "price": "699", + "pub_year": "1995", + "title": "The Golden Compass" + } + ] + } + ] +}
\ No newline at end of file diff --git a/cps-service/src/test/resources/bookstore-categories-data.xml b/cps-service/src/test/resources/bookstore-categories-data.xml new file mode 100644 index 0000000000..c8592c1f90 --- /dev/null +++ b/cps-service/src/test/resources/bookstore-categories-data.xml @@ -0,0 +1,14 @@ +<?xml version='1.0' encoding='UTF-8'?> +<categories> + <code>1</code> + <name>SciFi</name> + <books> + <title>2001: A Space Odyssey</title> + <lang>en</lang> + <authors> + Iain M. Banks + </authors> + <pub_year>1994</pub_year> + <price>895</price> + </books> +</categories>
\ No newline at end of file diff --git a/cps-service/src/test/resources/e2e/basic/cps-cavsta-onap-internal2021-01-28.yang b/cps-service/src/test/resources/e2e/basic/cps-cavsta-onap-internal2021-01-28.yang index 32517398a3..2dabd79e8e 100644 --- a/cps-service/src/test/resources/e2e/basic/cps-cavsta-onap-internal2021-01-28.yang +++ b/cps-service/src/test/resources/e2e/basic/cps-cavsta-onap-internal2021-01-28.yang @@ -36,7 +36,7 @@ module cps-cavsta-onap-internal { description "RAN Network YANG Model for ONAP/O-RAN POC"; reference - "https://wiki.onap.org/display/DW/E2E+Network+Slicing+Use+Case+in+R7+Guilin"; + "https://lf-onap.atlassian.net/wiki/spaces/DW/pages/16414819/E2E+Network+Slicing+Use+Case+in+R7+Guilin"; } typedef Tac { diff --git a/cps-service/src/test/resources/e2e/basic/cps-ran-inventory@2021-01-28.yang b/cps-service/src/test/resources/e2e/basic/cps-ran-inventory@2021-01-28.yang index c16a682512..2401409443 100644 --- a/cps-service/src/test/resources/e2e/basic/cps-ran-inventory@2021-01-28.yang +++ b/cps-service/src/test/resources/e2e/basic/cps-ran-inventory@2021-01-28.yang @@ -34,7 +34,7 @@ module cps-ran-inventory { description "RAN Network YANG Model for ONAP/O-RAN POC"; reference - "https://wiki.onap.org/display/DW/E2E+Network+Slicing+Use+Case+in+R7+Guilin"; + "https://lf-onap.atlassian.net/wiki/spaces/DW/pages/16414819/E2E+Network+Slicing+Use+Case+in+R7+Guilin"; } typedef Mcc { diff --git a/cps-service/src/test/resources/e2e/basic/cps-ran-schema-model@2021-05-19.yang b/cps-service/src/test/resources/e2e/basic/cps-ran-schema-model@2021-05-19.yang index 5fd292a99d..3223b15e65 100644 --- a/cps-service/src/test/resources/e2e/basic/cps-ran-schema-model@2021-05-19.yang +++ b/cps-service/src/test/resources/e2e/basic/cps-ran-schema-model@2021-05-19.yang @@ -43,14 +43,14 @@ module cps-ran-schema-model { description "Added support for OOF PCI SON Use case"; reference - "https://wiki.onap.org/display/DW/CPS+APIs"; + "https://lf-onap.atlassian.net/wiki/spaces/DW/pages/16456851/CPS+APIs"; } revision 2021-01-28 { description "CPS RAN Network YANG Model for ONAP/O-RAN POC"; reference - "https://wiki.onap.org/display/DW/E2E+Network+Slicing+Use+Case+in+R7+Guilin"; + "https://lf-onap.atlassian.net/wiki/spaces/DW/pages/16414819/E2E+Network+Slicing+Use+Case+in+R7+Guilin"; } typedef usageState { diff --git a/cps-service/src/test/resources/e2e/basic/ran-network2020-08-06.yang b/cps-service/src/test/resources/e2e/basic/ran-network2020-08-06.yang index 5065659307..a4612e73fb 100755 --- a/cps-service/src/test/resources/e2e/basic/ran-network2020-08-06.yang +++ b/cps-service/src/test/resources/e2e/basic/ran-network2020-08-06.yang @@ -43,7 +43,7 @@ module ran-network { description "RAN Network YANG Model for ONAP/O-RAN POC"; reference - "https://wiki.onap.org/display/DW/E2E+Network+Slicing+Use+Case+in+R7+Guilin"; + "https://lf-onap.atlassian.net/wiki/spaces/DW/pages/16414819/E2E+Network+Slicing+Use+Case+in+R7+Guilin"; } typedef usageState { |