a13f5952001cd8adb46f24975a5b3c05edc64acb
[cps.git] / cps-ncmp-service / src / test / groovy / org / onap / cps / ncmp / api / inventory / sync / ModuleSyncServiceSpec.groovy
1 /*
2  *  ============LICENSE_START=======================================================
3  *  Copyright (C) 2022-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.ncmp.api.inventory.sync
22
23 import static org.onap.cps.ncmp.api.impl.inventory.LockReasonCategory.LOCKED_MISBEHAVING
24 import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DATASPACE_NAME
25 import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DMI_REGISTRY_ANCHOR
26 import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME
27 import static org.onap.cps.ncmp.api.impl.inventory.LockReasonCategory.MODULE_UPGRADE
28
29 import org.onap.cps.ncmp.api.impl.inventory.CmHandleState
30 import org.onap.cps.spi.FetchDescendantsOption
31 import org.onap.cps.spi.model.DataNode
32 import org.onap.cps.api.CpsAdminService
33 import org.onap.cps.api.CpsDataService
34 import org.onap.cps.api.CpsModuleService
35 import org.onap.cps.ncmp.api.impl.inventory.sync.ModuleSyncService
36 import org.onap.cps.ncmp.api.impl.operations.DmiModelOperations
37 import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle
38 import org.onap.cps.ncmp.api.impl.inventory.CmHandleQueries
39 import org.onap.cps.ncmp.api.impl.inventory.CompositeStateBuilder
40 import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle
41 import org.onap.cps.spi.CascadeDeleteAllowed
42 import org.onap.cps.spi.exceptions.SchemaSetNotFoundException
43 import org.onap.cps.spi.model.ModuleReference
44 import org.onap.cps.utils.JsonObjectMapper
45 import spock.lang.Specification
46
47 class ModuleSyncServiceSpec extends Specification {
48
49     def mockCpsModuleService = Mock(CpsModuleService)
50     def mockDmiModelOperations = Mock(DmiModelOperations)
51     def mockCpsAdminService = Mock(CpsAdminService)
52     def mockCmHandleQueries = Mock(CmHandleQueries)
53     def mockCpsDataService = Mock(CpsDataService)
54     def mockJsonObjectMapper = Mock(JsonObjectMapper)
55
56     def objectUnderTest = new ModuleSyncService(mockDmiModelOperations, mockCpsModuleService, mockCpsAdminService,
57             mockCmHandleQueries, mockCpsDataService, mockJsonObjectMapper)
58
59     def expectedDataspaceName = NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME
60
61     def 'Sync model for a (new) cm handle with #scenario'() {
62         given: 'a cm handle'
63             def ncmpServiceCmHandle = new NcmpServiceCmHandle()
64             ncmpServiceCmHandle.setCompositeState(new CompositeStateBuilder().build())
65             def dmiServiceName = 'some service name'
66             ncmpServiceCmHandle.cmHandleId = 'cmHandleId-1'
67             def yangModelCmHandle = YangModelCmHandle.toYangModelCmHandle(dmiServiceName, '', '', ncmpServiceCmHandle,'')
68         and: 'DMI operations returns some module references'
69             def moduleReferences =  [ new ModuleReference('module1','1'), new ModuleReference('module2','2') ]
70             mockDmiModelOperations.getModuleReferences(yangModelCmHandle) >> moduleReferences
71         and: 'CPS-Core returns list of existing module resources'
72             mockCpsModuleService.getYangResourceModuleReferences(expectedDataspaceName) >> toModuleReference(existingModuleResourcesInCps)
73         and: 'DMI-Plugin returns resource(s) for "new" module(s)'
74             mockDmiModelOperations.getNewYangResourcesFromDmi(yangModelCmHandle, [new ModuleReference('module1', '1')]) >> newModuleNameContentToMap
75         when: 'module sync is triggered'
76             mockCpsModuleService.identifyNewModuleReferences(moduleReferences) >> toModuleReference(identifiedNewModuleReferences)
77             objectUnderTest.syncAndCreateOrUpgradeSchemaSetAndAnchor(yangModelCmHandle)
78         then: 'create schema set from module is invoked with correct parameters'
79             1 * mockCpsModuleService.createOrUpgradeSchemaSetFromModules(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, 'cmHandleId-1', newModuleNameContentToMap, moduleReferences)
80         and: 'anchor is created with the correct parameters'
81             1 * mockCpsAdminService.createAnchor(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, 'cmHandleId-1', 'cmHandleId-1')
82         where: 'the following parameters are used'
83             scenario             | existingModuleResourcesInCps           | identifiedNewModuleReferences | newModuleNameContentToMap
84             'one new module'     | [['module2' : '2'], ['module3' : '3']] | [['module1' : '1']]           | [module1: 'some yang source']
85             'no add. properties' | [['module2' : '2'], ['module3' : '3']] | [['module1' : '1']]           | [module1: 'some yang source']
86             'no new module'      | [['module1' : '1'], ['module2' : '2']] | []                            | [:]
87     }
88
89     def 'upgrade model for a existing cm handle'() {
90         given: 'a cm handle that is ready but locked for upgrade'
91             def ncmpServiceCmHandle = new NcmpServiceCmHandle()
92             ncmpServiceCmHandle.setCompositeState(new CompositeStateBuilder()
93                 .withLockReason(MODULE_UPGRADE, 'new moduleSetTag: targetModuleSetTag').build())
94             ncmpServiceCmHandle.setCmHandleId('cmHandleId-1')
95             def yangModelCmHandle = YangModelCmHandle.toYangModelCmHandle('some service name', '', '', ncmpServiceCmHandle, 'targetModuleSetTag')
96             mockCmHandleQueries.cmHandleHasState('cmHandleId-1', CmHandleState.READY) >> true
97         and: 'the module service returns some module references'
98             def moduleReferences = [new ModuleReference('module1', '1'), new ModuleReference('module2', '2')]
99             mockCpsModuleService.getYangResourcesModuleReferences(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR) >> moduleReferences
100         and: 'a cm handle with the same moduleSetTag can be found in the registry'
101             mockCmHandleQueries.queryNcmpRegistryByCpsPath("//cm-handles[@module-set-tag='targetModuleSetTag']",
102                 FetchDescendantsOption.OMIT_DESCENDANTS) >> [new DataNode(xpath: '/dmi-registry/cm-handles[@id=\'cmHandleId-1\']', leaves: ['id': 'cmHandleId-1', 'cm-handle-state': 'READY'])]
103         when: 'module upgrade is triggered'
104             objectUnderTest.syncAndCreateOrUpgradeSchemaSetAndAnchor(yangModelCmHandle)
105         then: 'the upgrade is delegated to the module service (with the correct parameters)'
106             1 * mockCpsModuleService.createOrUpgradeSchemaSetFromModules(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, 'cmHandleId-1', Collections.emptyMap(), moduleReferences)
107     }
108
109     def 'Delete Schema Set for CmHandle'() {
110         when: 'delete schema set if exists is called'
111             objectUnderTest.deleteSchemaSetIfExists('some-cmhandle-id')
112         then: 'the module service is invoked to delete the correct schema set'
113             1 * mockCpsModuleService.deleteSchemaSet(expectedDataspaceName, 'some-cmhandle-id', CascadeDeleteAllowed.CASCADE_DELETE_ALLOWED)
114     }
115
116     def 'Delete a non-existing Schema Set for CmHandle' () {
117         given: 'the DB throws an exception because its Schema Set does not exist'
118            mockCpsModuleService.deleteSchemaSet(*_) >> { throw new SchemaSetNotFoundException('some-dataspace-name', 'some-cmhandle-id') }
119         when: 'delete schema set if exists is called'
120             objectUnderTest.deleteSchemaSetIfExists('some-cmhandle-id')
121         then: 'the exception from the DB is ignored; there are no exceptions'
122             noExceptionThrown()
123     }
124
125     def 'Delete Schema Set for CmHandle with other exception' () {
126         given: 'an exception other than SchemaSetNotFoundException is thrown'
127             UnsupportedOperationException unsupportedOperationException = new UnsupportedOperationException();
128             1 * mockCpsModuleService.deleteSchemaSet(*_) >> { throw unsupportedOperationException }
129         when: 'delete schema set if exists is called'
130             objectUnderTest.deleteSchemaSetIfExists('some-cmhandle-id')
131         then: 'an exception is thrown'
132             def result = thrown(UnsupportedOperationException)
133             result == unsupportedOperationException
134     }
135
136     def 'Extract module set tag with #scenario'() {
137         expect: 'the module set tag is extracted correctly'
138             assert expectedModuleSetTag == objectUnderTest.extractModuleSetTag(new CompositeStateBuilder().withLockReason(lockReason, 'new moduleSetTag: targetModuleSetTag').build())
139         where: 'the following parameters are used'
140             scenario                    | lockReason         || expectedModuleSetTag
141             'locked reason is null'     | null               || ''
142             'locked for other reason'   | LOCKED_MISBEHAVING || ''
143             'locked for module upgrade' | MODULE_UPGRADE     || 'targetModuleSetTag'
144     }
145
146     def toModuleReference(moduleReferenceAsMap) {
147         def moduleReferences = [].withDefault { [:] }
148         moduleReferenceAsMap.forEach(property ->
149             property.forEach((moduleName, revision) -> {
150                 moduleReferences.add(new ModuleReference('moduleName' : moduleName, 'revision' : revision))
151             }))
152         return moduleReferences
153     }
154
155 }