aboutsummaryrefslogtreecommitdiffstats
path: root/cps-service/src/test
diff options
context:
space:
mode:
authorToineSiebelink <toine.siebelink@est.tech>2023-07-26 17:49:02 +0100
committerToineSiebelink <toine.siebelink@est.tech>2023-07-31 08:57:30 +0100
commite3cdc8a0591553da6d022337fa69c8dd507510f6 (patch)
tree6c72936bc39e00d2b9821def0622e83165c1cb8d /cps-service/src/test
parent92bf624e75673f8027ba48bf4f8c2d28b3b01552 (diff)
Increase code coverage in cps-service module
- After last rebase I had to remove 3 unused recent cloud eventd specific exceptions/constructors - Moved the only used new exception from SPI to the relevant util package (please NOTE not all exceptions belong in SPI and always question need for new exception when there is no specific handling, try to use standard or existign CPS exception instead!) - Increased cps-service module (line) coverage from 95 to 100% - Added tests for missing exceptions (handling i.e. thrown up) - Removed incorrect SPI defined OperationNotYetSupportedException (replaced with standard java exception instead) - Fixed some legacy issues with existign test classes I modified (unnecessary setup, conventions etc) - Increased coverage for DataNodeBuilder - Added or modified test to include more spi models - Added tests for Hazelcast Configs - Added more tests for json object mapper - Added test and fixed error handling in YangUtils/XmlFileUtils (it was incorrectly converting a config exception to a data validation exception) Issue-ID: CPS-475 Signed-off-by: ToineSiebelink <toine.siebelink@est.tech> Change-Id: I5852ba01bc5b33ae361b8f29daae9868f05baa35
Diffstat (limited to 'cps-service/src/test')
-rwxr-xr-xcps-service/src/test/groovy/org/onap/cps/api/impl/CpsAdminServiceImplSpec.groovy16
-rw-r--r--cps-service/src/test/groovy/org/onap/cps/api/impl/CpsDataServiceImplSpec.groovy88
-rw-r--r--cps-service/src/test/groovy/org/onap/cps/api/impl/CpsModuleServiceImplSpec.groovy43
-rw-r--r--cps-service/src/test/groovy/org/onap/cps/cache/HazelcastCacheConfigSpec.groovy54
-rw-r--r--cps-service/src/test/groovy/org/onap/cps/config/CacheConfigSpec.groovy32
-rw-r--r--cps-service/src/test/groovy/org/onap/cps/notification/CpsDataUpdatedEventFactorySpec.groovy (renamed from cps-service/src/test/groovy/org/onap/cps/notification/CpsDataUpdateEventFactorySpec.groovy)22
-rw-r--r--cps-service/src/test/groovy/org/onap/cps/notification/NotificationErrorHandlerSpec.groovy22
-rw-r--r--cps-service/src/test/groovy/org/onap/cps/notification/NotificationServiceSpec.groovy13
-rw-r--r--cps-service/src/test/groovy/org/onap/cps/spi/FetchDescendantsOptionSpec.groovy20
-rw-r--r--cps-service/src/test/groovy/org/onap/cps/spi/model/ConditionPropertiesSpec.groovy38
-rw-r--r--cps-service/src/test/groovy/org/onap/cps/spi/model/DataNodeBuilderSpec.groovy87
-rw-r--r--cps-service/src/test/groovy/org/onap/cps/utils/JsonObjectMapperSpec.groovy38
-rw-r--r--cps-service/src/test/groovy/org/onap/cps/utils/XmlFileUtilsSpec.groovy22
-rw-r--r--cps-service/src/test/groovy/org/onap/cps/utils/YangUtilsSpec.groovy11
-rw-r--r--cps-service/src/test/groovy/org/onap/cps/yang/YangTextSchemaSourceSetBuilderSpec.groovy16
15 files changed, 411 insertions, 111 deletions
diff --git a/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsAdminServiceImplSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsAdminServiceImplSpec.groovy
index 4e0349d2b..eb41e2085 100755
--- a/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsAdminServiceImplSpec.groovy
+++ b/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsAdminServiceImplSpec.groovy
@@ -25,6 +25,7 @@ package org.onap.cps.api.impl
import org.onap.cps.api.CpsDataService
import org.onap.cps.spi.CpsAdminPersistenceService
+import org.onap.cps.spi.exceptions.ModuleNamesNotFoundException
import org.onap.cps.spi.model.Anchor
import org.onap.cps.spi.model.Dataspace
import org.onap.cps.spi.utils.CpsValidator
@@ -154,6 +155,21 @@ class CpsAdminServiceImplSpec extends Specification {
1 * mockCpsValidator.validateNameCharacters('some-dataspace-name')
}
+ def 'Query all anchors with Module Names Not Found Exception in persistence layer.'() {
+ given: 'the persistence layer throws a Module Names Not Found Exception'
+ def originalException = new ModuleNamesNotFoundException('exception-ds', [ 'm1', 'm2'])
+ mockCpsAdminPersistenceService.queryAnchors(*_) >> { throw originalException}
+ when: 'attempt query anchors'
+ objectUnderTest.queryAnchorNames('some-dataspace-name', [])
+ then: 'the same exception is thrown (up)'
+ def thrownUp = thrown(ModuleNamesNotFoundException)
+ assert thrownUp == originalException
+ and: 'the exception details contains the relevant data'
+ assert thrownUp.details.contains('exception-ds')
+ assert thrownUp.details.contains('m1')
+ assert thrownUp.details.contains('m2')
+ }
+
def 'Delete dataspace.'() {
when: 'delete dataspace is invoked'
objectUnderTest.deleteDataspace('someDataspace')
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 ba438496f..cb95fb6bf 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
@@ -29,7 +29,11 @@ import org.onap.cps.notification.NotificationService
import org.onap.cps.notification.Operation
import org.onap.cps.spi.CpsDataPersistenceService
import org.onap.cps.spi.FetchDescendantsOption
+import org.onap.cps.spi.exceptions.ConcurrencyException
+import org.onap.cps.spi.exceptions.DataNodeNotFoundExceptionBatch
import org.onap.cps.spi.exceptions.DataValidationException
+import org.onap.cps.spi.exceptions.SessionManagerException
+import org.onap.cps.spi.exceptions.SessionTimeoutException
import org.onap.cps.spi.model.Anchor
import org.onap.cps.spi.model.DataNode
import org.onap.cps.spi.model.DataNodeBuilder
@@ -333,6 +337,18 @@ class CpsDataServiceImplSpec extends Specification {
'level 2 node' | ['/test-tree' : '{"branch": [{"name":"Name"}]}', '/test-tree/branch[@name=\'Name\']':'{"nest":{"name":"nestName"}}'] || ["/test-tree/branch[@name='Name']", "/test-tree/branch[@name='Name']/nest"]
}
+ def 'Replace data node with concurrency exception in persistence layer.'() {
+ given: 'the persistence layer throws an concurrency exception'
+ def originalException = new ConcurrencyException('message', 'details')
+ mockCpsDataPersistenceService.updateDataNodesAndDescendants(*_) >> { throw originalException }
+ setupSchemaSetMocks('test-tree.yang')
+ when: 'attempt to replace data node'
+ objectUnderTest.updateDataNodesAndDescendants(dataspaceName, anchorName, ['/' : '{"test-tree": {}}'] , observedTimestamp)
+ then: 'the same exception is thrown up'
+ def thrownUp = thrown(ConcurrencyException)
+ assert thrownUp == originalException
+ }
+
def 'Replace list content data fragment under parent node.'() {
given: 'schema set for given anchor and dataspace references test-tree model'
setupSchemaSetMocks('test-tree.yang')
@@ -366,8 +382,6 @@ class CpsDataServiceImplSpec extends Specification {
}
def 'Delete list element under existing node.'() {
- given: 'schema set for given anchor and dataspace references test-tree model'
- setupSchemaSetMocks('test-tree.yang')
when: 'delete list data method is invoked with list element json data'
objectUnderTest.deleteListOrListElement(dataspaceName, anchorName, '/test-tree/branch', observedTimestamp)
then: 'the persistence service method is invoked with correct parameters'
@@ -379,8 +393,6 @@ class CpsDataServiceImplSpec extends Specification {
}
def 'Delete multiple list elements under existing node.'() {
- given: 'schema set for given anchor and dataspace references test-tree model'
- setupSchemaSetMocks('test-tree.yang')
when: 'delete multiple list data method is invoked with list element json data'
objectUnderTest.deleteDataNodes(dataspaceName, anchorName, ['/test-tree/branch[@name="A"]', '/test-tree/branch[@name="B"]'], observedTimestamp)
then: 'the persistence service method is invoked with correct parameters'
@@ -392,8 +404,6 @@ class CpsDataServiceImplSpec extends Specification {
}
def 'Delete data node under anchor and dataspace.'() {
- given: 'schema set for given anchor and dataspace references test tree model'
- setupSchemaSetMocks('test-tree.yang')
when: 'delete data node method is invoked with correct parameters'
objectUnderTest.deleteDataNode(dataspaceName, anchorName, '/data-node', observedTimestamp)
then: 'the persistence service method is invoked with the correct parameters'
@@ -405,9 +415,7 @@ class CpsDataServiceImplSpec extends Specification {
}
def 'Delete all data nodes for a given anchor and dataspace.'() {
- given: 'schema set for given anchor and dataspace references test tree model'
- setupSchemaSetMocks('test-tree.yang')
- when: 'delete data node method is invoked with correct parameters'
+ when: 'delete data nodes method is invoked with correct parameters'
objectUnderTest.deleteDataNodes(dataspaceName, anchorName, observedTimestamp)
then: 'data updated event is sent to notification service before the delete'
1 * mockNotificationService.processDataUpdatedEvent(anchor, '/', Operation.DELETE, observedTimestamp)
@@ -417,6 +425,20 @@ class CpsDataServiceImplSpec extends Specification {
1 * mockCpsDataPersistenceService.deleteDataNodes(dataspaceName, anchorName)
}
+ def 'Delete all data nodes for a given anchor and dataspace with batch exception in persistence layer.'() {
+ given: 'a batch exception in persistence layer'
+ def originalException = new DataNodeNotFoundExceptionBatch('ds1','a1',[])
+ mockCpsDataPersistenceService.deleteDataNodes(*_) >> { throw originalException }
+ when: 'attempt to delete data nodes'
+ objectUnderTest.deleteDataNodes(dataspaceName, anchorName, observedTimestamp)
+ then: 'the original exception is thrown up'
+ def thrownUp = thrown(DataNodeNotFoundExceptionBatch)
+ assert thrownUp == originalException
+ and: 'the exception details contain the expected data'
+ assert thrownUp.details.contains('ds1')
+ assert thrownUp.details.contains('a1')
+ }
+
def 'Delete all data nodes for given dataspace and multiple anchors.'() {
given: 'schema set for given anchors and dataspace references test tree model'
setupSchemaSetMocks('test-tree.yang')
@@ -433,22 +455,28 @@ class CpsDataServiceImplSpec extends Specification {
1 * mockCpsDataPersistenceService.deleteDataNodes(dataspaceName, _ as Collection<String>)
}
- def setupSchemaSetMocks(String... yangResources) {
- def mockYangTextSchemaSourceSet = Mock(YangTextSchemaSourceSet)
- mockYangTextSchemaSourceSetCache.get(dataspaceName, schemaSetName) >> mockYangTextSchemaSourceSet
- def yangResourceNameToContent = TestUtils.getYangResourcesAsMap(yangResources)
- def schemaContext = YangTextSchemaSourceSetBuilder.of(yangResourceNameToContent).getSchemaContext()
- mockYangTextSchemaSourceSet.getSchemaContext() >> schemaContext
- }
-
- def 'start session'() {
+ def 'Start session.'() {
when: 'start session method is called'
objectUnderTest.startSession()
then: 'the persistence service method to start session is invoked'
1 * mockCpsDataPersistenceService.startSession()
}
- def 'close session'(){
+ def 'Start session with Session Manager Exceptions.'() {
+ given: 'the persistence layer throws an Session Manager Exception'
+ mockCpsDataPersistenceService.startSession() >> { throw originalException }
+ when: 'attempt to start session'
+ objectUnderTest.startSession()
+ then: 'the original exception is thrown up'
+ def thrownUp = thrown(SessionManagerException)
+ assert thrownUp == originalException
+ where: 'variations of Session Manager Exception are used'
+ originalException << [ new SessionManagerException('message','details'),
+ new SessionManagerException('message','details', new Exception('cause')),
+ new SessionTimeoutException('message','details', new Exception('cause'))]
+ }
+
+ def 'Close session.'(){
given: 'session Id from calling the start session method'
def sessionId = objectUnderTest.startSession()
when: 'close session method is called'
@@ -457,20 +485,26 @@ class CpsDataServiceImplSpec extends Specification {
1 * mockCpsDataPersistenceService.closeSession(sessionId)
}
- def 'lock anchor with no timeout parameter'(){
+ def 'Lock anchor with no timeout parameter.'(){
when: 'lock anchor method with no timeout parameter with details of anchor entity to lock'
objectUnderTest.lockAnchor('some-sessionId', 'some-dataspaceName', 'some-anchorName')
then: 'the persistence service method to lock anchor is invoked with default timeout'
- 1 * mockCpsDataPersistenceService.lockAnchor('some-sessionId', 'some-dataspaceName',
- 'some-anchorName', 300L)
+ 1 * mockCpsDataPersistenceService.lockAnchor('some-sessionId', 'some-dataspaceName', 'some-anchorName', 300L)
}
- def 'lock anchor with timeout parameter'(){
+ def 'Lock anchor with timeout parameter.'(){
when: 'lock anchor method with timeout parameter is called with details of anchor entity to lock'
- objectUnderTest.lockAnchor('some-sessionId', 'some-dataspaceName',
- 'some-anchorName', 250L)
+ objectUnderTest.lockAnchor('some-sessionId', 'some-dataspaceName', 'some-anchorName', 250L)
then: 'the persistence service method to lock anchor is invoked with the given timeout'
- 1 * mockCpsDataPersistenceService.lockAnchor('some-sessionId', 'some-dataspaceName',
- 'some-anchorName', 250L)
+ 1 * mockCpsDataPersistenceService.lockAnchor('some-sessionId', 'some-dataspaceName', 'some-anchorName', 250L)
+ }
+
+ def setupSchemaSetMocks(String... yangResources) {
+ def mockYangTextSchemaSourceSet = Mock(YangTextSchemaSourceSet)
+ mockYangTextSchemaSourceSetCache.get(dataspaceName, schemaSetName) >> mockYangTextSchemaSourceSet
+ def yangResourceNameToContent = TestUtils.getYangResourcesAsMap(yangResources)
+ def schemaContext = YangTextSchemaSourceSetBuilder.of(yangResourceNameToContent).getSchemaContext()
+ mockYangTextSchemaSourceSet.getSchemaContext() >> schemaContext
}
+
}
diff --git a/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsModuleServiceImplSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsModuleServiceImplSpec.groovy
index 3884eda66..a794c58fc 100644
--- a/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsModuleServiceImplSpec.groovy
+++ b/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsModuleServiceImplSpec.groovy
@@ -26,8 +26,10 @@ package org.onap.cps.api.impl
import org.onap.cps.TestUtils
import org.onap.cps.api.CpsAdminService
import org.onap.cps.spi.CpsModulePersistenceService
+import org.onap.cps.spi.exceptions.DuplicatedYangResourceException
import org.onap.cps.spi.exceptions.ModelValidationException
import org.onap.cps.spi.exceptions.SchemaSetInUseException
+import org.onap.cps.spi.model.ModuleDefinition
import org.onap.cps.spi.utils.CpsValidator
import org.onap.cps.spi.model.Anchor
import org.onap.cps.spi.model.ModuleReference
@@ -50,24 +52,22 @@ class CpsModuleServiceImplSpec extends Specification {
def objectUnderTest = new CpsModuleServiceImpl(mockCpsModulePersistenceService, mockYangTextSchemaSourceSetCache, mockCpsAdminService, mockCpsValidator,timedYangTextSchemaSourceSetBuilder)
def 'Create schema set.'() {
- given: 'Valid yang resource as name-to-content map'
- def yangResourcesNameToContentMap = TestUtils.getYangResourcesAsMap('bookstore.yang')
when: 'Create schema set method is invoked'
- objectUnderTest.createSchemaSet('someDataspace', 'someSchemaSet', yangResourcesNameToContentMap)
+ objectUnderTest.createSchemaSet('someDataspace', 'someSchemaSet', [:])
then: 'Parameters are validated and processing is delegated to persistence service'
- 1 * mockCpsModulePersistenceService.storeSchemaSet('someDataspace', 'someSchemaSet', yangResourcesNameToContentMap)
+ 1 * mockCpsModulePersistenceService.storeSchemaSet('someDataspace', 'someSchemaSet', [:])
and: 'the CpsValidator is called on the dataspaceName and schemaSetName'
1 * mockCpsValidator.validateNameCharacters('someDataspace', 'someSchemaSet')
}
def 'Create schema set from new modules and existing modules.'() {
given: 'a list of existing modules module reference'
- def moduleReferenceForExistingModule = new ModuleReference("test", "2021-10-12","test.org")
+ def moduleReferenceForExistingModule = new ModuleReference('test', '2021-10-12','test.org')
def listOfExistingModulesModuleReference = [moduleReferenceForExistingModule]
when: 'create schema set from modules method is invoked'
- objectUnderTest.createSchemaSetFromModules("someDataspaceName", "someSchemaSetName", [newModule: "newContent"], listOfExistingModulesModuleReference)
+ objectUnderTest.createSchemaSetFromModules('someDataspaceName', 'someSchemaSetName', [newModule: 'newContent'], listOfExistingModulesModuleReference)
then: 'processing is delegated to persistence service'
- 1 * mockCpsModulePersistenceService.storeSchemaSetFromModules("someDataspaceName", "someSchemaSetName", [newModule: "newContent"], listOfExistingModulesModuleReference)
+ 1 * mockCpsModulePersistenceService.storeSchemaSetFromModules('someDataspaceName', 'someSchemaSetName', [newModule: 'newContent'], listOfExistingModulesModuleReference)
and: 'the CpsValidator is called on the dataspaceName and schemaSetName'
1 * mockCpsValidator.validateNameCharacters('someDataspaceName', 'someSchemaSetName')
}
@@ -78,7 +78,21 @@ class CpsModuleServiceImplSpec extends Specification {
when: 'Create schema set method is invoked'
objectUnderTest.createSchemaSet('someDataspace', 'someSchemaSet', yangResourcesNameToContentMap)
then: 'Model validation exception is thrown'
- thrown(ModelValidationException.class)
+ thrown(ModelValidationException)
+ }
+
+ def 'Create schema set with duplicate yang resource exception in persistence layer.'() {
+ given: 'the persistence layer throws an duplicated yang resource exception'
+ def originalException = new DuplicatedYangResourceException('name', '123', null)
+ mockCpsModulePersistenceService.storeSchemaSet(*_) >> { throw originalException }
+ when: 'attempt to create schema set'
+ objectUnderTest.createSchemaSet('someDataspace', 'someSchemaSet', [:])
+ then: 'the same duplicated yang resource exception is thrown (up)'
+ def thrownUp = thrown(DuplicatedYangResourceException)
+ assert thrownUp == originalException
+ and: 'the exception message contains the relevant data'
+ assert thrownUp.message.contains('name')
+ assert thrownUp.message.contains('123')
}
def 'Get schema set by name and dataspace.'() {
@@ -212,20 +226,23 @@ class CpsModuleServiceImplSpec extends Specification {
1 * mockCpsValidator.validateNameCharacters('someDataspaceName', 'someAnchorName')
}
- def 'Identifying new module references'(){
+ def 'Identifying new module references.'(){
given: 'module references from cm handle'
def moduleReferencesToCheck = [new ModuleReference('some-module', 'some-revision')]
when: 'identifyNewModuleReferences is called'
objectUnderTest.identifyNewModuleReferences(moduleReferencesToCheck)
then: 'cps module persistence service is called with module references to check'
- 1 * mockCpsModulePersistenceService.identifyNewModuleReferences(moduleReferencesToCheck);
+ 1 * mockCpsModulePersistenceService.identifyNewModuleReferences(moduleReferencesToCheck)
}
def 'Getting module definitions.'() {
+ given: 'the module persistence service returns a collection of module definitions'
+ def moduleDefinitionsFromPersistenceService = [ new ModuleDefinition('name', 'revision', 'content' ) ]
+ mockCpsModulePersistenceService.getYangResourceDefinitions('some-dataspace-name', 'some-anchor-name') >> moduleDefinitionsFromPersistenceService
when: 'get module definitions method is called with a valid dataspace and anchor name'
- objectUnderTest.getModuleDefinitionsByAnchorName('some-dataspace-name', 'some-anchor-name')
- then: 'CPS module persistence service is invoked the correct number of times'
- 1 * mockCpsModulePersistenceService.getYangResourceDefinitions('some-dataspace-name', 'some-anchor-name')
+ def result = objectUnderTest.getModuleDefinitionsByAnchorName('some-dataspace-name', 'some-anchor-name')
+ then: 'the result is the same collection returned by the persistence service'
+ assert result == moduleDefinitionsFromPersistenceService
and: 'the CpsValidator is called on the dataspaceName and schemaSetName'
1 * mockCpsValidator.validateNameCharacters('some-dataspace-name', 'some-anchor-name')
}
diff --git a/cps-service/src/test/groovy/org/onap/cps/cache/HazelcastCacheConfigSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/cache/HazelcastCacheConfigSpec.groovy
new file mode 100644
index 000000000..8efd48547
--- /dev/null
+++ b/cps-service/src/test/groovy/org/onap/cps/cache/HazelcastCacheConfigSpec.groovy
@@ -0,0 +1,54 @@
+/*
+ * ============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.cache
+
+import spock.lang.Specification
+
+class HazelcastCacheConfigSpec extends Specification {
+
+ def objectUnderTest = new HazelcastCacheConfig()
+
+ def 'Create Hazelcast instance with a #scenario'() {
+ given: 'a cluster name'
+ objectUnderTest.clusterName = 'my cluster'
+ when: 'an hazelcast instance is created (name has to be unique)'
+ def result = objectUnderTest.createHazelcastInstance(scenario, config)
+ then: 'the instance is created and has the correct name'
+ assert result.name == scenario
+ and: 'if applicable it has a map config with the expected name'
+ if (expectMapConfig) {
+ assert result.config.mapConfigs.values()[0].name == 'my map config'
+ } else {
+ assert result.config.mapConfigs.isEmpty()
+ }
+ and: 'if applicable it has a queue config with the expected name'
+ if (expectQueueConfig) {
+ assert result.config.queueConfigs.values()[0].name == 'my queue config'
+ } else {
+ assert result.config.queueConfigs.isEmpty()
+ }
+ where: 'the following configs are used'
+ scenario | config || expectMapConfig | expectQueueConfig
+ 'Map Config' | HazelcastCacheConfig.createMapConfig('my map config') || true | false
+ 'Queue Config' | HazelcastCacheConfig.createQueueConfig('my queue config') || false | true
+ }
+
+}
diff --git a/cps-service/src/test/groovy/org/onap/cps/config/CacheConfigSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/config/CacheConfigSpec.groovy
new file mode 100644
index 000000000..b1880d50f
--- /dev/null
+++ b/cps-service/src/test/groovy/org/onap/cps/config/CacheConfigSpec.groovy
@@ -0,0 +1,32 @@
+/*
+ * ============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.config
+
+import spock.lang.Specification
+
+class CacheConfigSpec extends Specification {
+
+ def 'Create Cache Config. (easiest test ever)'() {
+ expect: 'can create a Cache Config'
+ new CacheConfig() != null
+ }
+
+}
diff --git a/cps-service/src/test/groovy/org/onap/cps/notification/CpsDataUpdateEventFactorySpec.groovy b/cps-service/src/test/groovy/org/onap/cps/notification/CpsDataUpdatedEventFactorySpec.groovy
index 5dbc2bb04..49f4bf385 100644
--- a/cps-service/src/test/groovy/org/onap/cps/notification/CpsDataUpdateEventFactorySpec.groovy
+++ b/cps-service/src/test/groovy/org/onap/cps/notification/CpsDataUpdatedEventFactorySpec.groovy
@@ -1,7 +1,7 @@
/*
* ============LICENSE_START=======================================================
* Copyright (c) 2021-2022 Bell Canada.
- * Modifications Copyright (c) 2022 Nordix Foundation
+ * Modifications Copyright (c) 2022-2023 Nordix Foundation
* Modifications Copyright (C) 2023 TechMahindra Ltd.
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -22,6 +22,8 @@
package org.onap.cps.notification
+import org.onap.cps.spi.model.DataNode
+
import java.time.OffsetDateTime
import java.time.format.DateTimeFormatter
import org.onap.cps.utils.DateTimeUtility
@@ -35,7 +37,7 @@ import org.onap.cps.spi.model.DataNodeBuilder
import org.springframework.util.StringUtils
import spock.lang.Specification
-class CpsDataUpdateEventFactorySpec extends Specification {
+class CpsDataUpdatedEventFactorySpec extends Specification {
def mockCpsDataService = Mock(CpsDataService)
@@ -112,6 +114,22 @@ class CpsDataUpdateEventFactorySpec extends Specification {
}
}
+ def 'Create CPS Data Event with URI Syntax Exception'() {
+ given: 'an anchor'
+ def anchor = new Anchor('my-anchorname', 'my-dataspace', 'my-schemaset-name')
+ and: 'a mocked data Node (collection)'
+ def mockDataNode = Mock(DataNode)
+ mockCpsDataService.getDataNodes(*_) >> [ mockDataNode ]
+ and: 'a URI syntax exception is thrown somewhere (using datanode as cannot manipulate hardcoded URIs'
+ def originalException = new URISyntaxException('input', 'reason', 0)
+ mockDataNode.getXpath() >> { throw originalException }
+ when: 'attempt to create data updated event'
+ objectUnderTest.createCpsDataUpdatedEvent(anchor, OffsetDateTime.now(), Operation.UPDATE)
+ then: 'the same exception is thrown up'
+ def thrownUp = thrown(URISyntaxException)
+ assert thrownUp == originalException
+ }
+
def isExpectedDateTimeFormat(String observedTimestamp) {
try {
DateTimeFormatter.ofPattern(dateTimeFormat).parse(observedTimestamp)
diff --git a/cps-service/src/test/groovy/org/onap/cps/notification/NotificationErrorHandlerSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/notification/NotificationErrorHandlerSpec.groovy
index d0cd47383..89e305aed 100644
--- a/cps-service/src/test/groovy/org/onap/cps/notification/NotificationErrorHandlerSpec.groovy
+++ b/cps-service/src/test/groovy/org/onap/cps/notification/NotificationErrorHandlerSpec.groovy
@@ -1,6 +1,6 @@
/*
* ============LICENSE_START=======================================================
- * Copyright (C) 2022 Nordix Foundation
+ * Copyright (C) 2022-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.
@@ -44,15 +44,17 @@ class NotificationErrorHandlerSpec extends Specification{
((Logger) LoggerFactory.getLogger(NotificationErrorHandler.class)).detachAndStopAllAppenders();
}
- def 'Logging exception via notification error handler'() {
- when: 'some exception occurs'
- objectUnderTest.onException(new Exception('sample exception'), 'some context')
+ def 'Logging exception via notification error handler #scenario'() {
+ when: 'exception #scenario occurs'
+ objectUnderTest.onException(exception, 'some context')
then: 'log output results contains the correct error details'
- def logMessage = logWatcher.list.get(0).getFormattedMessage()
- logMessage.contains(
- "Failed to process \n" +
- " Error cause: sample exception \n" +
- " Error context: [some context]")
+ def logMessage = logWatcher.list[0].getFormattedMessage()
+ assert logMessage.contains('Failed to process')
+ assert logMessage.contains("Error cause: ${exptectedCauseString}")
+ assert logMessage.contains("Error context: [some context]")
+ where:
+ scenario | exception || exptectedCauseString
+ 'with cause' | new Exception('message') || 'message'
+ 'without cause' | new Exception('message', new RuntimeException('cause')) || 'java.lang.RuntimeException: cause'
}
}
-
diff --git a/cps-service/src/test/groovy/org/onap/cps/notification/NotificationServiceSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/notification/NotificationServiceSpec.groovy
index 2ef468bb5..f07f89b39 100644
--- a/cps-service/src/test/groovy/org/onap/cps/notification/NotificationServiceSpec.groovy
+++ b/cps-service/src/test/groovy/org/onap/cps/notification/NotificationServiceSpec.groovy
@@ -42,14 +42,14 @@ import java.util.concurrent.TimeUnit
@ContextConfiguration(classes = [NotificationProperties, NotificationService, NotificationErrorHandler, AsyncConfig])
class NotificationServiceSpec extends Specification {
+ @SpringSpy
+ NotificationProperties spyNotificationProperties
@SpringBean
NotificationPublisher mockNotificationPublisher = Mock()
@SpringBean
CpsDataUpdatedEventFactory mockCpsDataUpdatedEventFactory = Mock()
@SpringSpy
NotificationErrorHandler spyNotificationErrorHandler
- @SpringSpy
- NotificationProperties spyNotificationProperties
@SpringBean
CpsAdminService mockCpsAdminService = Mock()
@@ -146,4 +146,13 @@ class NotificationServiceSpec extends Specification {
notThrown Exception
1 * spyNotificationErrorHandler.onException(_, _, _, '/', Operation.CREATE)
}
+
+ def 'Disabled Notification services'() {
+ given: 'a notification service that is disabled'
+ spyNotificationProperties.enabled >> false
+ NotificationService notificationService = new NotificationService(spyNotificationProperties, mockNotificationPublisher, mockCpsDataUpdatedEventFactory, spyNotificationErrorHandler, mockCpsAdminService)
+ notificationService.init()
+ expect: 'it will not send notifications'
+ assert notificationService.shouldSendNotification('') == false
+ }
}
diff --git a/cps-service/src/test/groovy/org/onap/cps/spi/FetchDescendantsOptionSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/spi/FetchDescendantsOptionSpec.groovy
index b095bfd3d..28bf38fb5 100644
--- a/cps-service/src/test/groovy/org/onap/cps/spi/FetchDescendantsOptionSpec.groovy
+++ b/cps-service/src/test/groovy/org/onap/cps/spi/FetchDescendantsOptionSpec.groovy
@@ -21,6 +21,7 @@
package org.onap.cps.spi
+import org.onap.cps.spi.exceptions.DataValidationException
import spock.lang.Specification
class FetchDescendantsOptionSpec extends Specification {
@@ -74,10 +75,10 @@ class FetchDescendantsOptionSpec extends Specification {
thrown IllegalArgumentException
}
- def 'Create fetch descendant option with descendant using #scenario.'() {
- when: 'the next level of depth is not allowed'
- def FetchDescendantsOption fetchDescendantsOption = FetchDescendantsOption.getFetchDescendantsOption(fetchDescendantsOptionAsString)
- then: 'fetch descendant object created'
+ def 'Create fetch descendant option from string scenario: #scenario.'() {
+ when: 'create fetch descendant option from string'
+ def fetchDescendantsOption = FetchDescendantsOption.getFetchDescendantsOption(fetchDescendantsOptionAsString)
+ then: 'fetch descendant object created with correct depth'
assert fetchDescendantsOption.depth == expectedDepth
where: 'following parameters are used'
scenario | fetchDescendantsOptionAsString || expectedDepth
@@ -85,12 +86,21 @@ class FetchDescendantsOptionSpec extends Specification {
'all descendants using all' | 'all' || -1
'No descendants by default' | '' || 0
'No descendants using none' | 'none' || 0
+ 'No descendants using number' | '0' || 0
'direct child using number' | '1' || 1
'direct child using direct' | 'direct' || 1
'til 10th descendants using number' | '10' || 10
}
- def 'String values.'() {
+ def 'Create fetch descendant option from string with invalid string.'() {
+ when: 'attempt to create fetch descendant option from invalid string'
+ FetchDescendantsOption.getFetchDescendantsOption('invalid-string')
+ then: 'a validation exception is thrown with the invalid string in the details'
+ def thrown = thrown(DataValidationException)
+ thrown.details.contains('invalid-string')
+ }
+
+ def 'Convert to string.'() {
expect: 'each fetch descendant option has the correct String value'
assert fetchDescendantsOption.toString() == expectedStringValue
where: 'the following option is used'
diff --git a/cps-service/src/test/groovy/org/onap/cps/spi/model/ConditionPropertiesSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/spi/model/ConditionPropertiesSpec.groovy
new file mode 100644
index 000000000..c8446902d
--- /dev/null
+++ b/cps-service/src/test/groovy/org/onap/cps/spi/model/ConditionPropertiesSpec.groovy
@@ -0,0 +1,38 @@
+/*
+ * ============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.spi.model
+
+import com.fasterxml.jackson.databind.ObjectMapper
+import org.onap.cps.utils.JsonObjectMapper
+import spock.lang.Specification
+
+class ConditionPropertiesSpec extends Specification {
+
+ ObjectMapper objectMapper = new ObjectMapper()
+
+ def 'Condition Properties JSON conversion.'() {
+ given: 'a condition properties'
+ def objectUnderTest = new ConditionProperties(conditionName: 'test', conditionParameters: [ [ key : 'value' ] ])
+ expect: 'the name is blank'
+ assert objectMapper.writeValueAsString(objectUnderTest) == '{"conditionName":"test","conditionParameters":[{"key":"value"}]}'
+ }
+
+}
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 1559783e9..fcbae628e 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
@@ -1,7 +1,7 @@
/*
* ============LICENSE_START=======================================================
* Copyright (C) 2021 Pantheon.tech
- * Modifications Copyright (C) 2021-2022 Nordix Foundation.
+ * Modifications Copyright (C) 2021-2023 Nordix Foundation.
* Modifications Copyright (C) 2022 TechMahindra Ltd.
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -22,15 +22,19 @@
package org.onap.cps.spi.model
import org.onap.cps.TestUtils
+import org.onap.cps.spi.exceptions.DataValidationException
import org.onap.cps.utils.DataMapUtils
import org.onap.cps.utils.YangUtils
import org.onap.cps.yang.YangTextSchemaSourceSetBuilder
import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode
+import org.opendaylight.yangtools.yang.data.api.schema.ForeignDataNode
import spock.lang.Specification
class DataNodeBuilderSpec extends Specification {
- Map<String, Map<String, Serializable>> expectedLeavesByXpathMap = [
+ def objectUnderTest = new DataNodeBuilder()
+
+ def expectedLeavesByXpathMap = [
'/test-tree' : [],
'/test-tree/branch[@name=\'Left\']' : [name: 'Left'],
'/test-tree/branch[@name=\'Left\']/nest' : [name: 'Small', birds: ['Sparrow', 'Robin', 'Finch']],
@@ -56,7 +60,7 @@ class DataNodeBuilderSpec extends Specification {
def jsonData = TestUtils.getResourceFileContent('test-tree.json')
def containerNode = YangUtils.parseJsonData(jsonData, schemaContext)
when: 'the container node is converted to a data node'
- def result = new DataNodeBuilder().withContainerNode(containerNode).build()
+ def result = objectUnderTest.withContainerNode(containerNode).build()
def mappedResult = TestUtils.getFlattenMapByXpath(result)
then: '6 DataNode objects with unique xpath were created in total'
mappedResult.size() == 6
@@ -76,16 +80,12 @@ class DataNodeBuilderSpec extends Specification {
def jsonData = '{ "branch": [{ "name": "Branch", "nest": { "name": "Nest", "birds": ["bird"] } }] }'
def containerNode = YangUtils.parseJsonData(jsonData, schemaContext, "/test-tree")
when: 'the container node is converted to a data node with parent node xpath defined'
- def result = new DataNodeBuilder()
- .withContainerNode(containerNode)
- .withParentNodeXpath("/test-tree")
- .build()
+ def result = objectUnderTest.withContainerNode(containerNode).withParentNodeXpath('/test-tree').build()
def mappedResult = TestUtils.getFlattenMapByXpath(result)
then: '2 DataNode objects with unique xpath were created in total'
mappedResult.size() == 2
and: 'all expected xpaths were built'
- mappedResult.keySet()
- .containsAll(['/test-tree/branch[@name=\'Branch\']', '/test-tree/branch[@name=\'Branch\']/nest'])
+ mappedResult.keySet().containsAll(['/test-tree/branch[@name=\'Branch\']', '/test-tree/branch[@name=\'Branch\']/nest'])
}
def 'Converting ContainerNode (tree) to a DataNode (tree) -- augmentation case.'() {
@@ -96,11 +96,10 @@ class DataNodeBuilderSpec extends Specification {
def jsonData = TestUtils.getResourceFileContent('ietf/data/ietf-network-topology-sample-rfc8345.json')
def containerNode = YangUtils.parseJsonData(jsonData, schemaContext)
when: 'the container node is converted to a data node '
- def result = new DataNodeBuilder().withContainerNode(containerNode).build()
+ def result = objectUnderTest.withContainerNode(containerNode).build()
def mappedResult = TestUtils.getFlattenMapByXpath(result)
then: 'all expected data nodes are populated'
mappedResult.size() == 32
- println(mappedResult.keySet().sort())
and: 'xpaths for augmentation nodes (link and termination-point nodes) were built correctly'
mappedResult.keySet().containsAll([
"/networks/network[@network-id='otn-hc']/link[@link-id='D1,1-2-1,D2,2-1-1']",
@@ -130,8 +129,7 @@ class DataNodeBuilderSpec extends Specification {
def jsonData = '{"source": {"source-node": "D1", "source-tp": "1-2-1"}}'
def containerNode = YangUtils.parseJsonData(jsonData, schemaContext, parentNodeXpath)
when: 'the container node is converted to a data node with given parent node xpath'
- def result = new DataNodeBuilder().withContainerNode(containerNode)
- .withParentNodeXpath(parentNodeXpath).build()
+ def result = objectUnderTest.withContainerNode(containerNode).withParentNodeXpath(parentNodeXpath).build()
then: 'the resulting data node represents a child of augmentation node'
assert result.xpath == "/networks/network[@network-id='otn-hc']/link[@link-id='D1,1-2-1,D2,2-1-1']/source"
assert result.leaves['source-node'] == 'D1'
@@ -146,15 +144,13 @@ class DataNodeBuilderSpec extends Specification {
def jsonData = TestUtils.getResourceFileContent('data-with-choice-node.json')
def containerNode = YangUtils.parseJsonData(jsonData, schemaContext)
when: 'the container node is converted to a data node'
- def result = new DataNodeBuilder().withContainerNode(containerNode).build()
+ def result = objectUnderTest.withContainerNode(containerNode).build()
def mappedResult = TestUtils.getFlattenMapByXpath(result)
then: 'the resulting data node contains only one xpath with 3 leaves'
- mappedResult.keySet().containsAll([
- "/container-with-choice-leaves"
- ])
- assert result.leaves['leaf-1'] == "test"
- assert result.leaves['choice-case1-leaf-a'] == "test"
- assert result.leaves['choice-case1-leaf-b'] == "test"
+ mappedResult.keySet().containsAll([ '/container-with-choice-leaves' ])
+ assert result.leaves['leaf-1'] == 'test'
+ assert result.leaves['choice-case1-leaf-a'] == 'test'
+ assert result.leaves['choice-case1-leaf-b'] == 'test'
}
def 'Converting ContainerNode into DataNode collection: #scenario.'() {
@@ -162,12 +158,11 @@ class DataNodeBuilderSpec extends Specification {
def yangResourceNameToContent = TestUtils.getYangResourcesAsMap('test-tree.yang')
def schemaContext = YangTextSchemaSourceSetBuilder.of(yangResourceNameToContent) getSchemaContext()
and: 'parent node xpath referencing parent of list element'
- def parentNodeXpath = "/test-tree"
+ def parentNodeXpath = '/test-tree'
and: 'the json data fragment (list element) parsed into container node object'
def containerNode = YangUtils.parseJsonData(jsonData, schemaContext, parentNodeXpath)
when: 'the container node is converted to a data node collection'
- def result = new DataNodeBuilder().withContainerNode(containerNode)
- .withParentNodeXpath(parentNodeXpath).buildCollection()
+ def result = objectUnderTest.withContainerNode(containerNode).withParentNodeXpath(parentNodeXpath).buildCollection()
def resultXpaths = result.collect { it.getXpath() }
then: 'the resulting collection contains data nodes for expected list elements'
assert resultXpaths.size() == expectedSize
@@ -178,15 +173,43 @@ class DataNodeBuilderSpec extends Specification {
'multiple entries' | '{"branch": [{"name": "One"}, {"name": "Two"}]}' | 2 | ['/test-tree/branch[@name=\'One\']', '/test-tree/branch[@name=\'Two\']']
}
- def 'Converting ContainerNode to a DataNode collection -- edge cases: #scenario.'() {
- when: 'the container node is #node'
- def result = new DataNodeBuilder().withContainerNode(containerNode).buildCollection()
- then: 'the resulting collection contains data nodes for expected list elements'
- assert result.isEmpty()
- where: 'following parameters are used'
- scenario | containerNode
- 'ContainerNode is null' | null
- 'ContainerNode is an unsupported type' | Mock(ContainerNode)
+ def 'Converting ContainerNode to a Collection with #scenario.'() {
+ expect: 'converting null to a collection returns an empty collection'
+ assert objectUnderTest.withContainerNode(containerNode).buildCollection().isEmpty()
+ where: 'the following container node is used'
+ scenario | containerNode
+ 'null object' | null
+ 'object without body' | Mock(ContainerNode)
+ }
+
+ def 'Converting ContainerNode to a DataNode with unsupported Normalized Node.'() {
+ given: 'a container node of an unsupported type'
+ def mockContainerNode = Mock(ContainerNode)
+ mockContainerNode.body() >> [ Mock(ForeignDataNode) ]
+ when: 'attempt to convert it'
+ objectUnderTest.withContainerNode(mockContainerNode).build()
+ then: 'a data validation exception is thrown'
+ thrown(DataValidationException)
+ }
+
+ def 'Build datanode from attributes.'() {
+ when: 'data node is built'
+ def result = new DataNodeBuilder()
+ .withDataspace('my dataspace')
+ .withAnchor('my anchor')
+ .withModuleNamePrefix('my prefix')
+ .withXpath('some xpath')
+ .withLeaves([leaf1: 'value1'])
+ .withChildDataNodes([Mock(DataNode)])
+ .build()
+ then: 'the datanode has all the defined attributes'
+ assert result.dataspace == 'my dataspace'
+ assert result.anchorName == 'my anchor'
+ assert result.moduleNamePrefix == 'my prefix'
+ assert result.moduleNamePrefix == 'my prefix'
+ assert result.xpath == 'some xpath'
+ assert result.leaves == [leaf1: 'value1']
+ assert result.childDataNodes.size() == 1
}
def 'Use of adding the module name prefix attribute of data node.'() {
diff --git a/cps-service/src/test/groovy/org/onap/cps/utils/JsonObjectMapperSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/utils/JsonObjectMapperSpec.groovy
index 2332282e2..8cbd49355 100644
--- a/cps-service/src/test/groovy/org/onap/cps/utils/JsonObjectMapperSpec.groovy
+++ b/cps-service/src/test/groovy/org/onap/cps/utils/JsonObjectMapperSpec.groovy
@@ -46,13 +46,23 @@ class JsonObjectMapperSpec extends Specification {
type << ['String', 'bytes']
}
+ def 'Convert to bytes with processing exception.'() {
+ given: 'the object mapper throws an processing exception'
+ spiedObjectMapper.writeValueAsBytes(_) >> { throw new JsonProcessingException('message from cause')}
+ when: 'attempt to convert an object to bytes'
+ jsonObjectMapper.asJsonBytes('does not matter')
+ then: 'a data validation exception is thrown with the original exception message as details'
+ def thrown = thrown(DataValidationException)
+ assert thrown.details == 'message from cause'
+ }
+
def 'Map a structured object to json String error.'() {
given: 'some object'
def object = new Object()
and: 'the Object mapper throws an exception'
spiedObjectMapper.writeValueAsString(object) >> { throw new JsonProcessingException('Sample problem'){} }
when: 'attempting to convert the object to a string'
- jsonObjectMapper.asJsonString(object);
+ jsonObjectMapper.asJsonString(object)
then: 'a Data Validation Exception is thrown'
def thrown = thrown(DataValidationException)
and: 'the details containing the original error message'
@@ -63,21 +73,27 @@ class JsonObjectMapperSpec extends Specification {
given: 'a map object model'
def contentMap = new JsonSlurper().parseText(TestUtils.getResourceFileContent('bookstore.json'))
when: 'converted into a Map'
- def result = jsonObjectMapper.convertToValueType(contentMap, Map);
+ def result = jsonObjectMapper.convertToValueType(contentMap, Map)
then: 'the result is a mapped into class of type Map'
assert result instanceof Map
and: 'the map contains the expected key'
assert result.containsKey('test:bookstore')
assert result.'test:bookstore'.categories[0].name == 'SciFi'
+ }
+ def 'Mapping a valid json string to class object of specific class type T.'() {
+ given: 'a json string representing a map'
+ def content = '{"key":"value"}'
+ expect: 'the string is converted correctly to a map'
+ jsonObjectMapper.convertJsonString(content, Map) == [ key: 'value' ]
}
def 'Mapping an unstructured json string to class object of specific class type T.'() {
given: 'Unstructured json string'
- def content = '{ "nest": { "birds": "bird"] } }'
+ def content = '{invalid json'
when: 'mapping json string to given class type'
- jsonObjectMapper.convertJsonString(content, Map);
- then: 'an exception is thrown'
+ jsonObjectMapper.convertJsonString(content, Map)
+ then: 'a data validation exception is thrown'
thrown(DataValidationException)
}
@@ -87,7 +103,7 @@ class JsonObjectMapperSpec extends Specification {
and: 'Object mapper throws an exception'
spiedObjectMapper.convertValue(*_) >> { throw new IllegalArgumentException() }
when: 'converted into specific class type'
- jsonObjectMapper.convertToValueType(contentMap, Object);
+ jsonObjectMapper.convertToValueType(contentMap, Object)
then: 'an exception is thrown'
thrown(DataValidationException)
}
@@ -96,9 +112,9 @@ class JsonObjectMapperSpec extends Specification {
given: 'Unstructured object'
def object = new Object()
and: 'disable serialization failure on empty bean'
- spiedObjectMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
+ spiedObjectMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS)
when: 'the object is mapped to string'
- jsonObjectMapper.asJsonString(object);
+ jsonObjectMapper.asJsonString(object)
then: 'no exception is thrown'
noExceptionThrown()
}
@@ -107,16 +123,16 @@ class JsonObjectMapperSpec extends Specification {
given: 'Unstructured object'
def content = '{ "nest": { "birds": "bird" } }'
when: 'the object is mapped to string'
- def result = jsonObjectMapper.convertToJsonNode(content);
+ def result = jsonObjectMapper.convertToJsonNode(content)
then: 'the result is a valid JsonNode'
- result.fieldNames().next() == "nest"
+ result.fieldNames().next() == 'nest'
}
def 'Map a unstructured json String to JsonNode.'() {
given: 'Unstructured object'
def content = '{ "nest": { "birds": "bird" }] }'
when: 'the object is mapped to string'
- jsonObjectMapper.convertToJsonNode(content);
+ jsonObjectMapper.convertToJsonNode(content)
then: 'a data validation exception is thrown'
thrown(DataValidationException)
}
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 b044e2e72..3864a5253 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
@@ -1,6 +1,7 @@
/*
* ============LICENSE_START=======================================================
* Copyright (C) 2022 Deutsche Telekom AG
+ * Modifications 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.
@@ -21,16 +22,18 @@ package org.onap.cps.utils
import org.onap.cps.TestUtils
import org.onap.cps.yang.YangTextSchemaSourceSetBuilder
+import org.xml.sax.SAXParseException
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'
+ 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'
+ then: 'the result xml is wrapped by root node defined in YANG schema'
assert parsedXmlContent == expectedOutput
where:
scenario | xmlData || expectedOutput
@@ -39,13 +42,22 @@ class XmlFileUtilsSpec extends Specification {
'no xml header' | '<stores><class> </class></stores>' || '<stores><class> </class></stores>'
}
+ def 'Parse a invalid xml content'(){
+ given: 'YANG model schema context'
+ def yangResourceNameToContent = TestUtils.getYangResourcesAsMap('bookstore.yang')
+ def schemaContext = YangTextSchemaSourceSetBuilder.of(yangResourceNameToContent).getSchemaContext()
+ when: 'attempt to parse invalid xml'
+ XmlFileUtils.prepareXmlContent('invalid-xml', schemaContext)
+ then: 'a Sax Parser exception is thrown'
+ thrown(SAXParseException)
+ }
+
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")
+ 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'
@@ -54,8 +66,6 @@ class XmlFileUtilsSpec extends Specification {
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 50b630643..e6344d303 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
@@ -1,6 +1,6 @@
/*
* ============LICENSE_START=======================================================
- * Copyright (C) 2020-2022 Nordix Foundation
+ * Copyright (C) 2020-2023 Nordix Foundation
* Modifications Copyright (C) 2021 Pantheon.tech
* Modifications Copyright (C) 2022 TechMahindra Ltd.
* Modifications Copyright (C) 2022 Deutsche Telekom AG
@@ -27,6 +27,7 @@ import org.onap.cps.TestUtils
import org.onap.cps.spi.exceptions.DataValidationException
import org.onap.cps.yang.YangTextSchemaSourceSetBuilder
import org.opendaylight.yangtools.yang.common.QName
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode
import spock.lang.Specification
@@ -162,4 +163,12 @@ 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']
}
+
+ def 'Get key attribute statement without key attributes'() {
+ given: 'a path argument without key attributes'
+ def mockPathArgument = Mock(YangInstanceIdentifier.NodeIdentifierWithPredicates)
+ mockPathArgument.entrySet() >> [ ]
+ expect: 'the result is an empty string'
+ YangUtils.getKeyAttributesStatement(mockPathArgument) == ''
+ }
}
diff --git a/cps-service/src/test/groovy/org/onap/cps/yang/YangTextSchemaSourceSetBuilderSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/yang/YangTextSchemaSourceSetBuilderSpec.groovy
index 3b4d57d3a..2739281bc 100644
--- a/cps-service/src/test/groovy/org/onap/cps/yang/YangTextSchemaSourceSetBuilderSpec.groovy
+++ b/cps-service/src/test/groovy/org/onap/cps/yang/YangTextSchemaSourceSetBuilderSpec.groovy
@@ -23,13 +23,13 @@
package org.onap.cps.yang
-
import org.onap.cps.TestUtils
import org.onap.cps.spi.exceptions.ModelValidationException
-import org.onap.cps.yang.YangTextSchemaSourceSetBuilder
import org.opendaylight.yangtools.yang.common.Revision
import spock.lang.Specification
+import java.nio.charset.StandardCharsets
+
class YangTextSchemaSourceSetBuilderSpec extends Specification {
def 'Building a valid YangTextSchemaSourceSet using #filenameCase filename.'() {
@@ -62,4 +62,16 @@ class YangTextSchemaSourceSetBuilderSpec extends Specification {
'invalid-empty.yang' | 'no valid content' || ModelValidationException
'invalid-missing-import.yang' | 'no dependency module' || ModelValidationException
}
+
+ def 'Convert yang source to a YangTextSchemaSource.'() {
+ given: 'a yang source text'
+ def yangSourceText = TestUtils.getResourceFileContent('bookstore.yang')
+ when: 'convert it to a YangTextSchemaSource'
+ def result = YangTextSchemaSourceSetBuilder.toYangTextSchemaSource('some name', yangSourceText)
+ then: 'the converted object has correct properties'
+ assert result.toString() == '{identifier=RevisionSourceIdentifier [name=some name]}'
+ assert new String(result.openStream().readAllBytes(), StandardCharsets.UTF_8) == yangSourceText
+ and: 'it has no symbolic name'
+ assert result.getSymbolicName().isEmpty()
+ }
}