From 3ccd62ed40e96dbe2970de1433b2ebdbe014d4aa Mon Sep 17 00:00:00 2001 From: sourabh_sourabh Date: Tue, 17 Oct 2023 14:16:25 +0100 Subject: Code coverage: Update YANG schema set using moduleSetTag - Code coverage is increased to 98. - New groovy test is added to cover new code. Issue-ID: CPS-1798 Signed-off-by: sourabh_sourabh Change-Id: Ia979be3f43ec8e4bbf6f8cb66a8a5e748118f19b Signed-off-by: sourabh_sourabh --- .../NetworkCmProxyRestExceptionHandlerSpec.groovy | 19 ++++---- cps-ncmp-service/pom.xml | 2 +- .../api/impl/NetworkCmProxyDataServiceImpl.java | 16 ++++--- ...rkCmProxyDataServiceImplRegistrationSpec.groovy | 13 ++++-- .../inventory/sync/ModuleSyncServiceSpec.groovy | 50 +++++++++++++++++----- .../api/inventory/sync/ModuleSyncTasksSpec.groovy | 47 ++++++++++++++++++++ .../ncmp/api/inventory/sync/SyncUtilsSpec.groovy | 2 + 7 files changed, 122 insertions(+), 27 deletions(-) diff --git a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/exceptions/NetworkCmProxyRestExceptionHandlerSpec.groovy b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/exceptions/NetworkCmProxyRestExceptionHandlerSpec.groovy index 592f1d165a..dd02b312a8 100644 --- a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/exceptions/NetworkCmProxyRestExceptionHandlerSpec.groovy +++ b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/exceptions/NetworkCmProxyRestExceptionHandlerSpec.groovy @@ -21,6 +21,16 @@ package org.onap.cps.ncmp.rest.exceptions +import static org.springframework.http.HttpStatus.BAD_GATEWAY +import static org.springframework.http.HttpStatus.BAD_REQUEST +import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR +import static org.springframework.http.HttpStatus.NOT_FOUND +import static org.springframework.http.HttpStatus.CONFLICT +import static org.onap.cps.ncmp.rest.exceptions.NetworkCmProxyRestExceptionHandlerSpec.ApiType.NCMP +import static org.onap.cps.ncmp.rest.exceptions.NetworkCmProxyRestExceptionHandlerSpec.ApiType.NCMPINVENTORY +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post + import groovy.json.JsonSlurper import org.mapstruct.factory.Mappers import org.onap.cps.TestUtils @@ -44,18 +54,11 @@ import org.spockframework.spring.SpringBean import org.springframework.beans.factory.annotation.Autowired import org.springframework.beans.factory.annotation.Value import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest -import org.springframework.http.HttpStatus import org.springframework.http.MediaType import org.springframework.test.web.servlet.MockMvc import spock.lang.Shared import spock.lang.Specification -import static org.onap.cps.ncmp.rest.exceptions.NetworkCmProxyRestExceptionHandlerSpec.ApiType.NCMP -import static org.onap.cps.ncmp.rest.exceptions.NetworkCmProxyRestExceptionHandlerSpec.ApiType.NCMPINVENTORY -import static org.springframework.http.HttpStatus.* -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post - @WebMvcTest class NetworkCmProxyRestExceptionHandlerSpec extends Specification { @@ -142,7 +145,7 @@ class NetworkCmProxyRestExceptionHandlerSpec extends Specification { when: 'the DMI request is executed' def response = performTestRequest(NCMP) then: 'NCMP service responds with 502 Bad Gateway status' - response.status == HttpStatus.BAD_GATEWAY.value() + response.status == BAD_GATEWAY.value() and: 'the NCMP response also contains the original DMI response details' response.contentAsString.contains('400') response.contentAsString.contains('Bad Request from DMI') diff --git a/cps-ncmp-service/pom.xml b/cps-ncmp-service/pom.xml index d7c1774731..04a8345c9e 100644 --- a/cps-ncmp-service/pom.xml +++ b/cps-ncmp-service/pom.xml @@ -34,7 +34,7 @@ cps-ncmp-service - 0.96 + 0.98 diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java index a37b27199c..b2c71941c5 100755 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java @@ -389,11 +389,7 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService } }); - final List cmHandleRegistrationResponses - = upgradeCmHandles(cmHandleStatePerCmHandle); - cmHandleRegistrationResponses.addAll(CmHandleRegistrationResponse.createFailureResponses(notReadyCmHandles, - CM_HANDLES_NOT_READY)); - return cmHandleRegistrationResponses; + return prepareAndGetCmHandleUpgradeResponses(cmHandleStatePerCmHandle, notReadyCmHandles); } private CmHandleRegistrationResponse deleteCmHandleAndGetCmHandleRegistrationResponse(final String cmHandleId) { @@ -462,6 +458,16 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService } } + private List prepareAndGetCmHandleUpgradeResponses(final Map cmHandleStatePerCmHandle, final Collection notReadyCmHandles) { + final List cmHandleRegistrationResponses + = upgradeCmHandles(cmHandleStatePerCmHandle); + final List failedCmHandleRegistrationResponses + = CmHandleRegistrationResponse.createFailureResponses(notReadyCmHandles, CM_HANDLES_NOT_READY); + failedCmHandleRegistrationResponses.forEach(cmHandleRegistrationResponses::add); + return cmHandleRegistrationResponses; + } + private List upgradeCmHandles(final Map cmHandleStatePerCmHandle) { final List cmHandleIds = getCmHandleIds(cmHandleStatePerCmHandle); diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplRegistrationSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplRegistrationSpec.groovy index 9e4737fff0..0f40906b6b 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplRegistrationSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplRegistrationSpec.groovy @@ -21,6 +21,8 @@ package org.onap.cps.ncmp.api.impl +import org.onap.cps.ncmp.api.models.UpgradedCmHandles + import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLES_NOT_FOUND import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLE_ALREADY_EXIST import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLE_INVALID_ID @@ -61,7 +63,7 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification { def mockDmiDataOperations = Mock(DmiDataOperations) def mockNetworkCmProxyDataServicePropertyHandler = Mock(NetworkCmProxyDataServicePropertyHandler) def mockInventoryPersistence = Mock(InventoryPersistence) - def mockCmhandleQueries = Mock(CmHandleQueries) + def mockCmHandleQueries = Mock(CmHandleQueries) def stubbedNetworkCmProxyCmHandlerQueryService = Stub(NetworkCmProxyCmHandleQueryService) def mockLcmEventsCmHandleStateHandler = Mock(LcmEventsCmHandleStateHandler) def mockCpsDataService = Mock(CpsDataService) @@ -69,14 +71,17 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification { def mockTrustLevelPerDmiPlugin = Mock(IMap) def objectUnderTest = getObjectUnderTest() - def 'DMI Registration: Create, Update & Delete operations are processed in the right order'() { + def 'DMI Registration: Create, Update, Delete & Upgrade operations are processed in the right order'() { given: 'a registration with operations of all three types' def dmiRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server') dmiRegistration.setCreatedCmHandles([new NcmpServiceCmHandle(cmHandleId: 'cmhandle-1', publicProperties: ['publicProp1': 'value'], dmiProperties: [:])]) dmiRegistration.setUpdatedCmHandles([new NcmpServiceCmHandle(cmHandleId: 'cmhandle-2', publicProperties: ['publicProp1': 'value'], dmiProperties: [:])]) dmiRegistration.setRemovedCmHandles(['cmhandle-2']) + dmiRegistration.setUpgradedCmHandles(new UpgradedCmHandles(cmHandles: ['cmhandle-3'], moduleSetTag: 'some-module-set-tag')) and: 'cm handles are persisted' mockInventoryPersistence.getYangModelCmHandles(['cmhandle-2']) >> [new YangModelCmHandle()] + and: 'cm handle is in READY state' + mockCmHandleQueries.cmHandleHasState('cmhandle-3', CmHandleState.READY) >> true when: 'registration is processed' objectUnderTest.updateDmiRegistrationAndSyncModule(dmiRegistration) then: 'cm-handles are removed first' @@ -87,6 +92,8 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification { 1 * objectUnderTest.parseAndProcessCreatedCmHandlesInRegistration(*_) then: 'cm-handles are updated' 1 * mockNetworkCmProxyDataServicePropertyHandler.updateCmHandleProperties(*_) + then: 'cm-handles are upgraded' + 1 * objectUnderTest.parseAndProcessUpgradedCmHandlesInRegistration(*_) } def 'DMI Registration: Response from all operations types are in response'() { @@ -378,7 +385,7 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification { def getObjectUnderTest() { return Spy(new NetworkCmProxyDataServiceImpl(spiedJsonObjectMapper, mockDmiDataOperations, - mockNetworkCmProxyDataServicePropertyHandler, mockInventoryPersistence, mockCmhandleQueries, + mockNetworkCmProxyDataServicePropertyHandler, mockInventoryPersistence, mockCmHandleQueries, stubbedNetworkCmProxyCmHandlerQueryService, mockLcmEventsCmHandleStateHandler, mockCpsDataService, mockModuleSyncStartedOnCmHandles, mockTrustLevelPerDmiPlugin)) } diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncServiceSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncServiceSpec.groovy index b547da7a16..a13f595200 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncServiceSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncServiceSpec.groovy @@ -20,11 +20,15 @@ package org.onap.cps.ncmp.api.inventory.sync -import org.onap.cps.spi.FetchDescendantsOption - +import static org.onap.cps.ncmp.api.impl.inventory.LockReasonCategory.LOCKED_MISBEHAVING +import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DATASPACE_NAME +import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DMI_REGISTRY_ANCHOR import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME import static org.onap.cps.ncmp.api.impl.inventory.LockReasonCategory.MODULE_UPGRADE +import org.onap.cps.ncmp.api.impl.inventory.CmHandleState +import org.onap.cps.spi.FetchDescendantsOption +import org.onap.cps.spi.model.DataNode import org.onap.cps.api.CpsAdminService import org.onap.cps.api.CpsDataService import org.onap.cps.api.CpsModuleService @@ -55,13 +59,12 @@ class ModuleSyncServiceSpec extends Specification { def expectedDataspaceName = NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME def 'Sync model for a (new) cm handle with #scenario'() { - given: 'a cm handle having lock reason : MODULE_UPGRADE' + given: 'a cm handle' def ncmpServiceCmHandle = new NcmpServiceCmHandle() - ncmpServiceCmHandle.setCompositeState(new CompositeStateBuilder() - .withLockReason(MODULE_UPGRADE, 'new moduleSetTag: someModuleSetTag').build()) + ncmpServiceCmHandle.setCompositeState(new CompositeStateBuilder().build()) def dmiServiceName = 'some service name' ncmpServiceCmHandle.cmHandleId = 'cmHandleId-1' - def yangModelCmHandle = YangModelCmHandle.toYangModelCmHandle(dmiServiceName, '', '', ncmpServiceCmHandle,'someModuleSetTag') + def yangModelCmHandle = YangModelCmHandle.toYangModelCmHandle(dmiServiceName, '', '', ncmpServiceCmHandle,'') and: 'DMI operations returns some module references' def moduleReferences = [ new ModuleReference('module1','1'), new ModuleReference('module2','2') ] mockDmiModelOperations.getModuleReferences(yangModelCmHandle) >> moduleReferences @@ -69,9 +72,6 @@ class ModuleSyncServiceSpec extends Specification { mockCpsModuleService.getYangResourceModuleReferences(expectedDataspaceName) >> toModuleReference(existingModuleResourcesInCps) and: 'DMI-Plugin returns resource(s) for "new" module(s)' mockDmiModelOperations.getNewYangResourcesFromDmi(yangModelCmHandle, [new ModuleReference('module1', '1')]) >> newModuleNameContentToMap - and: 'empty data node list is returned by cps path a query' - mockCmHandleQueries.queryNcmpRegistryByCpsPath("//cm-handles[@module-set-tag='someModuleSetTag']", - FetchDescendantsOption.OMIT_DESCENDANTS) >> Collections.emptyList() when: 'module sync is triggered' mockCpsModuleService.identifyNewModuleReferences(moduleReferences) >> toModuleReference(identifiedNewModuleReferences) objectUnderTest.syncAndCreateOrUpgradeSchemaSetAndAnchor(yangModelCmHandle) @@ -86,7 +86,27 @@ class ModuleSyncServiceSpec extends Specification { 'no new module' | [['module1' : '1'], ['module2' : '2']] | [] | [:] } - def 'Delete Schema Set for CmHandle' () { + def 'upgrade model for a existing cm handle'() { + given: 'a cm handle that is ready but locked for upgrade' + def ncmpServiceCmHandle = new NcmpServiceCmHandle() + ncmpServiceCmHandle.setCompositeState(new CompositeStateBuilder() + .withLockReason(MODULE_UPGRADE, 'new moduleSetTag: targetModuleSetTag').build()) + ncmpServiceCmHandle.setCmHandleId('cmHandleId-1') + def yangModelCmHandle = YangModelCmHandle.toYangModelCmHandle('some service name', '', '', ncmpServiceCmHandle, 'targetModuleSetTag') + mockCmHandleQueries.cmHandleHasState('cmHandleId-1', CmHandleState.READY) >> true + and: 'the module service returns some module references' + def moduleReferences = [new ModuleReference('module1', '1'), new ModuleReference('module2', '2')] + mockCpsModuleService.getYangResourcesModuleReferences(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR) >> moduleReferences + and: 'a cm handle with the same moduleSetTag can be found in the registry' + mockCmHandleQueries.queryNcmpRegistryByCpsPath("//cm-handles[@module-set-tag='targetModuleSetTag']", + FetchDescendantsOption.OMIT_DESCENDANTS) >> [new DataNode(xpath: '/dmi-registry/cm-handles[@id=\'cmHandleId-1\']', leaves: ['id': 'cmHandleId-1', 'cm-handle-state': 'READY'])] + when: 'module upgrade is triggered' + objectUnderTest.syncAndCreateOrUpgradeSchemaSetAndAnchor(yangModelCmHandle) + then: 'the upgrade is delegated to the module service (with the correct parameters)' + 1 * mockCpsModuleService.createOrUpgradeSchemaSetFromModules(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, 'cmHandleId-1', Collections.emptyMap(), moduleReferences) + } + + def 'Delete Schema Set for CmHandle'() { when: 'delete schema set if exists is called' objectUnderTest.deleteSchemaSetIfExists('some-cmhandle-id') then: 'the module service is invoked to delete the correct schema set' @@ -113,6 +133,16 @@ class ModuleSyncServiceSpec extends Specification { result == unsupportedOperationException } + def 'Extract module set tag with #scenario'() { + expect: 'the module set tag is extracted correctly' + assert expectedModuleSetTag == objectUnderTest.extractModuleSetTag(new CompositeStateBuilder().withLockReason(lockReason, 'new moduleSetTag: targetModuleSetTag').build()) + where: 'the following parameters are used' + scenario | lockReason || expectedModuleSetTag + 'locked reason is null' | null || '' + 'locked for other reason' | LOCKED_MISBEHAVING || '' + 'locked for module upgrade' | MODULE_UPGRADE || 'targetModuleSetTag' + } + def toModuleReference(moduleReferenceAsMap) { def moduleReferences = [].withDefault { [:] } moduleReferenceAsMap.forEach(property -> diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncTasksSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncTasksSpec.groovy index a11f14838c..8698136d65 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncTasksSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncTasksSpec.groovy @@ -21,9 +21,15 @@ package org.onap.cps.ncmp.api.inventory.sync +import ch.qos.logback.classic.Level +import ch.qos.logback.classic.Logger +import ch.qos.logback.classic.spi.ILoggingEvent +import ch.qos.logback.core.read.ListAppender import com.hazelcast.config.Config import com.hazelcast.instance.impl.HazelcastInstanceFactory import com.hazelcast.map.IMap +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeEach import org.onap.cps.ncmp.api.impl.events.lcm.LcmEventsCmHandleStateHandler import org.onap.cps.ncmp.api.impl.inventory.sync.ModuleSyncService import org.onap.cps.ncmp.api.impl.inventory.sync.ModuleSyncTasks @@ -35,11 +41,25 @@ import org.onap.cps.ncmp.api.impl.inventory.CompositeStateBuilder import org.onap.cps.ncmp.api.impl.inventory.InventoryPersistence import org.onap.cps.ncmp.api.impl.inventory.LockReasonCategory import org.onap.cps.spi.model.DataNode +import org.slf4j.LoggerFactory import spock.lang.Specification import java.util.concurrent.atomic.AtomicInteger class ModuleSyncTasksSpec extends Specification { + def logger = Spy(ListAppender) + + @BeforeEach + void setup() { + ((Logger) LoggerFactory.getLogger(ModuleSyncTasks.class)).addAppender(logger); + logger.start(); + } + + @AfterEach + void teardown() { + ((Logger) LoggerFactory.getLogger(ModuleSyncTasks.class)).detachAndStopAllAppenders(); + } + def mockInventoryPersistence = Mock(InventoryPersistence) def mockSyncUtils = Mock(SyncUtils) @@ -140,6 +160,29 @@ class ModuleSyncTasksSpec extends Specification { assert moduleSyncStartedOnCmHandles.get('other-cm-handle') != null } + def 'Remove already processed cm handle id from hazelcast map'() { + given: 'hazelcast map contains cm handle id' + moduleSyncStartedOnCmHandles.put('ch-1', 'started') + when: 'remove cm handle entry' + objectUnderTest.removeResetCmHandleFromModuleSyncMap('ch-1') + then: 'an event is logged with level INFO' + def loggingEvent = getLoggingEvent() + assert loggingEvent.level == Level.INFO + and: 'the log indicates the cm handle entry is removed successfully' + assert loggingEvent.formattedMessage == 'ch-1 removed from in progress map' + } + + def 'Remove non-existing cm handle id from hazelcast map'() { + given: 'hazelcast map does not contains cm handle id' + def result = moduleSyncStartedOnCmHandles.get('non-existing-cm-handle') + assert result == null + when: 'remove cm handle entry from hazelcast map' + objectUnderTest.removeResetCmHandleFromModuleSyncMap('non-existing-cm-handle') + then: 'no event is logged' + def loggingEvent = getLoggingEvent() + assert loggingEvent == null + } + def advisedCmHandleAsDataNode(cmHandleId) { return new DataNode(anchorName: cmHandleId, leaves: ['id': cmHandleId, 'cm-handle-state': 'ADVISED']) } @@ -167,4 +210,8 @@ class ModuleSyncTasksSpec extends Specification { def resetModuleSyncStartedOnCmHandles(moduleSyncStartedOnCmHandles) { moduleSyncStartedOnCmHandles.clear(); } + + def getLoggingEvent() { + return logger.list[0] + } } diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/SyncUtilsSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/SyncUtilsSpec.groovy index 00d14cd2a0..df2ad711ed 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/SyncUtilsSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/SyncUtilsSpec.groovy @@ -21,6 +21,7 @@ package org.onap.cps.ncmp.api.inventory.sync +import static org.onap.cps.ncmp.api.impl.inventory.LockReasonCategory.LOCKED_MISBEHAVING import static org.onap.cps.ncmp.api.impl.operations.DatastoreType.PASSTHROUGH_OPERATIONAL import static org.onap.cps.ncmp.api.impl.inventory.LockReasonCategory.MODULE_SYNC_FAILED import static org.onap.cps.ncmp.api.impl.inventory.LockReasonCategory.MODULE_UPGRADE @@ -163,6 +164,7 @@ class SyncUtilsSpec extends Specification{ scenario | lockReasonCategory || logReason | retryAttempt 'module upgrade' | MODULE_UPGRADE || 'Locked for module upgrade.' | true 'module sync failed' | MODULE_SYNC_FAILED || 'First Attempt:' | false + 'lock misbehaving' | LOCKED_MISBEHAVING || 'Locked for other reason' | false } def 'Get a Cm-Handle where #scenario'() { -- cgit 1.2.3-korg