diff options
Diffstat (limited to 'cps-service/src/test')
6 files changed, 200 insertions, 16 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 b78ab8a451..c81a50ea74 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 @@ -4,7 +4,7 @@ * Modifications Copyright (C) 2021 Pantheon.tech * Modifications Copyright (C) 2021-2022 Bell Canada. * Modifications Copyright (C) 2022 TechMahindra Ltd. - * ================================================================================ + * Modifications Copyright (C) 2022 Deutsche Telekom AG * 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 @@ -33,6 +33,7 @@ import org.onap.cps.spi.exceptions.DataValidationException import org.onap.cps.spi.model.Anchor import org.onap.cps.spi.model.DataNode import org.onap.cps.spi.model.DataNodeBuilder +import org.onap.cps.utils.ContentType import org.onap.cps.yang.YangTextSchemaSourceSet import org.onap.cps.yang.YangTextSchemaSourceSetBuilder import spock.lang.Specification @@ -61,7 +62,7 @@ class CpsDataServiceImplSpec extends Specification { def anchor = Anchor.builder().name(anchorName).schemaSetName(schemaSetName).build() def observedTimestamp = OffsetDateTime.now() - def 'Saving json data.'() { + def 'Saving multicontainer json data.'() { given: 'schema set for given anchor and dataspace references test-tree model' setupSchemaSetMocks('multipleDataTree.yang') when: 'save data method is invoked with test-tree json data' @@ -81,6 +82,39 @@ class CpsDataServiceImplSpec extends Specification { } + def 'Saving #scenario data.'() { + given: 'schema set for given anchor and dataspace references test-tree model' + setupSchemaSetMocks('test-tree.yang') + when: 'save data method is invoked with test-tree #scenario data' + def data = TestUtils.getResourceFileContent(dataFile) + objectUnderTest.saveData(dataspaceName, anchorName, data, observedTimestamp, contentType) + then: 'the persistence service method is invoked with correct parameters' + 1 * mockCpsDataPersistenceService.storeDataNodes(dataspaceName, anchorName, + { dataNode -> dataNode.xpath[0] == '/test-tree' }) + and: 'the CpsValidator is called on the dataspaceName and AnchorName' + 1 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName) + and: 'data updated event is sent to notification service' + 1 * mockNotificationService.processDataUpdatedEvent(dataspaceName, anchorName, '/', Operation.CREATE, observedTimestamp) + where: 'given parameters' + scenario | dataFile | contentType + 'json' | 'test-tree.json' | ContentType.JSON + 'xml' | 'test-tree.xml' | ContentType.XML + } + + def 'Saving #scenarioDesired data with invalid data.'() { + given: 'schema set for given anchor and dataspace references test-tree model' + setupSchemaSetMocks('test-tree.yang') + when: 'save data method is invoked with test-tree json data' + objectUnderTest.saveData(dataspaceName, anchorName, invalidData, observedTimestamp, contentType) + then: 'a data validation exception is thrown' + thrown(DataValidationException) + where: 'given parameters' + scenarioDesired | invalidData | contentType + 'json' | '{invalid json' | ContentType.XML + 'xml' | '<invalid xml' | ContentType.JSON + } + + def 'Saving child data fragment under existing node.'() { given: 'schema set for given anchor and dataspace references test-tree model' setupSchemaSetMocks('test-tree.yang') 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 new file mode 100644 index 0000000000..b044e2e727 --- /dev/null +++ b/cps-service/src/test/groovy/org/onap/cps/utils/XmlFileUtilsSpec.groovy @@ -0,0 +1,61 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2022 Deutsche Telekom AG + * ================================================================================ + * 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 org.onap.cps.TestUtils +import org.onap.cps.yang.YangTextSchemaSourceSetBuilder +import spock.lang.Specification + +class XmlFileUtilsSpec extends Specification { + def 'Parse a valid xml content #scenario'(){ + given: 'YANG model schema context' + def yangResourceNameToContent = TestUtils.getYangResourcesAsMap('bookstore.yang') + def schemaContext = YangTextSchemaSourceSetBuilder.of(yangResourceNameToContent).getSchemaContext() + when: 'the XML data is parsed' + def parsedXmlContent = XmlFileUtils.prepareXmlContent(xmlData, schemaContext) + 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>' + } + + def 'Parse a xml content with XPath container #scenario'() { + given: 'YANG model schema context' + def yangResourceNameToContent = TestUtils.getYangResourcesAsMap('test-tree.yang') + def schemaContext = YangTextSchemaSourceSetBuilder.of(yangResourceNameToContent).getSchemaContext() + and: 'Parent schema node by xPath' + def parentSchemaNode = YangUtils.getDataSchemaNodeAndIdentifiersByXpath(xPath, schemaContext) + .get("dataSchemaNode") + when: 'the XML data is parsed' + def parsedXmlContent = XmlFileUtils.prepareXmlContent(xmlData, parentSchemaNode, xPath) + then: 'the result XML is wrapped by xPath defined parent root node' + assert parsedXmlContent == expectedOutput + where: + scenario | xmlData | xPath || expectedOutput + 'XML element test tree' | '<?xml version="1.0" encoding="UTF-8"?><test-tree xmlns="org:onap:cps:test:test-tree"><branch><name>Left</name><nest><name>Small</name><birds>Sparrow</birds></nest></branch></test-tree>' | '/test-tree' || '<?xml version="1.0" encoding="UTF-8"?><test-tree xmlns="org:onap:cps:test:test-tree"><branch><name>Left</name><nest><name>Small</name><birds>Sparrow</birds></nest></branch></test-tree>' + '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>' + + + } + +} diff --git a/cps-service/src/test/groovy/org/onap/cps/utils/YangUtilsSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/utils/YangUtilsSpec.groovy index 990b7186f7..bf6e134a65 100644 --- a/cps-service/src/test/groovy/org/onap/cps/utils/YangUtilsSpec.groovy +++ b/cps-service/src/test/groovy/org/onap/cps/utils/YangUtilsSpec.groovy @@ -3,6 +3,7 @@ * Copyright (C) 2020-2022 Nordix Foundation * Modifications Copyright (C) 2021 Pantheon.tech * Modifications Copyright (C) 2022 TechMahindra Ltd. + * Modifications Copyright (C) 2022 Deutsche Telekom AG * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,7 +31,7 @@ import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode import spock.lang.Specification class YangUtilsSpec extends Specification { - def 'Parsing a valid Json String.'() { + def 'Parsing a valid multicontainer Json String.'() { given: 'a yang model (file)' def jsonData = org.onap.cps.TestUtils.getResourceFileContent('multiple-object-data.json') and: 'a model for that data' @@ -48,36 +49,62 @@ class YangUtilsSpec extends Specification { 1 | 'last-container' } + def 'Parsing a valid #scenario String.'() { + given: 'a yang model (file)' + def fileData = org.onap.cps.TestUtils.getResourceFileContent(contentFile) + and: 'a model for that data' + def yangResourceNameToContent = TestUtils.getYangResourcesAsMap('bookstore.yang') + def schemaContext = YangTextSchemaSourceSetBuilder.of(yangResourceNameToContent).getSchemaContext() + when: 'the data is parsed' + NormalizedNode result = YangUtils.parseData(contentType, fileData, schemaContext) + then: 'the result is a normalized node of the correct type' + if (revision) { + result.identifier.nodeType == QName.create(namespace, revision, localName) + } else { + result.identifier.nodeType == QName.create(namespace, localName) + } + where: + scenario | contentFile | contentType | namespace | revision | localName + 'JSON' | 'bookstore.json' | ContentType.JSON | 'org:onap:ccsdk:sample' | '2020-09-15' | 'bookstore' + 'XML' | 'bookstore.xml' | ContentType.XML | 'urn:ietf:params:xml:ns:netconf:base:1.0' | '' | 'bookstore' + } + def 'Parsing invalid data: #description.'() { given: 'a yang model (file)' def yangResourceNameToContent = TestUtils.getYangResourcesAsMap('bookstore.yang') def schemaContext = YangTextSchemaSourceSetBuilder.of(yangResourceNameToContent).getSchemaContext() when: 'invalid data is parsed' - YangUtils.parseJsonData(invalidJson, schemaContext) + YangUtils.parseData(contentType, invalidData, schemaContext) then: 'an exception is thrown' thrown(DataValidationException) - where: 'the following invalid json is provided' - invalidJson | description - '{incomplete json' | 'incomplete json' - '{"test:bookstore": {"address": "Parnell st." }}' | 'json with un-modelled data' - '{" }' | 'json with syntax exception' + where: 'the following invalid data is provided' + invalidData | contentType | description + '{incomplete json' | ContentType.JSON | 'incomplete json' + '{"test:bookstore": {"address": "Parnell st." }}' | ContentType.JSON | 'json with un-modelled data' + '{" }' | ContentType.JSON | 'json with syntax exception' + '<data>' | ContentType.XML | 'incomplete xml' + '<data><bookstore><bookstore-anything>blabla</bookstore-anything></bookstore</data>' | ContentType.XML | 'xml with invalid model' + '' | ContentType.XML | 'empty xml' } - def 'Parsing json data fragment by xpath for #scenario.'() { + def 'Parsing data fragment by xpath for #scenario.'() { given: 'schema context' def yangResourcesMap = TestUtils.getYangResourcesAsMap('test-tree.yang') def schemaContext = YangTextSchemaSourceSetBuilder.of(yangResourcesMap).getSchemaContext() when: 'json string is parsed' - def result = YangUtils.parseJsonData(jsonData, schemaContext, parentNodeXpath) + def result = YangUtils.parseData(contentType, nodeData, schemaContext, parentNodeXpath) 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' result.body().getAt(0).getIdentifier().nodeType == QName.create('org:onap:cps:test:test-tree', '2020-02-02', nodeName) where: - scenario | jsonData | parentNodeXpath || nodeName - 'list element as container' | '{ "branch": { "name": "B", "nest": { "name": "N", "birds": ["bird"] } } }' | '/test-tree' || 'branch' - 'list element within list' | '{ "branch": [{ "name": "B", "nest": { "name": "N", "birds": ["bird"] } }] }' | '/test-tree' || 'branch' - 'container element' | '{ "nest": { "name": "N", "birds": ["bird"] } }' | '/test-tree/branch[@name=\'Branch\']' || 'nest' + scenario | contentType | nodeData | parentNodeXpath || nodeName + 'JSON list element as container' | ContentType.JSON | '{ "branch": { "name": "B", "nest": { "name": "N", "birds": ["bird"] } } }' | '/test-tree' || 'branch' + 'JSON list element within list' | ContentType.JSON | '{ "branch": [{ "name": "B", "nest": { "name": "N", "birds": ["bird"] } }] }' | '/test-tree' || 'branch' + 'JSON container element' | ContentType.JSON | '{ "nest": { "name": "N", "birds": ["bird"] } }' | '/test-tree/branch[@name=\'Branch\']' || 'nest' + 'XML element test tree' | ContentType.XML | '<?xml version=\'1.0\' encoding=\'UTF-8\'?><branch xmlns="org:onap:cps:test:test-tree"><name>Left</name><nest><name>Small</name><birds>Sparrow</birds></nest></branch>' | '/test-tree' || 'branch' + 'XML element branch xpath' | ContentType.XML | '<?xml version=\'1.0\' encoding=\'UTF-8\'?><branch xmlns="org:onap:cps:test:test-tree"><name>Left</name><nest><name>Small</name><birds>Sparrow</birds><birds>Robin</birds></nest></branch>' | '/test-tree' || 'branch' + 'XML container element' | ContentType.XML | '<?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\']' || 'nest' } def 'Parsing json data fragment by xpath error scenario: #scenario.'() { @@ -135,5 +162,4 @@ class YangUtilsSpec extends Specification { 'xpath contains list attribute' | '/test-tree/branch[@name=\'Branch\']' || ['test-tree','branch'] 'xpath contains list attributes with /' | '/test-tree/branch[@name=\'/Branch\']/categories[@id=\'/broken\']' || ['test-tree','branch','categories'] } - } diff --git a/cps-service/src/test/resources/bookstore.xml b/cps-service/src/test/resources/bookstore.xml new file mode 100644 index 0000000000..dd45e16896 --- /dev/null +++ b/cps-service/src/test/resources/bookstore.xml @@ -0,0 +1,19 @@ +<?xml version='1.0' encoding='UTF-8'?> +<stores xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"> +<bookstore xmlns="org:onap:ccsdk:sample"> + <bookstore-name>Chapters</bookstore-name> + <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> +</bookstore> +</stores>
\ No newline at end of file diff --git a/cps-service/src/test/resources/bookstore_xpath.xml b/cps-service/src/test/resources/bookstore_xpath.xml new file mode 100644 index 0000000000..e206901d6d --- /dev/null +++ b/cps-service/src/test/resources/bookstore_xpath.xml @@ -0,0 +1,17 @@ +<?xml version='1.0' encoding='UTF-8'?> +<bookstore xmlns="org:onap:ccsdk:sample"> + <bookstore-name>Chapters</bookstore-name> + <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> +</bookstore>
\ No newline at end of file diff --git a/cps-service/src/test/resources/test-tree.xml b/cps-service/src/test/resources/test-tree.xml new file mode 100644 index 0000000000..3daa814cf6 --- /dev/null +++ b/cps-service/src/test/resources/test-tree.xml @@ -0,0 +1,27 @@ +<?xml version='1.0' encoding='UTF-8'?> +<data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"> + <test-tree xmlns="org:onap:cps:test:test-tree"> + <branch> + <name>Left</name> + <nest> + <name>Small</name> + <birds>Sparrow</birds> + <birds>Robin</birds> + <birds>Finch</birds> + </nest> + </branch> + <branch> + <name>Right</name> + <nest> + <name>Big</name> + <birds>Owl</birds> + <birds>Raven</birds> + <birds>Crow</birds> + </nest> + </branch> + <fruit> + <name>Apple</name> + <color>Green</color> + </fruit> + </test-tree> +</data> |