2 * ============LICENSE_START=======================================================
3 * Copyright (C) 2023 Nordix Foundation
4 * ================================================================================
5 * Licensed under the Apache License, Version 2.0 (the 'License');
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an 'AS IS' BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
17 * SPDX-License-Identifier: Apache-2.0
18 * ============LICENSE_END=========================================================
21 package org.onap.cps.integration.functional
23 import org.onap.cps.api.CpsModuleService
24 import org.onap.cps.api.impl.CpsModuleServiceImpl
25 import org.onap.cps.integration.base.FunctionalSpecBase
26 import org.onap.cps.spi.CascadeDeleteAllowed
27 import org.onap.cps.spi.exceptions.AlreadyDefinedException
28 import org.onap.cps.spi.exceptions.DataspaceNotFoundException
29 import org.onap.cps.spi.exceptions.ModelValidationException
30 import org.onap.cps.spi.exceptions.SchemaSetInUseException
31 import org.onap.cps.spi.exceptions.SchemaSetNotFoundException
32 import org.onap.cps.spi.model.ModuleDefinition
33 import org.onap.cps.spi.model.ModuleReference
35 class CpsModuleServiceIntegrationSpec extends FunctionalSpecBase {
37 CpsModuleService objectUnderTest
39 private static def originalNumberOfModuleReferences = 1
40 private static def existingModuleReference = new ModuleReference('stores','2020-09-15')
41 static def NEW_RESOURCE_REVISION = '2023-05-10'
42 static def NEW_RESOURCE_CONTENT = 'module test_module {\n' +
43 ' yang-version 1.1;\n' +
44 ' namespace "org:onap:ccsdk:sample";\n' +
46 ' prefix book-store;\n' +
48 ' revision "2023-05-10" {\n' +
50 ' "Sample Model";\n' +
54 def newYangResourcesNameToContentMap = [:]
55 def moduleReferences = []
56 def noNewModules = [:]
59 objectUnderTest = cpsModuleService
63 C R E A T E S C H E M A S E T U S E - C A S E S
66 def 'Create new schema set from yang resources with #scenario'() {
67 given: 'a new schema set with #numberOfModules modules'
68 populateNewYangResourcesNameToContentMapAndAllModuleReferences(numberOfNewModules)
69 when: 'the new schema set is created'
70 objectUnderTest.createSchemaSet(FUNCTIONAL_TEST_DATASPACE_1, 'newSchemaSet', newYangResourcesNameToContentMap)
71 then: 'the number of module references has increased by #expectedIncrease'
72 def yangResourceModuleReferences = objectUnderTest.getYangResourceModuleReferences(FUNCTIONAL_TEST_DATASPACE_1)
73 originalNumberOfModuleReferences + numberOfNewModules == yangResourceModuleReferences.size()
75 objectUnderTest.deleteSchemaSetsWithCascade(FUNCTIONAL_TEST_DATASPACE_1, [ 'newSchemaSet' ])
76 where: 'the following parameters are use'
77 scenario | numberOfNewModules
78 'two valid new modules' | 2
79 'empty schema set' | 0
80 'over max batch size #modules' | 101
83 def 'Create new schema set with recommended filename format but invalid yang'() {
84 given: 'a filename using RFC6020 recommended format (for coverage only)'
85 def fileName = 'test@2023-05-11.yang'
86 when: 'attempt to create a schema set with invalid Yang'
87 objectUnderTest.createSchemaSet(FUNCTIONAL_TEST_DATASPACE_1, 'newSchemaSet', [(fileName) :'invalid yang'])
88 then: 'a model validation exception'
89 thrown(ModelValidationException)
92 def 'Create new schema set from modules with #scenario'() {
93 given: 'a new schema set with #numberOfNewModules modules'
94 populateNewYangResourcesNameToContentMapAndAllModuleReferences(numberOfNewModules)
95 and: 'add existing module references (optional)'
96 moduleReferences.addAll(existingModuleReferences)
97 when: 'the new schema set is created'
98 def schemaSetName = "NewSchemaWith${numberOfNewModules}Modules"
99 objectUnderTest.createSchemaSetFromModules(FUNCTIONAL_TEST_DATASPACE_1, schemaSetName, newYangResourcesNameToContentMap, moduleReferences)
100 and: 'associated with a new anchor'
101 cpsAnchorService.createAnchor(FUNCTIONAL_TEST_DATASPACE_1, schemaSetName, 'newAnchor')
102 then: 'the new anchor has the correct number of modules'
103 def yangResourceModuleReferences = objectUnderTest.getYangResourcesModuleReferences(FUNCTIONAL_TEST_DATASPACE_1, 'newAnchor')
104 assert expectedNumberOfModulesForAnchor == yangResourceModuleReferences.size()
106 objectUnderTest.deleteSchemaSetsWithCascade(FUNCTIONAL_TEST_DATASPACE_1, [ schemaSetName.toString() ])
107 where: 'the following module references are provided'
108 scenario | numberOfNewModules | existingModuleReferences || expectedNumberOfModulesForAnchor
109 'empty schema set' | 0 | [ ] || 0
110 'one existing module' | 0 | [ existingModuleReference ] || 1
111 'two new modules' | 2 | [ ] || 2
112 'two new modules, one existing' | 2 | [ existingModuleReference ] || 3
113 'over max batch size #modules' | 101 | [ ] || 101
114 'two valid, one invalid module' | 2 | [ new ModuleReference('NOT EXIST','IRRELEVANT') ] || 2
117 def 'Duplicate schema content.'() {
118 given: 'a map of yang resources'
119 populateNewYangResourcesNameToContentMapAndAllModuleReferences(1)
120 when: 'a new schema set is created'
121 objectUnderTest.createSchemaSet(FUNCTIONAL_TEST_DATASPACE_1, 'newSchema1', newYangResourcesNameToContentMap)
122 then: 'the dataspace has one new module (reference)'
123 def numberOfModuleReferencesAfterFirstSchemaSetHasBeenAdded = objectUnderTest.getYangResourceModuleReferences(FUNCTIONAL_TEST_DATASPACE_1).size()
124 assert numberOfModuleReferencesAfterFirstSchemaSetHasBeenAdded == originalNumberOfModuleReferences + 1
125 when: 'a second new schema set is created'
126 objectUnderTest.createSchemaSet(FUNCTIONAL_TEST_DATASPACE_1, 'newSchema2', newYangResourcesNameToContentMap)
127 then: 'the dataspace has no additional module (reference)'
128 assert numberOfModuleReferencesAfterFirstSchemaSetHasBeenAdded == objectUnderTest.getYangResourceModuleReferences(FUNCTIONAL_TEST_DATASPACE_1).size()
130 objectUnderTest.deleteSchemaSetsWithCascade(FUNCTIONAL_TEST_DATASPACE_1, [ 'newSchema1', 'newSchema2'])
133 def 'Create schema set error scenario: #scenario.'() {
134 when: 'attempt to store schema set #schemaSetName in dataspace #dataspaceName'
135 populateNewYangResourcesNameToContentMapAndAllModuleReferences(0)
136 objectUnderTest.createSchemaSet(dataspaceName, schemaSetName, newYangResourcesNameToContentMap)
137 then: 'an #expectedException is thrown'
138 thrown(expectedException)
139 where: 'the following data is used'
140 scenario | dataspaceName | schemaSetName || expectedException
141 'dataspace does not exist' | 'unknown' | 'not-relevant' || DataspaceNotFoundException
142 'schema set already exists' | FUNCTIONAL_TEST_DATASPACE_1 | BOOKSTORE_SCHEMA_SET || AlreadyDefinedException
146 R E A D S C H E M A S E T I N F O U S E - C A S E S
149 def 'Retrieving module definitions by anchor.'() {
150 when: 'the module definitions for an anchor are retrieved'
151 def result = objectUnderTest.getModuleDefinitionsByAnchorName(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1)
152 then: 'the correct module definitions are returned'
153 result == [new ModuleDefinition('stores','2020-09-15','')]
156 def 'Retrieving yang resource module references by anchor.'() {
157 when: 'the yang resource module references for an anchor are retrieved'
158 def result = objectUnderTest.getYangResourcesModuleReferences(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1)
159 then: 'the correct module references are returned'
160 result == [ existingModuleReference ]
163 def 'Identifying new module references with #scenario'() {
164 when: 'identifyNewModuleReferences is called'
165 def result = objectUnderTest.identifyNewModuleReferences(moduleReferences)
166 then: 'the correct module references are returned'
167 assert result.size() == expectedResult.size()
168 assert result.containsAll(expectedResult)
169 where: 'the following data is used'
170 scenario | moduleReferences || expectedResult
171 'just new module references' | [new ModuleReference('new1', 'r1'), new ModuleReference('new2', 'r1')] || [new ModuleReference('new1', 'r1'), new ModuleReference('new2', 'r1')]
172 'one new module,one existing reference' | [new ModuleReference('new1', 'r1'), existingModuleReference] || [new ModuleReference('new1', 'r1')]
173 'no new module references' | [existingModuleReference] || []
174 'no module references' | [] || []
175 'module references collection is null' | null || []
178 def 'Retrieve schema set.'() {
179 when: 'a specific schema set is retreived'
180 def result = objectUnderTest.getSchemaSet(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_SCHEMA_SET)
181 then: 'the result has the correct name and module(s)'
182 assert result.name == 'bookstoreSchemaSet'
183 assert result.moduleReferences == [ new ModuleReference('stores', '2020-09-15', 'org:onap:ccsdk:sample') ]
186 def 'Retrieve all schema sets.'() {
187 given: 'an extra schema set is stored'
188 populateNewYangResourcesNameToContentMapAndAllModuleReferences(1)
189 objectUnderTest.createSchemaSet(FUNCTIONAL_TEST_DATASPACE_1, 'newSchema1', newYangResourcesNameToContentMap)
190 when: 'all schema sets are retrieved'
191 def result = objectUnderTest.getSchemaSets(FUNCTIONAL_TEST_DATASPACE_1)
192 then: 'the result contains all expected schema sets'
193 assert result.name == [ 'bookstoreSchemaSet', 'newSchema1' ]
195 objectUnderTest.deleteSchemaSetsWithCascade(FUNCTIONAL_TEST_DATASPACE_1, ['newSchema1'])
199 D E L E T E S C H E M A S E T U S E - C A S E S
202 def 'Delete schema sets with(out) cascade.'() {
203 given: 'a schema set'
204 populateNewYangResourcesNameToContentMapAndAllModuleReferences(1)
205 objectUnderTest.createSchemaSet(FUNCTIONAL_TEST_DATASPACE_1, 'newSchemaSet', newYangResourcesNameToContentMap)
206 and: 'optionally create anchor for the schema set'
207 if (associateWithAnchor) {
208 cpsAnchorService.createAnchor(FUNCTIONAL_TEST_DATASPACE_1, 'newSchemaSet', 'newAnchor')
210 when: 'attempt to delete the schema set'
212 objectUnderTest.deleteSchemaSet(FUNCTIONAL_TEST_DATASPACE_1, 'newSchemaSet', cascadeDeleteAllowedOption)
214 catch (Exception e) { // only accept correct exception when schema set cannot be deleted
215 assert e instanceof SchemaSetInUseException && expectSchemaSetStillPresent
217 then: 'check if the dataspace still contains the new schema set or not'
218 def remainingSchemaSetNames = objectUnderTest.getSchemaSets(FUNCTIONAL_TEST_DATASPACE_1).name
219 assert remainingSchemaSetNames.contains('newSchemaSet') == expectSchemaSetStillPresent
221 objectUnderTest.deleteSchemaSetsWithCascade(FUNCTIONAL_TEST_DATASPACE_1, ['newSchemaSet'])
222 where: 'the following options are used'
223 associateWithAnchor | cascadeDeleteAllowedOption || expectSchemaSetStillPresent
224 false | CascadeDeleteAllowed.CASCADE_DELETE_ALLOWED || false
225 false | CascadeDeleteAllowed.CASCADE_DELETE_PROHIBITED || false
226 true | CascadeDeleteAllowed.CASCADE_DELETE_ALLOWED || false
227 true | CascadeDeleteAllowed.CASCADE_DELETE_PROHIBITED || true
230 def 'Delete schema sets with shared resources.'() {
231 given: 'a new schema set'
232 populateNewYangResourcesNameToContentMapAndAllModuleReferences(1)
233 objectUnderTest.createSchemaSet(FUNCTIONAL_TEST_DATASPACE_1, 'newSchemaSet1', newYangResourcesNameToContentMap)
234 and: 'another schema set which shares one yang resource (module)'
235 populateNewYangResourcesNameToContentMapAndAllModuleReferences(2)
236 objectUnderTest.createSchemaSet(FUNCTIONAL_TEST_DATASPACE_1, 'newSchemaSet2', newYangResourcesNameToContentMap)
237 when: 'all schema sets are retrieved'
238 def moduleRevisions = objectUnderTest.getYangResourceModuleReferences(FUNCTIONAL_TEST_DATASPACE_1).revision
239 then: 'both modules (revisions) are present'
240 assert moduleRevisions.containsAll(['2000-01-01', '2000-01-01'])
241 when: 'delete the second schema set that has two resources one of which is a shared resource'
242 objectUnderTest.deleteSchemaSetsWithCascade(FUNCTIONAL_TEST_DATASPACE_1, ['newSchemaSet2'])
243 then: 'only the second schema set is deleted'
244 def remainingSchemaSetNames = objectUnderTest.getSchemaSets(FUNCTIONAL_TEST_DATASPACE_1).name
245 assert remainingSchemaSetNames.contains('newSchemaSet1')
246 assert !remainingSchemaSetNames.contains('newSchemaSet2')
247 and: 'only the shared module (revision) remains'
248 def remainingModuleRevisions = objectUnderTest.getYangResourceModuleReferences(FUNCTIONAL_TEST_DATASPACE_1).revision
249 assert remainingModuleRevisions.contains('2000-01-01')
250 assert !remainingModuleRevisions.contains('2001-01-01')
252 objectUnderTest.deleteSchemaSetsWithCascade(FUNCTIONAL_TEST_DATASPACE_1, ['newSchemaSet1'])
255 def 'Delete schema set error scenario: #scenario.'() {
256 when: 'attempt to delete a schema set where #scenario'
257 objectUnderTest.deleteSchemaSet(dataspaceName, schemaSetName, CascadeDeleteAllowed.CASCADE_DELETE_PROHIBITED)
258 then: 'an #expectedException is thrown'
259 thrown(expectedException)
260 where: 'the following data is used'
261 scenario | dataspaceName | schemaSetName || expectedException
262 'dataspace does not exist' | 'unknown' | 'not-relevant' || DataspaceNotFoundException
263 'schema set does not exists' | FUNCTIONAL_TEST_DATASPACE_1 | 'unknown' || SchemaSetNotFoundException
270 def 'Upgrade schema set (with existing and new modules, no matching module set tag in NCMP)'() {
271 given: 'an anchor and schema set with 2 modules (to be upgraded)'
272 populateNewYangResourcesNameToContentMapAndAllModuleReferences('original', 2)
273 objectUnderTest.createSchemaSetFromModules(FUNCTIONAL_TEST_DATASPACE_1, 'targetSchema', newYangResourcesNameToContentMap, [])
274 cpsAnchorService.createAnchor(FUNCTIONAL_TEST_DATASPACE_1, 'targetSchema', 'targetAnchor')
275 def yangResourceModuleReferencesBeforeUpgrade = objectUnderTest.getYangResourcesModuleReferences(FUNCTIONAL_TEST_DATASPACE_1, 'targetAnchor')
276 assert yangResourceModuleReferencesBeforeUpgrade.size() == 2
277 assert yangResourceModuleReferencesBeforeUpgrade.containsAll([new ModuleReference('original_0','2000-01-01'),new ModuleReference('original_1','2001-01-01')])
278 and: 'two new 2 modules (from node)'
279 populateNewYangResourcesNameToContentMapAndAllModuleReferences('new', 2)
280 def newModuleReferences = [new ModuleReference('new_0','2000-01-01'),new ModuleReference('new_1','2001-01-01')]
281 and: 'a list of all module references (normally retrieved from node)'
282 def allModuleReferences = []
283 allModuleReferences.add(existingModuleReference)
284 allModuleReferences.addAll(newModuleReferences)
285 when: 'the schema set is upgraded'
286 objectUnderTest.upgradeSchemaSetFromModules(FUNCTIONAL_TEST_DATASPACE_1, 'targetSchema', newYangResourcesNameToContentMap, allModuleReferences)
287 then: 'the new anchor has the correct new and existing modules'
288 def yangResourceModuleReferencesAfterUpgrade = objectUnderTest.getYangResourcesModuleReferences(FUNCTIONAL_TEST_DATASPACE_1, 'targetAnchor')
289 assert yangResourceModuleReferencesAfterUpgrade.size() == 3
290 assert yangResourceModuleReferencesAfterUpgrade.contains(existingModuleReference)
291 assert yangResourceModuleReferencesAfterUpgrade.containsAll(newModuleReferences);
293 objectUnderTest.deleteSchemaSetsWithCascade(FUNCTIONAL_TEST_DATASPACE_1, ['targetSchema'])
296 def 'Upgrade existing schema set from another anchor (used in NCMP for matching module set tag)'() {
297 given: 'an anchor and schema set with 1 module (target)'
298 populateNewYangResourcesNameToContentMapAndAllModuleReferences('target', 1)
299 objectUnderTest.createSchemaSetFromModules(FUNCTIONAL_TEST_DATASPACE_1, 'targetSchema', newYangResourcesNameToContentMap, [])
300 cpsAnchorService.createAnchor(FUNCTIONAL_TEST_DATASPACE_1, 'targetSchema', 'targetAnchor')
301 def moduleReferencesBeforeUpgrade = objectUnderTest.getYangResourcesModuleReferences(FUNCTIONAL_TEST_DATASPACE_1, 'targetAnchor')
302 assert moduleReferencesBeforeUpgrade.size() == 1
303 and: 'another anchor and schema set with 2 other modules (source for upgrade)'
304 populateNewYangResourcesNameToContentMapAndAllModuleReferences('source', 2)
305 objectUnderTest.createSchemaSetFromModules(FUNCTIONAL_TEST_DATASPACE_1, 'sourceSchema', newYangResourcesNameToContentMap, [])
306 cpsAnchorService.createAnchor(FUNCTIONAL_TEST_DATASPACE_1, 'sourceSchema', 'sourceAnchor')
307 assert objectUnderTest.getYangResourcesModuleReferences(FUNCTIONAL_TEST_DATASPACE_1, 'sourceAnchor').size() == 2
308 when: 'the target schema is upgraded using the module references from the source anchor'
309 def moduleReferencesFromSourceAnchor = objectUnderTest.getYangResourcesModuleReferences(FUNCTIONAL_TEST_DATASPACE_1, 'sourceAnchor')
310 objectUnderTest.upgradeSchemaSetFromModules(FUNCTIONAL_TEST_DATASPACE_1, 'targetSchema', noNewModules, moduleReferencesFromSourceAnchor)
311 then: 'the target schema now refers to the source modules (with namespace) modules'
312 def schemaSetModuleReferencesAfterUpgrade = getObjectUnderTest().getSchemaSet(FUNCTIONAL_TEST_DATASPACE_1, 'targetSchema').moduleReferences
313 assert schemaSetModuleReferencesAfterUpgrade.containsAll([new ModuleReference('source_0','2000-01-01','org:onap:ccsdk:sample'),new ModuleReference('source_1','2001-01-01','org:onap:ccsdk:sample')]);
314 and: 'the associated target anchor has the same module references (without namespace but that is a legacy issue)'
315 def anchorModuleReferencesAfterUpgrade = objectUnderTest.getYangResourcesModuleReferences(FUNCTIONAL_TEST_DATASPACE_1, 'targetAnchor')
316 assert anchorModuleReferencesAfterUpgrade.containsAll([new ModuleReference('source_0','2000-01-01'),new ModuleReference('source_1','2001-01-01')]);
318 objectUnderTest.deleteSchemaSetsWithCascade(FUNCTIONAL_TEST_DATASPACE_1, ['sourceSchema', 'targetSchema'])
322 H E L P E R M E T H O D S
325 def populateNewYangResourcesNameToContentMapAndAllModuleReferences(numberOfModules) {
326 populateNewYangResourcesNameToContentMapAndAllModuleReferences('name', numberOfModules)
329 def populateNewYangResourcesNameToContentMapAndAllModuleReferences(namePrefix, numberOfModules) {
330 numberOfModules.times {
331 def uniqueName = namePrefix + '_' + it
332 def uniqueRevision = String.valueOf(2000 + it) + '-01-01'
333 moduleReferences.add(new ModuleReference(uniqueName, uniqueRevision))
334 def uniqueContent = NEW_RESOURCE_CONTENT.replace(NEW_RESOURCE_REVISION, uniqueRevision).replace('module test_module', 'module '+uniqueName)
335 newYangResourcesNameToContentMap.put(uniqueRevision, uniqueContent)