2 * ============LICENSE_START=======================================================
3 * Copyright (C) 2021-2024 Nordix Foundation
4 * Modifications Copyright (C) 2022 Bell Canada
5 * ================================================================================
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
18 * SPDX-License-Identifier: Apache-2.0
19 * ============LICENSE_END=========================================================
22 package org.onap.cps.ncmp.api.impl
24 import static org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.Status
25 import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLES_NOT_FOUND
26 import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLE_ALREADY_EXIST
27 import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLE_INVALID_ID
28 import static org.onap.cps.ncmp.api.NcmpResponseStatus.UNKNOWN_ERROR
30 import org.onap.cps.ncmp.api.impl.inventory.CompositeState
31 import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevelManager
32 import org.onap.cps.ncmp.api.impl.utils.AlternateIdChecker
33 import org.onap.cps.ncmp.api.models.UpgradedCmHandles
34 import com.fasterxml.jackson.databind.ObjectMapper
35 import com.hazelcast.map.IMap
36 import org.onap.cps.api.CpsDataService
37 import org.onap.cps.api.CpsModuleService
38 import org.onap.cps.ncmp.api.NetworkCmProxyCmHandleQueryService
39 import org.onap.cps.ncmp.api.impl.events.lcm.LcmEventsCmHandleStateHandler
40 import org.onap.cps.ncmp.api.impl.exception.DmiRequestException
41 import org.onap.cps.ncmp.api.impl.operations.DmiDataOperations
42 import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevel
43 import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle
44 import org.onap.cps.ncmp.api.impl.inventory.CmHandleQueries
45 import org.onap.cps.ncmp.api.impl.inventory.CmHandleState
46 import org.onap.cps.ncmp.api.impl.inventory.InventoryPersistence
47 import org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse
48 import org.onap.cps.ncmp.api.models.DmiPluginRegistration
49 import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle
50 import org.onap.cps.spi.exceptions.AlreadyDefinedException
51 import org.onap.cps.spi.exceptions.DataNodeNotFoundException
52 import org.onap.cps.spi.exceptions.DataValidationException
53 import org.onap.cps.spi.exceptions.SchemaSetNotFoundException
54 import org.onap.cps.utils.JsonObjectMapper
55 import spock.lang.Specification
57 class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
59 def ncmpServiceCmHandle = new NcmpServiceCmHandle(cmHandleId: 'some-cm-handle-id')
60 def mockCpsModuleService = Mock(CpsModuleService)
61 def spiedJsonObjectMapper = Spy(new JsonObjectMapper(new ObjectMapper()))
62 def mockDmiDataOperations = Mock(DmiDataOperations)
63 def mockNetworkCmProxyDataServicePropertyHandler = Mock(NetworkCmProxyDataServicePropertyHandler)
64 def mockInventoryPersistence = Mock(InventoryPersistence)
65 def mockCmHandleQueries = Mock(CmHandleQueries)
66 def stubbedNetworkCmProxyCmHandlerQueryService = Stub(NetworkCmProxyCmHandleQueryService)
67 def mockLcmEventsCmHandleStateHandler = Mock(LcmEventsCmHandleStateHandler)
68 def mockCpsDataService = Mock(CpsDataService)
69 def mockModuleSyncStartedOnCmHandles = Mock(IMap<String, Object>)
70 def trustLevelPerDmiPlugin = [:]
71 def mockTrustLevelManager = Mock(TrustLevelManager)
72 def mockAlternateIdChecker = Mock(AlternateIdChecker)
74 def objectUnderTest = Spy(new NetworkCmProxyDataServiceImpl(spiedJsonObjectMapper, mockDmiDataOperations,
75 mockNetworkCmProxyDataServicePropertyHandler, mockInventoryPersistence, mockCmHandleQueries,
76 stubbedNetworkCmProxyCmHandlerQueryService, mockLcmEventsCmHandleStateHandler, mockCpsDataService,
77 mockModuleSyncStartedOnCmHandles, trustLevelPerDmiPlugin, mockTrustLevelManager, mockAlternateIdChecker))
80 // always accept all cm handles
81 mockAlternateIdChecker.getIdsOfCmHandlesWithRejectedAlternateId(*_) >> []
83 // always can find all cm handles in DB
84 mockInventoryPersistence.getYangModelCmHandles(_) >> { args -> args[0].collect { new YangModelCmHandle(id:it) } }
87 def 'DMI Registration: Create, Update, Delete & Upgrade operations are processed in the right order'() {
88 given: 'a registration with operations of all types'
89 def dmiRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server')
90 dmiRegistration.setCreatedCmHandles([new NcmpServiceCmHandle(cmHandleId: 'cmhandle-1', publicProperties: ['publicProp1': 'value'], dmiProperties: [:])])
91 dmiRegistration.setUpdatedCmHandles([new NcmpServiceCmHandle(cmHandleId: 'cmhandle-2', publicProperties: ['publicProp1': 'value'], dmiProperties: [:])])
92 dmiRegistration.setRemovedCmHandles(['cmhandle-2'])
93 dmiRegistration.setUpgradedCmHandles(new UpgradedCmHandles(cmHandles: ['cmhandle-3'], moduleSetTag: 'some-module-set-tag'))
94 and: 'cm handles are persisted'
95 mockInventoryPersistence.getYangModelCmHandles(['cmhandle-2']) >> [new YangModelCmHandle()]
96 mockInventoryPersistence.getYangModelCmHandle('cmhandle-3') >> new YangModelCmHandle(id: 'cmhandle-3', moduleSetTag: '', compositeState: new CompositeState(cmHandleState: CmHandleState.READY))
97 and: 'cm handle is in READY state'
98 mockCmHandleQueries.cmHandleHasState('cmhandle-3', CmHandleState.READY) >> true
99 when: 'registration is processed'
100 objectUnderTest.updateDmiRegistrationAndSyncModule(dmiRegistration)
101 then: 'cm-handles are removed first'
102 1 * objectUnderTest.processRemovedCmHandles(*_)
103 and: 'de-registered cm handle entry is removed from in progress map'
104 1 * mockModuleSyncStartedOnCmHandles.remove('cmhandle-2')
105 then: 'cm-handles are created'
106 1 * objectUnderTest.processCreatedCmHandles(*_)
107 then: 'cm-handles are updated'
108 1 * objectUnderTest.processUpdatedCmHandles(*_)
109 1 * mockNetworkCmProxyDataServicePropertyHandler.updateCmHandleProperties(*_) >> []
110 then: 'cm-handles are upgraded'
111 1 * objectUnderTest.processUpgradedCmHandles(*_)
114 def 'DMI Registration upgrade operation with upgrade node state #scenario'() {
115 given: 'a registration with upgrade operation'
116 def dmiRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server')
117 dmiRegistration.setUpgradedCmHandles(new UpgradedCmHandles(cmHandles: ['cmhandle-3'], moduleSetTag: 'some-module-set-tag'))
118 and: 'exception while checking cm handle state'
119 mockInventoryPersistence.getYangModelCmHandle('cmhandle-3') >> new YangModelCmHandle(id: 'cmhandle-3', moduleSetTag: '', compositeState: new CompositeState(cmHandleState: cmHandleState))
120 when: 'registration is processed'
121 def result = objectUnderTest.updateDmiRegistrationAndSyncModule(dmiRegistration)
122 then: 'upgrade operation contains expected error code'
123 assert result.upgradedCmHandles[0].status == expectedResponseStatus
124 where: 'the following parameters are used'
125 scenario | cmHandleState || expectedResponseStatus
126 'READY' | CmHandleState.READY || Status.SUCCESS
127 'Not READY' | CmHandleState.LOCKED || Status.FAILURE
130 def 'DMI Registration upgrade with exception #scenario'() {
131 given: 'a registration with upgrade operation'
132 def dmiRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server')
133 dmiRegistration.setUpgradedCmHandles(new UpgradedCmHandles(cmHandles: ['cmhandle-3'], moduleSetTag: 'some-module-set-tag'))
134 and: 'exception while checking cm handle state'
135 mockInventoryPersistence.getYangModelCmHandle('cmhandle-3') >> { throw exception }
136 when: 'registration is processed'
137 def result = objectUnderTest.updateDmiRegistrationAndSyncModule(dmiRegistration)
138 then: 'upgrade operation contains expected error code'
139 assert result.upgradedCmHandles.ncmpResponseStatus.code[0] == expectedErrorCode
140 where: 'the following parameters are used'
141 scenario | exception || expectedErrorCode
142 'data node not found' | new DataNodeNotFoundException('some-dataspace-name', 'some-anchor-name') || '100'
143 'cm handle is invalid' | new DataValidationException('some error message', 'some error details') || '110'
146 def 'Create CM-handle Validation: Registration with valid Service names: #scenario'() {
147 given: 'a registration '
148 def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: dmiPlugin, dmiModelPlugin: dmiModelPlugin,
149 dmiDataPlugin: dmiDataPlugin)
150 dmiPluginRegistration.createdCmHandles = [ncmpServiceCmHandle]
151 when: 'update registration and sync module is called with correct DMI plugin information'
152 objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
153 then: 'create cm handles registration and sync modules is called with the correct plugin information'
154 1 * objectUnderTest.processCreatedCmHandles(dmiPluginRegistration, _)
155 and: 'dmi is added to the dmi trustLevel map'
156 assert trustLevelPerDmiPlugin.size() == 1
157 assert trustLevelPerDmiPlugin.containsKey(expectedDmiPluginRegisteredName)
159 scenario | dmiPlugin | dmiModelPlugin | dmiDataPlugin || expectedDmiPluginRegisteredName
160 'combined DMI plugin' | 'service1' | '' | '' || 'service1'
161 'data & model DMI plugins' | '' | 'service1' | 'service2' || 'service2'
162 'data & model using same service' | '' | 'service1' | 'service1' || 'service1'
165 def 'Create CM-handle Validation: Invalid DMI plugin service name with #scenario'() {
166 given: 'a registration '
167 def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: dmiPlugin, dmiModelPlugin: dmiModelPlugin,
168 dmiDataPlugin: dmiDataPlugin)
169 dmiPluginRegistration.createdCmHandles = [ncmpServiceCmHandle]
170 when: 'registration is called with incorrect DMI plugin information'
171 objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
172 then: 'a DMI Request Exception is thrown with correct message details'
173 def exceptionThrown = thrown(DmiRequestException.class)
174 assert exceptionThrown.getMessage().contains(expectedMessageDetails)
175 and: 'registration is not called'
176 0 * objectUnderTest.processCreatedCmHandles(*_)
178 scenario | dmiPlugin | dmiModelPlugin | dmiDataPlugin || expectedMessageDetails
179 'empty DMI plugins' | '' | '' | '' || 'No DMI plugin service names'
180 'blank DMI plugins' | ' ' | ' ' | ' ' || 'No DMI plugin service names'
181 'null DMI plugins' | null | null | null || 'No DMI plugin service names'
182 'all DMI plugins' | 'service1' | 'service2' | 'service3' || 'Cannot register combined plugin service name and other service names'
183 '(combined)DMI and Data Plugin' | 'service1' | '' | 'service2' || 'Cannot register combined plugin service name and other service names'
184 '(combined)DMI and model Plugin' | 'service1' | 'service2' | '' || 'Cannot register combined plugin service name and other service names'
185 'only model DMI plugin' | '' | 'service1' | '' || 'Cannot register just a Data or Model plugin service name'
186 'only data DMI plugin' | '' | '' | 'service1' || 'Cannot register just a Data or Model plugin service name'
189 def 'Create CM-Handle Successfully: #scenario.'() {
190 given: 'a registration without cm-handle properties'
191 def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server')
192 dmiPluginRegistration.createdCmHandles = [new NcmpServiceCmHandle(cmHandleId: 'cmhandle', dmiProperties: dmiProperties, publicProperties: publicProperties)]
193 when: 'registration is updated'
194 def response = objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
195 then: 'a successful response is received'
196 response.createdCmHandles.size() == 1
197 with(response.createdCmHandles[0]) {
198 assert it.status == Status.SUCCESS
199 assert it.cmHandle == 'cmhandle'
201 and: 'state handler is invoked with the expected parameters'
202 1 * mockLcmEventsCmHandleStateHandler.initiateStateAdvised(_) >> {
204 def yangModelCmHandles = args[0]
205 assert yangModelCmHandles.id == ['cmhandle']
206 assert yangModelCmHandles.dmiServiceName == ['my-server']
210 scenario | dmiProperties | publicProperties || expectedDmiProperties | expectedPublicProperties
211 'with dmi & public properties' | ['dmi-key': 'dmi-value'] | ['public-key': 'public-value'] || '[{"name":"dmi-key","value":"dmi-value"}]' | '[{"name":"public-key","value":"public-value"}]'
212 'with only public properties' | [:] | ['public-key': 'public-value'] || [:] | '[{"name":"public-key","value":"public-value"}]'
213 'with only dmi properties' | ['dmi-key': 'dmi-value'] | [:] || '[{"name":"dmi-key","value":"dmi-value"}]' | [:]
214 'without dmi & public properties' | [:] | [:] || [:] | [:]
217 def 'Add CM-Handle #scenario.'() {
218 given: ' registration details for one cm handles'
219 def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server',
220 createdCmHandles:[new NcmpServiceCmHandle(cmHandleId: 'ch-1', registrationTrustLevel: registrationTrustLevel)])
221 when: 'registration is updated'
222 objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
223 then: 'trustLevel is set for the created cm-handle'
224 1 * mockTrustLevelManager.handleInitialRegistrationOfTrustLevels(expectedMapping)
226 scenario | registrationTrustLevel || expectedMapping
227 'with trusted cm handle' | TrustLevel.COMPLETE || [ 'ch-1' : TrustLevel.COMPLETE ]
228 'without trust level' | null || [ 'ch-1' : null ]
231 def 'Create CM-Handle Multiple Requests: All cm-handles creation requests are processed with some failures'() {
232 given: 'a registration with three cm-handles to be created'
233 def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server',
234 createdCmHandles: [new NcmpServiceCmHandle(cmHandleId: 'cmhandle1'),
235 new NcmpServiceCmHandle(cmHandleId: 'cmhandle2'),
236 new NcmpServiceCmHandle(cmHandleId: 'cmhandle3')])
237 and: 'cm-handle creation is successful for 1st and 3rd; failed for 2nd'
238 def xpath = "somePathWithId[@id='cmhandle2']"
239 mockLcmEventsCmHandleStateHandler.initiateStateAdvised(*_) >> { throw AlreadyDefinedException.forDataNodes([xpath], 'some-context') }
240 when: 'registration is updated to create cm-handles'
241 def response = objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
242 then: 'a response is received for all cm-handles'
243 response.createdCmHandles.size() == 1
244 and: 'all cm-handles creation fails'
245 response.createdCmHandles.each {
246 assert it.cmHandle == 'cmhandle2'
247 assert it.status == Status.FAILURE
248 assert it.ncmpResponseStatus == CM_HANDLE_ALREADY_EXIST
249 assert it.errorText == 'cm-handle already exists'
253 def 'Create CM-Handle Error Handling: Registration fails: #scenario'() {
254 given: 'a registration without cm-handle properties'
255 def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server')
256 dmiPluginRegistration.createdCmHandles = [new NcmpServiceCmHandle(cmHandleId: 'cmhandle')]
257 and: 'cm-handler registration fails: #scenario'
258 mockLcmEventsCmHandleStateHandler.initiateStateAdvised(*_) >> { throw exception }
259 when: 'registration is updated'
260 def response = objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
261 then: 'a failure response is received'
262 response.createdCmHandles.size() == 1
263 with(response.createdCmHandles[0]) {
264 assert it.status == Status.FAILURE
265 assert it.cmHandle == 'cmhandle'
266 assert it.ncmpResponseStatus == expectedError
267 assert it.errorText == expectedErrorText
270 scenario | exception || expectedError | expectedErrorText
271 'cm-handle already exist' | AlreadyDefinedException.forDataNodes(["path[@id='cmhandle']"], 'some-context') || CM_HANDLE_ALREADY_EXIST | 'cm-handle already exists'
272 'unknown exception while registering cm-handle' | new RuntimeException('Failed') || UNKNOWN_ERROR | 'Failed'
275 def 'Update CM-Handle: Update Operation Response is added to the response'() {
276 given: 'a registration to update CmHandles'
277 def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server', updatedCmHandles: [{}])
278 and: 'cm-handle updates can be processed successfully'
279 def updateOperationResponse = [CmHandleRegistrationResponse.createSuccessResponse('cm-handle-1'),
280 CmHandleRegistrationResponse.createFailureResponse('cm-handle-2', new Exception("Failed")),
281 CmHandleRegistrationResponse.createFailureResponse('cm-handle-3', CM_HANDLES_NOT_FOUND),
282 CmHandleRegistrationResponse.createFailureResponse('cm handle 4', CM_HANDLE_INVALID_ID)]
283 mockNetworkCmProxyDataServicePropertyHandler.updateCmHandleProperties(_) >> updateOperationResponse
284 when: 'registration is updated'
285 def response = objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
286 then: 'the response contains updateOperationResponse'
287 assert response.updatedCmHandles.size() == 4
288 assert response.updatedCmHandles.containsAll(updateOperationResponse)
291 def 'Remove CmHandle Successfully: #scenario'() {
292 given: 'a registration'
293 def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server', removedCmHandles: ['cmhandle'])
295 mockCpsModuleService.deleteSchemaSetsWithCascade(_, ['cmhandle']) >> { if (!schemaSetExist) { throw new SchemaSetNotFoundException('', '') } }
296 when: 'registration is updated to delete cmhandle'
297 def response = objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
298 then: 'the cmHandle state is updated to "DELETING"'
299 1 * mockLcmEventsCmHandleStateHandler.updateCmHandleStateBatch(_) >>
300 { args -> args[0].values()[0] == CmHandleState.DELETING }
301 then: 'method to delete relevant schema set is called once'
302 1 * mockInventoryPersistence.deleteSchemaSetsWithCascade(_)
303 and: 'method to delete relevant list/list element is called once'
304 1 * mockInventoryPersistence.deleteDataNodes(_)
305 and: 'successful response is received'
306 assert response.removedCmHandles.size() == 1
307 with(response.removedCmHandles[0]) {
308 assert it.status == Status.SUCCESS
309 assert it.cmHandle == 'cmhandle'
311 and: 'the cmHandle state is updated to "DELETED"'
312 1 * mockLcmEventsCmHandleStateHandler.updateCmHandleStateBatch(_) >>
313 { args -> args[0].values()[0] == CmHandleState.DELETED }
314 and: 'No cm handles state updates for "upgraded cm handles"'
315 1 * mockLcmEventsCmHandleStateHandler.updateCmHandleStateBatch([:])
317 scenario | schemaSetExist
318 'schema-set exists and can be deleted successfully' | true
319 'schema-set does not exist' | false
322 def 'Remove CmHandle: Partial Success'() {
323 given: 'a registration with three cm-handles to be deleted'
324 def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server',
325 removedCmHandles: ['cmhandle1', 'cmhandle2', 'cmhandle3'])
326 and: 'cm-handle deletion fails on batch'
327 mockInventoryPersistence.deleteDataNodes(_) >> { throw new RuntimeException("Failed") }
328 and: 'cm-handle deletion is successful for 1st and 3rd; failed for 2nd'
329 mockInventoryPersistence.deleteDataNode("/dmi-registry/cm-handles[@id='cmhandle2']") >> { throw new RuntimeException("Failed") }
330 when: 'registration is updated to delete cmhandles'
331 def response = objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
332 then: 'the cmHandle states are all updated to "DELETING"'
333 1 * mockLcmEventsCmHandleStateHandler.updateCmHandleStateBatch({ assert it.every { entry -> entry.value == CmHandleState.DELETING } })
334 and: 'a response is received for all cm-handles'
335 response.removedCmHandles.size() == 3
336 and: 'successfully de-registered cm handle 1 is removed from in progress map'
337 1 * mockModuleSyncStartedOnCmHandles.remove('cmhandle1')
338 and: 'successfully de-registered cm handle 3 is removed from in progress map even though it was already being removed'
339 1 * mockModuleSyncStartedOnCmHandles.remove('cmhandle3') >> 'already in progress'
340 and: 'failed de-registered cm handle entries should NOT be removed from in progress map'
341 0 * mockModuleSyncStartedOnCmHandles.remove('cmhandle2')
342 and: '1st and 3rd cm-handle deletes successfully'
343 with(response.removedCmHandles[0]) {
344 assert it.status == Status.SUCCESS
345 assert it.cmHandle == 'cmhandle1'
347 with(response.removedCmHandles[2]) {
348 assert it.status == Status.SUCCESS
349 assert it.cmHandle == 'cmhandle3'
351 and: '2nd cm-handle deletion fails'
352 with(response.removedCmHandles[1]) {
353 assert it.status == Status.FAILURE
354 assert it.ncmpResponseStatus == UNKNOWN_ERROR
355 assert it.errorText == 'Failed'
356 assert it.cmHandle == 'cmhandle2'
358 and: 'the cmHandle state is updated to DELETED for 1st and 3rd'
359 1 * mockLcmEventsCmHandleStateHandler.updateCmHandleStateBatch({
360 assert it.size() == 2
361 assert it.every { entry -> entry.value == CmHandleState.DELETED }
363 and: 'No cm handles state updates for "upgraded cm handles"'
364 1 * mockLcmEventsCmHandleStateHandler.updateCmHandleStateBatch([:])
368 def 'Remove CmHandle Error Handling: Schema Set Deletion failed'() {
369 given: 'a registration'
370 def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server',
371 removedCmHandles: ['cmhandle'])
372 and: 'schema set batch deletion failed with unknown error'
373 mockInventoryPersistence.deleteSchemaSetsWithCascade(_) >> { throw new RuntimeException('Failed') }
374 and: 'schema set single deletion failed with unknown error'
375 mockInventoryPersistence.deleteSchemaSetWithCascade(_) >> { throw new RuntimeException('Failed') }
376 when: 'registration is updated to delete cmhandle'
377 def response = objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
378 then: 'no exception is thrown'
380 and: 'cm-handle is not deleted'
381 0 * mockInventoryPersistence.deleteDataNodes(_)
382 and: 'the cmHandle state is not updated to "DELETED"'
383 0 * mockLcmEventsCmHandleStateHandler.updateCmHandleStateBatch([yangModelCmHandle: CmHandleState.DELETED])
384 and: 'a failure response is received'
385 assert response.removedCmHandles.size() == 1
386 with(response.removedCmHandles[0]) {
387 assert it.status == Status.FAILURE
388 assert it.cmHandle == 'cmhandle'
389 assert it.errorText == 'Failed'
390 assert it.ncmpResponseStatus == UNKNOWN_ERROR
394 def 'Remove CmHandle Error Handling: #scenario'() {
395 given: 'a registration'
396 def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server',
397 removedCmHandles: ['cmhandle'])
398 and: 'cm-handle deletion fails on batch'
399 mockInventoryPersistence.deleteDataNodes(_) >> { throw deleteListElementException }
400 and: 'cm-handle deletion fails on individual delete'
401 mockInventoryPersistence.deleteDataNode(_) >> { throw deleteListElementException }
402 when: 'registration is updated to delete cmhandle'
403 def response = objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
404 then: 'a failure response is received'
405 assert response.removedCmHandles.size() == 1
406 with(response.removedCmHandles[0]) {
407 assert it.status == Status.FAILURE
408 assert it.cmHandle == 'cmhandle'
409 assert it.ncmpResponseStatus == expectedError
410 assert it.errorText == expectedErrorText
412 and: 'the cm handle state is not updated to "DELETED"'
413 0 * mockLcmEventsCmHandleStateHandler.updateCmHandleStateBatch(_, CmHandleState.DELETED)
415 scenario | cmHandleId | deleteListElementException || expectedError | expectedErrorText
416 'cm-handle does not exist' | 'cmhandle' | new DataNodeNotFoundException('', '', '') || CM_HANDLES_NOT_FOUND | 'cm handle id(s) not found'
417 'cm-handle has invalid name' | 'cm handle with space' | new DataValidationException('', '') || CM_HANDLE_INVALID_ID | 'cm-handle has an invalid character(s) in id'
418 'an unexpected exception' | 'cmhandle' | new RuntimeException('Failed') || UNKNOWN_ERROR | 'Failed'