Remove the dependency-cycle between beans
[cps.git] / integration-test / src / test / groovy / org / onap / cps / integration / functional / CpsModuleServiceIntegrationSpec.groovy
1 /*
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
8  *
9  *        http://www.apache.org/licenses/LICENSE-2.0
10  *
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.
16  *
17  *  SPDX-License-Identifier: Apache-2.0
18  *  ============LICENSE_END=========================================================
19  */
20
21 package org.onap.cps.integration.functional
22
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
34
35 class CpsModuleServiceIntegrationSpec extends FunctionalSpecBase {
36
37     CpsModuleService objectUnderTest
38
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' +
45         '\n' +
46         '    prefix book-store;\n' +
47         '\n' +
48         '    revision "2023-05-10" {\n' +
49         '        description\n' +
50         '        "Sample Model";\n' +
51         '    }' +
52         '}'
53
54     def newYangResourcesNameToContentMap = [:]
55     def moduleReferences = []
56     def noNewModules = [:]
57
58     def setup() {
59         objectUnderTest = cpsModuleService
60     }
61
62     /*
63         C R E A T E   S C H E M A   S E T   U S E - C A S E S
64      */
65
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()
74         cleanup:
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
81     }
82
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)
90     }
91
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()
105         cleanup:
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
115     }
116
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()
129         cleanup:
130             objectUnderTest.deleteSchemaSetsWithCascade(FUNCTIONAL_TEST_DATASPACE_1, [ 'newSchema1', 'newSchema2'])
131     }
132
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
143     }
144
145     /*
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
147      */
148
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','')]
154     }
155
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 ]
161     }
162
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                                                                   || []
176     }
177
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') ]
184     }
185
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' ]
194         cleanup:
195             objectUnderTest.deleteSchemaSetsWithCascade(FUNCTIONAL_TEST_DATASPACE_1, ['newSchema1'])
196     }
197
198     /*
199         D E L E T E   S C H E M A   S E T   U S E - C A S E S
200      */
201
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')
209             }
210         when: 'attempt to delete the schema set'
211             try {
212                 objectUnderTest.deleteSchemaSet(FUNCTIONAL_TEST_DATASPACE_1, 'newSchemaSet', cascadeDeleteAllowedOption)
213             }
214             catch (Exception e) {  // only accept correct exception when schema set cannot be deleted
215                 assert e instanceof SchemaSetInUseException && expectSchemaSetStillPresent
216             }
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
220         cleanup:
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
228     }
229
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')
251         cleanup:
252             objectUnderTest.deleteSchemaSetsWithCascade(FUNCTIONAL_TEST_DATASPACE_1, ['newSchemaSet1'])
253     }
254
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
264     }
265
266     /*
267         U P G R A D E
268      */
269
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);
292         cleanup:
293             objectUnderTest.deleteSchemaSetsWithCascade(FUNCTIONAL_TEST_DATASPACE_1, ['targetSchema'])
294     }
295
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')]);
317         cleanup:
318             objectUnderTest.deleteSchemaSetsWithCascade(FUNCTIONAL_TEST_DATASPACE_1, ['sourceSchema', 'targetSchema'])
319     }
320
321     /*
322         H E L P E R   M E T H O D S
323      */
324
325     def populateNewYangResourcesNameToContentMapAndAllModuleReferences(numberOfModules) {
326         populateNewYangResourcesNameToContentMapAndAllModuleReferences('name', numberOfModules)
327     }
328
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)
336         }
337     }
338
339 }