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
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.ncmp.api.impl.events.lcm
23 import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle
24 import org.onap.cps.ncmp.api.inventory.CompositeState
25 import org.onap.cps.ncmp.api.inventory.DataStoreSyncState
26 import org.onap.cps.ncmp.api.inventory.InventoryPersistence
27 import spock.lang.Specification
29 import static org.onap.cps.ncmp.api.inventory.CmHandleState.ADVISED
30 import static org.onap.cps.ncmp.api.inventory.CmHandleState.DELETED
31 import static org.onap.cps.ncmp.api.inventory.CmHandleState.DELETING
32 import static org.onap.cps.ncmp.api.inventory.CmHandleState.LOCKED
33 import static org.onap.cps.ncmp.api.inventory.CmHandleState.READY
34 import static org.onap.cps.ncmp.api.inventory.LockReasonCategory.LOCKED_MODULE_SYNC_FAILED
36 class LcmEventsCmHandleStateHandlerImplSpec extends Specification {
38 def mockInventoryPersistence = Mock(InventoryPersistence)
39 def mockLcmEventsCreator = Mock(LcmEventsCreator)
40 def mockLcmEventsService = Mock(LcmEventsService)
42 def objectUnderTest = new LcmEventsCmHandleStateHandlerImpl(mockInventoryPersistence, mockLcmEventsCreator, mockLcmEventsService)
44 def cmHandleId = 'cmhandle-id-1'
48 def 'Update and Publish Events on State Change #stateChange'() {
49 given: 'Cm Handle represented as YangModelCmHandle'
50 compositeState = new CompositeState(cmHandleState: fromCmHandleState)
51 yangModelCmHandle = new YangModelCmHandle(id: cmHandleId, dmiProperties: [], publicProperties: [], compositeState: compositeState)
52 when: 'update state is invoked'
53 objectUnderTest.updateCmHandleState(yangModelCmHandle, toCmHandleState)
54 then: 'state is saved using inventory persistence'
55 expectedCallsToInventoryPersistence * mockInventoryPersistence.saveCmHandleState(cmHandleId, _)
56 and: 'event service is called to publish event'
57 expectedCallsToEventService * mockLcmEventsService.publishLcmEvent(cmHandleId, _)
58 where: 'state change parameters are provided'
59 stateChange | fromCmHandleState | toCmHandleState || expectedCallsToInventoryPersistence | expectedCallsToEventService
60 'ADVISED to READY' | ADVISED | READY || 1 | 1
61 'READY to LOCKED' | READY | LOCKED || 1 | 1
62 'ADVISED to ADVISED' | ADVISED | ADVISED || 0 | 0
63 'READY to READY' | READY | READY || 0 | 0
64 'LOCKED to LOCKED' | LOCKED | LOCKED || 0 | 0
68 def 'Update and Publish Events on State Change from NO_EXISTING state to ADVISED'() {
69 given: 'Cm Handle represented as YangModelCmHandle'
70 yangModelCmHandle = new YangModelCmHandle(id: cmHandleId, dmiProperties: [], publicProperties: [])
71 when: 'update state is invoked'
72 objectUnderTest.updateCmHandleState(yangModelCmHandle, ADVISED)
73 then: 'state is saved using inventory persistence'
74 1 * mockInventoryPersistence.saveCmHandle(yangModelCmHandle)
75 and: 'event service is called to publish event'
76 1 * mockLcmEventsService.publishLcmEvent(cmHandleId, _)
79 def 'Update and Publish Events on State Change from LOCKED to ADVISED'() {
80 given: 'Cm Handle represented as YangModelCmHandle in LOCKED state'
81 compositeState = new CompositeState(cmHandleState: LOCKED,
82 lockReason: CompositeState.LockReason.builder().lockReasonCategory(LOCKED_MODULE_SYNC_FAILED).details('some lock details').build())
83 yangModelCmHandle = new YangModelCmHandle(id: cmHandleId, dmiProperties: [], publicProperties: [], compositeState: compositeState)
84 when: 'update state is invoked'
85 objectUnderTest.updateCmHandleState(yangModelCmHandle, ADVISED)
86 then: 'state is saved using inventory persistence and old lock reason details are retained'
87 1 * mockInventoryPersistence.saveCmHandleState(cmHandleId, _) >> {
89 assert (args[1] as CompositeState).lockReason.details == 'some lock details'
92 and: 'event service is called to publish event'
93 1 * mockLcmEventsService.publishLcmEvent(cmHandleId, _)
96 def 'Update and Publish Events on State Change to READY'() {
97 given: 'Cm Handle represented as YangModelCmHandle'
98 compositeState = new CompositeState(cmHandleState: ADVISED)
99 yangModelCmHandle = new YangModelCmHandle(id: cmHandleId, dmiProperties: [], publicProperties: [], compositeState: compositeState)
100 and: 'global sync flag is set'
101 compositeState.setDataSyncEnabled(false)
102 when: 'update cmhandle state is invoked'
103 objectUnderTest.updateCmHandleState(yangModelCmHandle, READY)
104 then: 'state is saved using inventory persistence with expected dataSyncState'
105 1 * mockInventoryPersistence.saveCmHandleState(cmHandleId, _) >> {
107 def result = (args[1] as CompositeState)
108 assert result.dataSyncEnabled == false
109 assert result.dataStores.operationalDataStore.dataStoreSyncState == DataStoreSyncState.NONE_REQUESTED
113 and: 'event service is called to publish event'
114 1 * mockLcmEventsService.publishLcmEvent(cmHandleId, _)
117 def 'Update cmHandle state to "DELETING"' (){
118 given: 'cm Handle as Yang model'
119 compositeState = new CompositeState(cmHandleState: READY)
120 yangModelCmHandle = new YangModelCmHandle(id: cmHandleId, dmiProperties: [], publicProperties: [], compositeState: compositeState)
121 when: 'updating cm handle state to "DELETING"'
122 objectUnderTest.updateCmHandleState(yangModelCmHandle, DELETING)
123 then: 'the cm handle state is as expected'
124 yangModelCmHandle.getCompositeState().getCmHandleState() == DELETING
125 and: 'method to persist cm handle state is called once'
126 1 * mockInventoryPersistence.saveCmHandleState(yangModelCmHandle.getId(), yangModelCmHandle.getCompositeState())
127 and: 'the method to publish Lcm event is called once'
128 1 * mockLcmEventsService.publishLcmEvent(cmHandleId, _)
131 def 'Update cmHandle state to "DELETED"' (){
132 given: 'cm Handle with state "DELETING" as Yang model '
133 compositeState = new CompositeState(cmHandleState: DELETING)
134 yangModelCmHandle = new YangModelCmHandle(id: cmHandleId, dmiProperties: [], publicProperties: [], compositeState: compositeState)
135 when: 'updating cm handle state to "DELETED"'
136 objectUnderTest.updateCmHandleState(yangModelCmHandle, DELETED)
137 then: 'the cm handle state is as expected'
138 yangModelCmHandle.getCompositeState().getCmHandleState() == DELETED
139 and: 'the method to publish Lcm event is called once'
140 1 * mockLcmEventsService.publishLcmEvent(cmHandleId, _)
143 def 'No state change and no event to be published'() {
144 given: 'Cm Handle batch with same state transition as before'
145 def cmHandleStateMap = setupBatch('NO_CHANGE')
146 when: 'updating a batch of changes'
147 objectUnderTest.updateCmHandleStateBatch(cmHandleStateMap)
148 then: 'batch is empty and nothing to update'
149 1 * mockInventoryPersistence.saveCmHandleBatch(_) >> {
151 assert (args[0] as Collection<YangModelCmHandle>).size() == 0
154 and: 'no event will be published'
155 0 * mockLcmEventsService.publishLcmEvent(*_)
158 def 'Batch of new cm handles provided'() {
159 given: 'A batch of new cm handles'
160 def cmHandleStateMap = setupBatch('NEW')
161 when: 'updating a batch of changes'
162 objectUnderTest.updateCmHandleStateBatch(cmHandleStateMap)
163 then: 'new cm handles are saved using inventory persistence'
164 1 * mockInventoryPersistence.saveCmHandleBatch(_) >> {
166 assert (args[0] as Collection<YangModelCmHandle>).id.containsAll('cmhandle1', 'cmhandle2')
169 and: 'event service is called to publish event'
170 2 * mockLcmEventsService.publishLcmEvent(_, _)
174 def 'Batch of existing cm handles is updated'() {
175 given: 'A batch of updated cm handles'
176 def cmHandleStateMap = setupBatch('UPDATE')
177 when: 'updating a batch of changes'
178 objectUnderTest.updateCmHandleStateBatch(cmHandleStateMap)
179 then : 'existing cm handles composite state is persisted'
180 1 * mockInventoryPersistence.saveCmHandleStateBatch(_) >> {
182 assert (args[0] as Map<String, CompositeState>).keySet().containsAll(['cmhandle1','cmhandle2'])
185 and: 'event service is called to publish event'
186 2 * mockLcmEventsService.publishLcmEvent(_, _)
190 def setupBatch(type) {
192 def yangModelCmHandle1 = new YangModelCmHandle(id: 'cmhandle1', dmiProperties: [], publicProperties: [])
193 def yangModelCmHandle2 = new YangModelCmHandle(id: 'cmhandle2', dmiProperties: [], publicProperties: [])
196 return [(yangModelCmHandle1): ADVISED, (yangModelCmHandle2): ADVISED]
199 if ('UPDATE' == type) {
200 yangModelCmHandle1.compositeState = new CompositeState(cmHandleState: ADVISED)
201 yangModelCmHandle2.compositeState = new CompositeState(cmHandleState: READY)
202 return [(yangModelCmHandle1): READY, (yangModelCmHandle2): DELETING]
205 if ('NO_CHANGE' == type) {
206 yangModelCmHandle1.compositeState = new CompositeState(cmHandleState: ADVISED)
207 yangModelCmHandle2.compositeState = new CompositeState(cmHandleState: READY)
208 return [(yangModelCmHandle1): ADVISED, (yangModelCmHandle2): READY]