Replace RestTemplate with WebClient in synchronous DMI calls
[cps.git] / integration-test / src / test / groovy / org / onap / cps / integration / functional / NcmpCmHandleCreateSpec.groovy
1 /*
2  *  ============LICENSE_START=======================================================
3  *  Copyright (C) 2024 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 spock.lang.Ignore
24
25 import java.time.Duration
26 import java.time.OffsetDateTime
27 import org.apache.kafka.common.TopicPartition
28 import org.apache.kafka.common.serialization.StringDeserializer
29 import org.onap.cps.integration.KafkaTestContainer
30 import org.onap.cps.integration.base.CpsIntegrationSpecBase
31 import org.onap.cps.ncmp.api.NetworkCmProxyDataService
32 import org.onap.cps.ncmp.api.impl.inventory.CmHandleState
33 import org.onap.cps.ncmp.api.impl.inventory.LockReasonCategory
34 import org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse
35 import org.onap.cps.ncmp.api.models.DmiPluginRegistration
36 import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle
37 import org.onap.cps.ncmp.events.lcm.v1.LcmEvent
38 import spock.util.concurrent.PollingConditions
39
40 @Ignore
41 class NcmpCmHandleCreateSpec extends CpsIntegrationSpecBase {
42
43     NetworkCmProxyDataService objectUnderTest
44
45     def kafkaConsumer = KafkaTestContainer.getConsumer('ncmp-group', StringDeserializer.class)
46
47     static final MODULE_REFERENCES_RESPONSE_A = readResourceDataFile('mock-dmi-responses/bookStoreAWithModules_M1_M2_Response.json')
48     static final MODULE_RESOURCES_RESPONSE_A = readResourceDataFile('mock-dmi-responses/bookStoreAWithModules_M1_M2_ResourcesResponse.json')
49     static final MODULE_REFERENCES_RESPONSE_B = readResourceDataFile('mock-dmi-responses/bookStoreBWithModules_M1_M3_Response.json')
50     static final MODULE_RESOURCES_RESPONSE_B = readResourceDataFile('mock-dmi-responses/bookStoreBWithModules_M1_M3_ResourcesResponse.json')
51
52     def setup() {
53         objectUnderTest = networkCmProxyDataService
54         mockDmiWillRespondToHealthChecks(DMI_URL)
55     }
56
57     def 'CM Handle registration is successful.'() {
58         given: 'DMI will return modules when requested'
59             mockDmiResponsesForModuleSync(DMI_URL, 'ch-1', MODULE_REFERENCES_RESPONSE_A, MODULE_RESOURCES_RESPONSE_A)
60
61         and: 'consumer subscribed to topic'
62             kafkaConsumer.subscribe(['ncmp-events'])
63
64         when: 'a CM-handle is registered for creation'
65             def cmHandleToCreate = new NcmpServiceCmHandle(cmHandleId: 'ch-1')
66             def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: DMI_URL, createdCmHandles: [cmHandleToCreate])
67             def dmiPluginRegistrationResponse = networkCmProxyDataService.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
68
69         then: 'registration gives successful response'
70             assert dmiPluginRegistrationResponse.createdCmHandles == [CmHandleRegistrationResponse.createSuccessResponse('ch-1')]
71
72         and: 'CM-handle is initially in ADVISED state'
73             assert CmHandleState.ADVISED == objectUnderTest.getCmHandleCompositeState('ch-1').cmHandleState
74
75         when: 'module sync runs'
76             moduleSyncWatchdog.moduleSyncAdvisedCmHandles()
77
78         then: 'CM-handle goes to READY state'
79             new PollingConditions().within(3, () -> {
80                 assert CmHandleState.READY == objectUnderTest.getCmHandleCompositeState('ch-1').cmHandleState
81             })
82
83         and: 'the messages is polled'
84             def message = kafkaConsumer.poll(Duration.ofMillis(10000))
85             def records = message.records(new TopicPartition('ncmp-events', 0))
86
87         and: 'the newest lcm event notification is received with READY state'
88             def notificationMessage = jsonObjectMapper.convertJsonString(records.last().value().toString(), LcmEvent)
89             assert notificationMessage.event.newValues.cmHandleState.value() == 'READY'
90
91         and: 'the CM-handle has expected modules'
92             assert ['M1', 'M2'] == objectUnderTest.getYangResourcesModuleReferences('ch-1').moduleName.sort()
93
94         and: 'DMI received expected requests'
95             mockDmiServer.verify()
96
97         cleanup: 'deregister CM handle'
98             deregisterCmHandle(DMI_URL, 'ch-1')
99     }
100
101     def 'CM Handle goes to LOCKED state when DMI gives error during module sync.'() {
102         given: 'DMI is not available to handle requests'
103             mockDmiIsNotAvailableForModuleSync(DMI_URL, 'ch-1')
104
105         when: 'a CM-handle is registered for creation'
106             def cmHandleToCreate = new NcmpServiceCmHandle(cmHandleId: 'ch-1')
107             def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: DMI_URL, createdCmHandles: [cmHandleToCreate])
108             networkCmProxyDataService.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
109
110         and: 'module sync runs'
111             moduleSyncWatchdog.moduleSyncAdvisedCmHandles()
112
113         then: 'CM-handle goes to LOCKED state with reason MODULE_SYNC_FAILED'
114             new PollingConditions().within(3, () -> {
115                 def cmHandleCompositeState = objectUnderTest.getCmHandleCompositeState('ch-1')
116                 assert cmHandleCompositeState.cmHandleState == CmHandleState.LOCKED
117                 assert cmHandleCompositeState.lockReason.lockReasonCategory == LockReasonCategory.MODULE_SYNC_FAILED
118             })
119
120         and: 'CM-handle has no modules'
121             assert objectUnderTest.getYangResourcesModuleReferences('ch-1').empty
122
123         cleanup: 'deregister CM handle'
124             deregisterCmHandle(DMI_URL, 'ch-1')
125     }
126
127     def 'Create a CM-handle with existing moduleSetTag.'() {
128         given: 'existing CM-handles cm-1 with moduleSetTag "A", and cm-2 with moduleSetTag "B"'
129             mockDmiResponsesForModuleSync(DMI_URL, 'ch-1', MODULE_REFERENCES_RESPONSE_A, MODULE_RESOURCES_RESPONSE_A)
130             mockDmiResponsesForModuleSync(DMI_URL, 'ch-2', MODULE_REFERENCES_RESPONSE_B, MODULE_RESOURCES_RESPONSE_B)
131             registerCmHandle(DMI_URL, 'ch-1', 'A')
132             registerCmHandle(DMI_URL, 'ch-2', 'B')
133             assert ['M1', 'M2'] == objectUnderTest.getYangResourcesModuleReferences('ch-1').moduleName.sort()
134             assert ['M1', 'M3'] == objectUnderTest.getYangResourcesModuleReferences('ch-2').moduleName.sort()
135
136         when: 'a CM-handle is registered for creation with moduleSetTag "B"'
137             def cmHandleToCreate = new NcmpServiceCmHandle(cmHandleId: 'ch-3', moduleSetTag: 'B')
138             networkCmProxyDataService.updateDmiRegistrationAndSyncModule(new DmiPluginRegistration(dmiPlugin: DMI_URL, createdCmHandles: [cmHandleToCreate]))
139
140         then: 'the CM-handle goes to READY state after module sync'
141             moduleSyncWatchdog.moduleSyncAdvisedCmHandles()
142             new PollingConditions().within(3, () -> {
143                 assert CmHandleState.READY == objectUnderTest.getCmHandleCompositeState('ch-3').cmHandleState
144             })
145
146         and: 'the CM-handle has expected moduleSetTag'
147             assert objectUnderTest.getNcmpServiceCmHandle('ch-3').moduleSetTag == 'B'
148
149         and: 'the CM-handle has expected modules from module set "B": M1 and M3'
150             assert ['M1', 'M3'] == objectUnderTest.getYangResourcesModuleReferences('ch-3').moduleName.sort()
151
152         cleanup: 'deregister CM handles'
153             deregisterCmHandles(DMI_URL, ['ch-1', 'ch-2', 'ch-3'])
154     }
155
156     def 'CM Handle retry after failed module sync.'() {
157         given: 'DMI is not initially available to handle requests'
158             mockDmiIsNotAvailableForModuleSync(DMI_URL, 'ch-1')
159             mockDmiIsNotAvailableForModuleSync(DMI_URL, 'ch-2')
160         and: 'DMI will be available for retry'
161             mockDmiResponsesForModuleSync(DMI_URL, 'ch-1', MODULE_REFERENCES_RESPONSE_A, MODULE_RESOURCES_RESPONSE_A)
162             mockDmiResponsesForModuleSync(DMI_URL, 'ch-2', MODULE_REFERENCES_RESPONSE_B, MODULE_RESOURCES_RESPONSE_B)
163
164         when: 'CM-handles are registered for creation'
165             def cmHandlesToCreate = [new NcmpServiceCmHandle(cmHandleId: 'ch-1'), new NcmpServiceCmHandle(cmHandleId: 'ch-2')]
166             def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: DMI_URL, createdCmHandles: cmHandlesToCreate)
167             networkCmProxyDataService.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
168         and: 'module sync runs'
169             moduleSyncWatchdog.moduleSyncAdvisedCmHandles()
170         then: 'CM-handles go to LOCKED state'
171             new PollingConditions().within(3, () -> {
172                 assert objectUnderTest.getCmHandleCompositeState('ch-1').cmHandleState == CmHandleState.LOCKED
173                 assert objectUnderTest.getCmHandleCompositeState('ch-2').cmHandleState == CmHandleState.LOCKED
174             })
175
176         when: 'we wait for LOCKED CM handle retry time (actually just subtract 3 minutes from handles lastUpdateTime)'
177             overrideCmHandleLastUpdateTime('ch-1', OffsetDateTime.now().minusMinutes(3))
178             overrideCmHandleLastUpdateTime('ch-2', OffsetDateTime.now().minusMinutes(3))
179         and: 'failed CM handles are reset'
180             moduleSyncWatchdog.resetPreviouslyFailedCmHandles()
181         then: 'CM-handles are ADVISED state'
182             assert objectUnderTest.getCmHandleCompositeState('ch-1').cmHandleState == CmHandleState.ADVISED
183             assert objectUnderTest.getCmHandleCompositeState('ch-2').cmHandleState == CmHandleState.ADVISED
184
185         when: 'module sync runs'
186             moduleSyncWatchdog.moduleSyncAdvisedCmHandles()
187         then: 'CM-handles go to READY state'
188             new PollingConditions().within(3, () -> {
189                 assert objectUnderTest.getCmHandleCompositeState('ch-1').cmHandleState == CmHandleState.READY
190                 assert objectUnderTest.getCmHandleCompositeState('ch-2').cmHandleState == CmHandleState.READY
191             })
192         and: 'CM-handles have expected modules'
193             assert ['M1', 'M2'] == objectUnderTest.getYangResourcesModuleReferences('ch-1').moduleName.sort()
194             assert ['M1', 'M3'] == objectUnderTest.getYangResourcesModuleReferences('ch-2').moduleName.sort()
195         and: 'CM-handles have expected module set tags (blank)'
196             assert objectUnderTest.getNcmpServiceCmHandle('ch-1').moduleSetTag == ''
197             assert objectUnderTest.getNcmpServiceCmHandle('ch-2').moduleSetTag == ''
198         and: 'DMI received expected requests'
199             mockDmiServer.verify()
200
201         cleanup: 'deregister CM handle'
202             deregisterCmHandles(DMI_URL, ['ch-1', 'ch-2'])
203     }
204 }