Patch # 3: Data operation response event (NCMP → Client App) to comply with CloudEvents
[cps.git] / cps-ncmp-service / src / test / groovy / org / onap / cps / ncmp / api / impl / events / lcm / LcmEventsCmHandleStateHandlerImplSpec.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.impl.events.lcm
22
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
28
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
35
36 class LcmEventsCmHandleStateHandlerImplSpec extends Specification {
37
38     def mockInventoryPersistence = Mock(InventoryPersistence)
39     def mockLcmEventsCreator = Mock(LcmEventsCreator)
40     def mockLcmEventsService = Mock(LcmEventsService)
41
42     def objectUnderTest = new LcmEventsCmHandleStateHandlerImpl(mockInventoryPersistence, mockLcmEventsCreator, mockLcmEventsService)
43
44     def cmHandleId = 'cmhandle-id-1'
45     def compositeState
46     def yangModelCmHandle
47
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
65
66     }
67
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, _, _)
77     }
78
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, _) >> {
88                 args -> {
89                     assert (args[1] as CompositeState).lockReason.details == 'some lock details'
90                 }
91             }
92         and: 'event service is called to publish event'
93             1 * mockLcmEventsService.publishLcmEvent(cmHandleId, _, _)
94     }
95
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, _) >> {
106                 args-> {
107                     def result = (args[1] as CompositeState)
108                     assert result.dataSyncEnabled == false
109                     assert result.dataStores.operationalDataStore.dataStoreSyncState == DataStoreSyncState.NONE_REQUESTED
110
111                 }
112             }
113         and: 'event service is called to publish event'
114             1 * mockLcmEventsService.publishLcmEvent(cmHandleId, _, _)
115     }
116
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, _, _)
129     }
130
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, _, _)
141     }
142
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(_) >> {
150                 args -> {
151                     assert (args[0] as Collection<YangModelCmHandle>).size() == 0
152                 }
153             }
154         and: 'no event will be published'
155             0 * mockLcmEventsService.publishLcmEvent(*_)
156     }
157
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(_) >> {
165                 args -> {
166                     assert (args[0] as Collection<YangModelCmHandle>).id.containsAll('cmhandle1', 'cmhandle2')
167                 }
168             }
169         and: 'event service is called to publish event'
170             2 * mockLcmEventsService.publishLcmEvent(_, _, _)
171
172     }
173
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(_) >> {
181                 args -> {
182                     assert (args[0] as Map<String, CompositeState>).keySet().containsAll(['cmhandle1','cmhandle2'])
183                 }
184             }
185         and: 'event service is called to publish event'
186             2 * mockLcmEventsService.publishLcmEvent(_, _, _)
187
188     }
189
190     def setupBatch(type) {
191
192         def yangModelCmHandle1 = new YangModelCmHandle(id: 'cmhandle1', dmiProperties: [], publicProperties: [])
193         def yangModelCmHandle2 = new YangModelCmHandle(id: 'cmhandle2', dmiProperties: [], publicProperties: [])
194
195         if ('NEW' == type) {
196             return [(yangModelCmHandle1): ADVISED, (yangModelCmHandle2): ADVISED]
197         }
198
199         if ('UPDATE' == type) {
200             yangModelCmHandle1.compositeState = new CompositeState(cmHandleState: ADVISED)
201             yangModelCmHandle2.compositeState = new CompositeState(cmHandleState: READY)
202             return [(yangModelCmHandle1): READY, (yangModelCmHandle2): DELETING]
203         }
204
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]
209         }
210     }
211 }