diff options
Diffstat (limited to 'integration-test/src/test/groovy/org')
4 files changed, 381 insertions, 148 deletions
diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/base/CpsIntegrationSpecBase.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/base/CpsIntegrationSpecBase.groovy index ecf7d67d2d..23504e49cd 100644 --- a/integration-test/src/test/groovy/org/onap/cps/integration/base/CpsIntegrationSpecBase.groovy +++ b/integration-test/src/test/groovy/org/onap/cps/integration/base/CpsIntegrationSpecBase.groovy @@ -30,6 +30,10 @@ import org.onap.cps.integration.DatabaseTestContainer import org.onap.cps.ncmp.api.NetworkCmProxyCmHandleQueryService import org.onap.cps.ncmp.api.NetworkCmProxyDataService import org.onap.cps.ncmp.api.NetworkCmProxyQueryService +import org.onap.cps.ncmp.api.impl.inventory.CmHandleState +import org.onap.cps.ncmp.api.impl.inventory.sync.ModuleSyncWatchdog +import org.onap.cps.ncmp.api.models.DmiPluginRegistration +import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle import org.onap.cps.spi.exceptions.DataspaceNotFoundException import org.onap.cps.spi.model.DataNode import org.onap.cps.spi.repository.DataspaceRepository @@ -40,10 +44,17 @@ import org.springframework.boot.autoconfigure.domain.EntityScan import org.springframework.boot.test.context.SpringBootTest import org.springframework.context.annotation.ComponentScan import org.springframework.data.jpa.repository.config.EnableJpaRepositories +import org.springframework.http.HttpStatus +import org.springframework.http.MediaType +import org.springframework.test.web.client.MockRestServiceServer import org.springframework.web.client.RestTemplate import org.testcontainers.spock.Testcontainers import spock.lang.Shared import spock.lang.Specification +import spock.util.concurrent.PollingConditions + +import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo +import static org.springframework.test.web.client.response.MockRestResponseCreators.withStatus @SpringBootTest(classes = [CpsDataspaceService]) @Testcontainers @@ -75,9 +86,6 @@ abstract class CpsIntegrationSpecBase extends Specification { SessionManager sessionManager @Autowired - RestTemplate restTemplate - - @Autowired NetworkCmProxyCmHandleQueryService networkCmProxyCmHandleQueryService @Autowired @@ -86,6 +94,16 @@ abstract class CpsIntegrationSpecBase extends Specification { @Autowired NetworkCmProxyQueryService networkCmProxyQueryService + @Autowired + RestTemplate restTemplate + + @Autowired + ModuleSyncWatchdog moduleSyncWatchdog + + MockRestServiceServer mockDmiServer = null + + static final DMI_URL = 'http://mock-dmi-server' + def static GENERAL_TEST_DATASPACE = 'generalTestDataspace' def static BOOKSTORE_SCHEMA_SET = 'bookstoreSchemaSet' @@ -98,8 +116,19 @@ abstract class CpsIntegrationSpecBase extends Specification { createStandardBookStoreSchemaSet(GENERAL_TEST_DATASPACE) initialized = true } + mockDmiServer = MockRestServiceServer.createServer(restTemplate) + } + + def cleanup() { + mockDmiServer.reset() } + def static readResourceDataFile(filename) { + return new File('src/test/resources/data/' + filename).text + } + + // *** CPS Integration Test Utilities *** + def static countDataNodesInTree(DataNode dataNode) { return 1 + countDataNodesInTree(dataNode.getChildDataNodes()) } @@ -112,10 +141,6 @@ abstract class CpsIntegrationSpecBase extends Specification { return nodeCount } - def static readResourceDataFile(filename) { - return new File('src/test/resources/data/' + filename).text - } - def getBookstoreYangResourcesNameToContentMap() { def bookstoreModelFileContent = readResourceDataFile('bookstore/bookstore.yang') def bookstoreTypesFileContent = readResourceDataFile('bookstore/bookstore-types.yang') @@ -158,4 +183,35 @@ abstract class CpsIntegrationSpecBase extends Specification { return '"' + name + '":[' + innerJson + ']' } + // *** NCMP Integration Test Utilities *** + + def registerCmHandle(dmiPlugin, cmHandleId, moduleSetTag, dmiModuleReferencesResponse, dmiModuleResourcesResponse) { + def cmHandleToCreate = new NcmpServiceCmHandle(cmHandleId: cmHandleId, moduleSetTag: moduleSetTag) + networkCmProxyDataService.updateDmiRegistrationAndSyncModule(new DmiPluginRegistration(dmiPlugin: dmiPlugin, createdCmHandles: [cmHandleToCreate])) + mockDmiResponsesForRegistration(dmiPlugin, cmHandleId, dmiModuleReferencesResponse, dmiModuleResourcesResponse) + moduleSyncWatchdog.moduleSyncAdvisedCmHandles() + new PollingConditions().within(3, () -> { + CmHandleState.READY == networkCmProxyDataService.getCmHandleCompositeState(cmHandleId).cmHandleState + }) + mockDmiServer.reset() + } + + def deregisterCmHandle(dmiPlugin, cmHandleId) { + deregisterCmHandles(dmiPlugin, [cmHandleId]) + } + + def deregisterCmHandles(dmiPlugin, cmHandleIds) { + networkCmProxyDataService.updateDmiRegistrationAndSyncModule(new DmiPluginRegistration(dmiPlugin: dmiPlugin, removedCmHandles: cmHandleIds)) + } + + def mockDmiResponsesForRegistration(dmiPlugin, cmHandleId, dmiModuleReferencesResponse, dmiModuleResourcesResponse) { + if (dmiModuleReferencesResponse != null) { + mockDmiServer.expect(requestTo("${dmiPlugin}/dmi/v1/ch/${cmHandleId}/modules")) + .andRespond(withStatus(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON).body(dmiModuleReferencesResponse)) + } + if (dmiModuleResourcesResponse != null) { + mockDmiServer.expect(requestTo("${dmiPlugin}/dmi/v1/ch/${cmHandleId}/moduleResources")) + .andRespond(withStatus(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON).body(dmiModuleResourcesResponse)) + } + } } diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/functional/NcmpCmHandleCreateSpec.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/functional/NcmpCmHandleCreateSpec.groovy new file mode 100644 index 0000000000..6707753257 --- /dev/null +++ b/integration-test/src/test/groovy/org/onap/cps/integration/functional/NcmpCmHandleCreateSpec.groovy @@ -0,0 +1,131 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2024 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the 'License'); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an 'AS IS' BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.integration.functional + +import org.onap.cps.integration.base.CpsIntegrationSpecBase +import org.onap.cps.ncmp.api.NetworkCmProxyDataService +import org.onap.cps.ncmp.api.impl.inventory.CmHandleState +import org.onap.cps.ncmp.api.impl.inventory.LockReasonCategory +import org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse +import org.onap.cps.ncmp.api.models.DmiPluginRegistration +import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle +import org.springframework.http.HttpStatus +import spock.util.concurrent.PollingConditions + +import static org.springframework.test.web.client.match.MockRestRequestMatchers.anything +import static org.springframework.test.web.client.response.MockRestResponseCreators.withStatus + +class NcmpCmHandleCreateSpec extends CpsIntegrationSpecBase { + + NetworkCmProxyDataService objectUnderTest + + static final MODULE_REFERENCES_RESPONSE_A = readResourceDataFile('mock-dmi-responses/bookStoreAWithModules_M1_M2_Response.json') + static final MODULE_RESOURCES_RESPONSE_A = readResourceDataFile('mock-dmi-responses/bookStoreAWithModules_M1_M2_ResourcesResponse.json') + static final MODULE_REFERENCES_RESPONSE_B = readResourceDataFile('mock-dmi-responses/bookStoreBWithModules_M1_M3_Response.json') + static final MODULE_RESOURCES_RESPONSE_B = readResourceDataFile('mock-dmi-responses/bookStoreBWithModules_M1_M3_ResourcesResponse.json') + + def setup() { + objectUnderTest = networkCmProxyDataService + } + + def 'CM Handle registration is successful.'() { + given: 'DMI will return modules when requested' + mockDmiResponsesForRegistration(DMI_URL, 'ch-1', MODULE_REFERENCES_RESPONSE_A, MODULE_RESOURCES_RESPONSE_A) + + when: 'a CM-handle is registered for creation' + def cmHandleToCreate = new NcmpServiceCmHandle(cmHandleId: 'ch-1') + def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: DMI_URL, createdCmHandles: [cmHandleToCreate]) + def dmiPluginRegistrationResponse = networkCmProxyDataService.updateDmiRegistrationAndSyncModule(dmiPluginRegistration) + + then: 'registration gives successful response' + assert dmiPluginRegistrationResponse.createdCmHandles == [CmHandleRegistrationResponse.createSuccessResponse('ch-1')] + + and: 'CM-handle is initially in ADVISED state' + assert CmHandleState.ADVISED == objectUnderTest.getCmHandleCompositeState('ch-1').cmHandleState + + when: 'module sync runs' + moduleSyncWatchdog.moduleSyncAdvisedCmHandles() + + then: 'CM-handle goes to READY state' + new PollingConditions().within(3, () -> { + assert CmHandleState.READY == objectUnderTest.getCmHandleCompositeState('ch-1').cmHandleState + }) + + and: 'the CM-handle has expected modules' + assert ['M1', 'M2'] == objectUnderTest.getYangResourcesModuleReferences('ch-1').moduleName.sort() + + and: 'DMI received expected requests' + mockDmiServer.verify() + + cleanup: 'deregister CM handle' + deregisterCmHandle(DMI_URL, 'ch-1') + } + + def 'CM Handle goes to LOCKED state when DMI gives error during module sync.'() { + given: 'DMI is not available to handle requests' + mockDmiServer.expect(anything()).andRespond(withStatus(HttpStatus.SERVICE_UNAVAILABLE)) + + when: 'a CM-handle is registered for creation' + def cmHandleToCreate = new NcmpServiceCmHandle(cmHandleId: 'ch-1') + def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: DMI_URL, createdCmHandles: [cmHandleToCreate]) + networkCmProxyDataService.updateDmiRegistrationAndSyncModule(dmiPluginRegistration) + + and: 'module sync runs' + moduleSyncWatchdog.moduleSyncAdvisedCmHandles() + + then: 'CM-handle goes to LOCKED state with reason MODULE_SYNC_FAILED' + new PollingConditions().within(3, () -> { + def cmHandleCompositeState = objectUnderTest.getCmHandleCompositeState('ch-1') + assert cmHandleCompositeState.cmHandleState == CmHandleState.LOCKED + assert cmHandleCompositeState.lockReason.lockReasonCategory == LockReasonCategory.MODULE_SYNC_FAILED + }) + + and: 'CM-handle has no modules' + assert objectUnderTest.getYangResourcesModuleReferences('ch-1').empty + + cleanup: 'deregister CM handle' + deregisterCmHandle(DMI_URL, 'ch-1') + } + + def 'Create a CM-handle with existing moduleSetTag.'() { + given: 'existing CM-handles cm-1 with moduleSetTag "A", and cm-2 with moduleSetTag "B"' + registerCmHandle(DMI_URL, 'ch-1', 'A', MODULE_REFERENCES_RESPONSE_A, MODULE_RESOURCES_RESPONSE_A) + registerCmHandle(DMI_URL, 'ch-2', 'B', MODULE_REFERENCES_RESPONSE_B, MODULE_RESOURCES_RESPONSE_B) + assert ['M1', 'M2'] == objectUnderTest.getYangResourcesModuleReferences('ch-1').moduleName.sort() + assert ['M1', 'M3'] == objectUnderTest.getYangResourcesModuleReferences('ch-2').moduleName.sort() + + when: 'a CM-handle is registered for creation with moduleSetTag "B"' + def cmHandleToCreate = new NcmpServiceCmHandle(cmHandleId: 'ch-3', moduleSetTag: 'B') + networkCmProxyDataService.updateDmiRegistrationAndSyncModule(new DmiPluginRegistration(dmiPlugin: DMI_URL, createdCmHandles: [cmHandleToCreate])) + + then: 'the CM-handle goes to READY state after module sync' + moduleSyncWatchdog.moduleSyncAdvisedCmHandles() + new PollingConditions().within(3, () -> { + assert CmHandleState.READY == objectUnderTest.getCmHandleCompositeState('ch-3').cmHandleState + }) + + and: 'the CM-handle has expected modules from module set "B": M1 and M3' + ['M1', 'M3'] == objectUnderTest.getYangResourcesModuleReferences('ch-3').moduleName.sort() + + cleanup: 'deregister CM handles' + deregisterCmHandles(DMI_URL, ['ch-1', 'ch-2', 'ch-3']) + } +} diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/functional/NcmpCmHandleRegistrationSpec.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/functional/NcmpCmHandleRegistrationSpec.groovy deleted file mode 100644 index 9cd4d7cbf9..0000000000 --- a/integration-test/src/test/groovy/org/onap/cps/integration/functional/NcmpCmHandleRegistrationSpec.groovy +++ /dev/null @@ -1,141 +0,0 @@ -/* - * ============LICENSE_START======================================================= - * Copyright (C) 2024 Nordix Foundation - * ================================================================================ - * Licensed under the Apache License, Version 2.0 (the 'License'); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an 'AS IS' BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - * ============LICENSE_END========================================================= - */ - -package org.onap.cps.integration.functional - -import org.onap.cps.integration.base.CpsIntegrationSpecBase -import org.onap.cps.ncmp.api.NetworkCmProxyDataService -import org.onap.cps.ncmp.api.impl.inventory.CmHandleState -import org.onap.cps.ncmp.api.impl.inventory.sync.ModuleSyncWatchdog -import org.onap.cps.ncmp.api.models.DmiPluginRegistration -import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle -import org.onap.cps.spi.exceptions.DataNodeNotFoundException -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.http.HttpStatus -import org.springframework.http.MediaType -import org.springframework.test.web.client.MockRestServiceServer -import spock.util.concurrent.PollingConditions - -import static org.springframework.test.web.client.match.MockRestRequestMatchers.anything -import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo -import static org.springframework.test.web.client.response.MockRestResponseCreators.withStatus - -class NcmpCmHandleRegistrationSpec extends CpsIntegrationSpecBase { - - NetworkCmProxyDataService objectUnderTest - - @Autowired - ModuleSyncWatchdog moduleSyncWatchdog - - static final DMI_URL = 'http://mock-dmi-server' - def mockDmiServer - def moduleReferencesResponse - def moduleResourcesResponse - - def setup() { - objectUnderTest = networkCmProxyDataService - mockDmiServer = MockRestServiceServer.createServer(restTemplate) - moduleReferencesResponse = readResourceDataFile('mock-dmi-responses/ietfYangModuleResponse.json') - moduleResourcesResponse = readResourceDataFile('mock-dmi-responses/ietfYangModuleResourcesResponse.json') - } - - def 'CM Handle is READY when Registration is successful.'() { - given: 'a CM handle to create' - def cmHandlesToCreate = [new NcmpServiceCmHandle(cmHandleId: 'cm-1')] - - and: 'DMI registration params' - def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: DMI_URL, createdCmHandles: cmHandlesToCreate) - dmiPluginRegistration.validateDmiPluginRegistration() - - and: 'DMI returns modules' - mockDmiServer.expect(requestTo("${DMI_URL}/dmi/v1/ch/cm-1/modules")) - .andRespond(withStatus(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON).body(moduleReferencesResponse)) - mockDmiServer.expect(requestTo("${DMI_URL}/dmi/v1/ch/cm-1/moduleResources")) - .andRespond(withStatus(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON).body(moduleResourcesResponse)) - - when: 'a CM-handle is registered' - def dmiPluginRegistrationResponse = networkCmProxyDataService.updateDmiRegistrationAndSyncModule(dmiPluginRegistration); - - then: 'registration gives expected response' - assert dmiPluginRegistrationResponse.createdCmHandles.size() == 1 - - and: 'CM-handle is initially in ADVISED state' - assert CmHandleState.ADVISED == objectUnderTest.getCmHandleCompositeState('cm-1').cmHandleState - - when: 'module sync runs' - moduleSyncWatchdog.moduleSyncAdvisedCmHandles() - - then: 'CM-handle goes to READY state' - new PollingConditions().within(3, () -> { - assert CmHandleState.READY == objectUnderTest.getCmHandleCompositeState('cm-1').cmHandleState - }) - - and: 'DMI received expected requests' - mockDmiServer.verify() - } - - def 'CM Handle goes to LOCKED state when DMI gives error during module sync.'() { - given: 'a CM handle to create' - def cmHandlesToCreate = [new NcmpServiceCmHandle(cmHandleId: 'cm-2')] - - and: 'DMI registration params' - def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: DMI_URL, createdCmHandles: cmHandlesToCreate) - dmiPluginRegistration.validateDmiPluginRegistration() - - and: 'DMI returns error code' - mockDmiServer.expect(anything()).andRespond(withStatus(HttpStatus.SERVICE_UNAVAILABLE)) - - when: 'a CM-handle is registered' - def dmiPluginRegistrationResponse = networkCmProxyDataService.updateDmiRegistrationAndSyncModule(dmiPluginRegistration); - - then: 'registration gives expected response' - assert dmiPluginRegistrationResponse.createdCmHandles.size() == 1 - - and: 'CM-handle is initially in ADVISED state' - assert CmHandleState.ADVISED == objectUnderTest.getCmHandleCompositeState('cm-2').cmHandleState - - when: 'module sync runs' - moduleSyncWatchdog.moduleSyncAdvisedCmHandles() - - then: 'CM-handle goes to LOCKED state' - new PollingConditions().within(3, () -> { - assert CmHandleState.LOCKED == objectUnderTest.getCmHandleCompositeState('cm-2').cmHandleState - }) - - and: 'DMI received expected requests' - mockDmiServer.verify() - } - - def 'Deregister CM-handles.'() { - given: 'a list of CM handles to remove' - def cmHandlesToRemove = ['cm-1', 'cm-2'] - - and: 'DMI registration parameters are set' - def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: DMI_URL, removedCmHandles: cmHandlesToRemove) - dmiPluginRegistration.validateDmiPluginRegistration() - - when: 'the CM-handles are deregistered' - networkCmProxyDataService.updateDmiRegistrationAndSyncModule(dmiPluginRegistration); - - then: 'the CM-handles no longer exists' - assert !objectUnderTest.getAllCmHandleIdsByDmiPluginIdentifier(DMI_URL).contains('cm-1') - assert !objectUnderTest.getAllCmHandleIdsByDmiPluginIdentifier(DMI_URL).contains('cm-2') - } -} diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/functional/NcmpCmHandleUpgradeSpec.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/functional/NcmpCmHandleUpgradeSpec.groovy new file mode 100644 index 0000000000..6b550a76bc --- /dev/null +++ b/integration-test/src/test/groovy/org/onap/cps/integration/functional/NcmpCmHandleUpgradeSpec.groovy @@ -0,0 +1,187 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2024 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the 'License'); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an 'AS IS' BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.integration.functional + +import org.onap.cps.integration.base.CpsIntegrationSpecBase +import org.onap.cps.ncmp.api.NetworkCmProxyDataService +import org.onap.cps.ncmp.api.impl.inventory.CmHandleState +import org.onap.cps.ncmp.api.impl.inventory.LockReasonCategory +import org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse +import org.onap.cps.ncmp.api.models.DmiPluginRegistration +import org.onap.cps.ncmp.api.models.UpgradedCmHandles +import org.springframework.http.HttpStatus +import spock.lang.Ignore +import spock.util.concurrent.PollingConditions + +import static org.springframework.test.web.client.match.MockRestRequestMatchers.anything +import static org.springframework.test.web.client.response.MockRestResponseCreators.withStatus + +class NcmpCmHandleUpgradeSpec extends CpsIntegrationSpecBase { + + NetworkCmProxyDataService objectUnderTest + + static final INITIAL_MODULE_REFERENCES_RESPONSE = readResourceDataFile('mock-dmi-responses/bookStoreAWithModules_M1_M2_Response.json') + static final INITIAL_MODULE_RESOURCES_RESPONSE = readResourceDataFile('mock-dmi-responses/bookStoreAWithModules_M1_M2_ResourcesResponse.json') + static final UPDATED_MODULE_REFERENCES_RESPONSE = readResourceDataFile('mock-dmi-responses/bookStoreBWithModules_M1_M3_Response.json') + static final UPDATED_MODULE_RESOURCES_RESPONSE = readResourceDataFile('mock-dmi-responses/bookStoreBWithModules_M1_M3_ResourcesResponse.json') + static final NO_MODULE_SET_TAG = '' + static final CM_HANDLE_ID = 'ch-1' + static final CM_HANDLE_ID_WITH_EXISTING_MODULE_SET_TAG = 'ch-2' + + def setup() { + objectUnderTest = networkCmProxyDataService + } + + @Ignore + def 'Upgrade CM-handle with new moduleSetTag or no moduleSetTag.'() { + given: 'an existing CM-handle with expected initial modules: M1 and M2' + registerCmHandle(DMI_URL, CM_HANDLE_ID, initialModuleSetTag, INITIAL_MODULE_REFERENCES_RESPONSE, INITIAL_MODULE_RESOURCES_RESPONSE) + assert ['M1', 'M2'] == objectUnderTest.getYangResourcesModuleReferences(CM_HANDLE_ID).moduleName.sort() + + and: 'DMI returns different modules for upgrade' + mockDmiResponsesForRegistration(DMI_URL, CM_HANDLE_ID, UPDATED_MODULE_REFERENCES_RESPONSE, UPDATED_MODULE_RESOURCES_RESPONSE) + + when: "CM-handle is upgraded with given moduleSetTag '${updatedModuleSetTag}'" + def cmHandlesToUpgrade = new UpgradedCmHandles(cmHandles: [CM_HANDLE_ID], moduleSetTag: updatedModuleSetTag) + def dmiPluginRegistrationResponse = networkCmProxyDataService.updateDmiRegistrationAndSyncModule( + new DmiPluginRegistration(dmiPlugin: DMI_URL, upgradedCmHandles: cmHandlesToUpgrade)) + + then: 'registration gives successful response' + assert dmiPluginRegistrationResponse.upgradedCmHandles == [CmHandleRegistrationResponse.createSuccessResponse(CM_HANDLE_ID)] + + and: 'CM-handle is in LOCKED state due to MODULE_UPGRADE' + def cmHandleCompositeState = objectUnderTest.getCmHandleCompositeState(CM_HANDLE_ID) + assert cmHandleCompositeState.cmHandleState == CmHandleState.LOCKED + assert cmHandleCompositeState.lockReason.lockReasonCategory == LockReasonCategory.MODULE_UPGRADE + assert cmHandleCompositeState.lockReason.details == "Upgrade to ModuleSetTag: ${updatedModuleSetTag}" + + when: 'module sync runs' + moduleSyncWatchdog.resetPreviouslyFailedCmHandles() + moduleSyncWatchdog.moduleSyncAdvisedCmHandles() + + then: 'CM-handle goes to READY state' + new PollingConditions().within(3, () -> { + assert CmHandleState.READY == objectUnderTest.getCmHandleCompositeState(CM_HANDLE_ID).cmHandleState + }) + + and: 'CM-handle has expected updated modules: M1 and M3' + assert ['M1', 'M3'] == objectUnderTest.getYangResourcesModuleReferences(CM_HANDLE_ID).moduleName.sort() + + and: 'DMI received expected requests' + mockDmiServer.verify() + + cleanup: 'deregister CM-handle' + deregisterCmHandle(DMI_URL, CM_HANDLE_ID) + + where: + initialModuleSetTag | updatedModuleSetTag + NO_MODULE_SET_TAG | NO_MODULE_SET_TAG + NO_MODULE_SET_TAG | 'new' + 'initial' | NO_MODULE_SET_TAG + 'initial' | 'new' + } + + def 'Upgrade CM-handle with existing moduleSetTag.'() { + given: "an existing CM-handle handle with moduleSetTag '${updatedModuleSetTag}'" + registerCmHandle(DMI_URL, CM_HANDLE_ID_WITH_EXISTING_MODULE_SET_TAG, updatedModuleSetTag, UPDATED_MODULE_REFERENCES_RESPONSE, UPDATED_MODULE_RESOURCES_RESPONSE) + assert ['M1', 'M3'] == objectUnderTest.getYangResourcesModuleReferences(CM_HANDLE_ID_WITH_EXISTING_MODULE_SET_TAG).moduleName.sort() + and: "a CM-handle with moduleSetTag '${initialModuleSetTag}' which will be upgraded" + registerCmHandle(DMI_URL, CM_HANDLE_ID, initialModuleSetTag, INITIAL_MODULE_REFERENCES_RESPONSE, INITIAL_MODULE_RESOURCES_RESPONSE) + assert ['M1', 'M2'] == objectUnderTest.getYangResourcesModuleReferences(CM_HANDLE_ID).moduleName.sort() + + when: "CM-handle is upgraded to moduleSetTag '${updatedModuleSetTag}'" + def cmHandlesToUpgrade = new UpgradedCmHandles(cmHandles: [CM_HANDLE_ID], moduleSetTag: updatedModuleSetTag) + def dmiPluginRegistrationResponse = networkCmProxyDataService.updateDmiRegistrationAndSyncModule( + new DmiPluginRegistration(dmiPlugin: DMI_URL, upgradedCmHandles: cmHandlesToUpgrade)) + + then: 'registration gives successful response' + assert dmiPluginRegistrationResponse.upgradedCmHandles == [CmHandleRegistrationResponse.createSuccessResponse(CM_HANDLE_ID)] + + when: 'module sync runs' + moduleSyncWatchdog.resetPreviouslyFailedCmHandles() + moduleSyncWatchdog.moduleSyncAdvisedCmHandles() + + then: 'CM-handle goes to READY state' + new PollingConditions().within(3, () -> { + assert CmHandleState.READY == objectUnderTest.getCmHandleCompositeState(CM_HANDLE_ID).cmHandleState + }) + + and: 'CM-handle has expected updated modules: M1 and M3' + assert ['M1', 'M3'] == objectUnderTest.getYangResourcesModuleReferences(CM_HANDLE_ID).moduleName.sort() + + cleanup: 'deregister CM-handle' + deregisterCmHandles(DMI_URL, [CM_HANDLE_ID, CM_HANDLE_ID_WITH_EXISTING_MODULE_SET_TAG]) + + where: + initialModuleSetTag | updatedModuleSetTag + NO_MODULE_SET_TAG | 'moduleSet2' + 'moduleSet1' | 'moduleSet2' + } + + @Ignore + def 'Skip upgrade of CM-handle with same moduleSetTag as before.'() { + given: 'an existing CM-handle with expected initial modules: M1 and M2' + registerCmHandle(DMI_URL, CM_HANDLE_ID, 'same', INITIAL_MODULE_REFERENCES_RESPONSE, INITIAL_MODULE_RESOURCES_RESPONSE) + assert ['M1', 'M2'] == objectUnderTest.getYangResourcesModuleReferences(CM_HANDLE_ID).moduleName.sort() + + when: 'CM-handle is upgraded with the same moduleSetTag' + def cmHandlesToUpgrade = new UpgradedCmHandles(cmHandles: [CM_HANDLE_ID], moduleSetTag: 'same') + networkCmProxyDataService.updateDmiRegistrationAndSyncModule( + new DmiPluginRegistration(dmiPlugin: DMI_URL, upgradedCmHandles: cmHandlesToUpgrade)) + + then: 'CM-handle remains in READY state' + assert CmHandleState.READY == objectUnderTest.getCmHandleCompositeState(CM_HANDLE_ID).cmHandleState + + and: 'CM-handle has same modules as before: M1 and M2' + assert ['M1', 'M2'] == objectUnderTest.getYangResourcesModuleReferences(CM_HANDLE_ID).moduleName.sort() + + cleanup: 'deregister CM-handle' + deregisterCmHandle(DMI_URL, CM_HANDLE_ID) + } + + def 'Upgrade of CM-handle fails due to DMI error.'() { + given: 'an existing CM-handle' + registerCmHandle(DMI_URL, CM_HANDLE_ID, NO_MODULE_SET_TAG, INITIAL_MODULE_REFERENCES_RESPONSE, INITIAL_MODULE_RESOURCES_RESPONSE) + + and: 'DMI returns error code' + mockDmiServer.expect(anything()).andRespond(withStatus(HttpStatus.SERVICE_UNAVAILABLE)) + + when: "CM-handle is upgraded" + def cmHandlesToUpgrade = new UpgradedCmHandles(cmHandles: [CM_HANDLE_ID], moduleSetTag: NO_MODULE_SET_TAG) + networkCmProxyDataService.updateDmiRegistrationAndSyncModule( + new DmiPluginRegistration(dmiPlugin: DMI_URL, upgradedCmHandles: cmHandlesToUpgrade)) + + and: 'module sync runs' + moduleSyncWatchdog.resetPreviouslyFailedCmHandles() + moduleSyncWatchdog.moduleSyncAdvisedCmHandles() + + then: 'CM-handle goes to LOCKED state with reason MODULE_UPGRADE_FAILED' + new PollingConditions().within(3, () -> { + def cmHandleCompositeState = objectUnderTest.getCmHandleCompositeState(CM_HANDLE_ID) + assert cmHandleCompositeState.cmHandleState == CmHandleState.LOCKED + assert cmHandleCompositeState.lockReason.lockReasonCategory == LockReasonCategory.MODULE_UPGRADE_FAILED + }) + + cleanup: 'deregister CM-handle' + deregisterCmHandle(DMI_URL, CM_HANDLE_ID) + } + +} |