Code coverage: Update YANG schema set using moduleSetTag 04/136204/5
authorsourabh_sourabh <sourabh.sourabh@est.tech>
Tue, 17 Oct 2023 13:16:25 +0000 (14:16 +0100)
committersourabh_sourabh <sourabh.sourabh@est.tech>
Thu, 19 Oct 2023 10:15:04 +0000 (11:15 +0100)
- Code coverage is increased to 98.
- New groovy test is added to cover new code.

Issue-ID: CPS-1798
Signed-off-by: sourabh_sourabh <sourabh.sourabh@est.tech>
Change-Id: Ia979be3f43ec8e4bbf6f8cb66a8a5e748118f19b
Signed-off-by: sourabh_sourabh <sourabh.sourabh@est.tech>
cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/exceptions/NetworkCmProxyRestExceptionHandlerSpec.groovy
cps-ncmp-service/pom.xml
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplRegistrationSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncServiceSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncTasksSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/SyncUtilsSpec.groovy

index 592f1d1..dd02b31 100644 (file)
 
 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')
index d7c1774..04a8345 100644 (file)
@@ -34,7 +34,7 @@
     <artifactId>cps-ncmp-service</artifactId>
 
     <properties>
-        <minimum-coverage>0.96</minimum-coverage>
+        <minimum-coverage>0.98</minimum-coverage>
     </properties>
     <dependencies>
         <dependency>
index a37b271..b2c7194 100755 (executable)
@@ -389,11 +389,7 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
             }
         });
 
-        final List<CmHandleRegistrationResponse> 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<CmHandleRegistrationResponse> prepareAndGetCmHandleUpgradeResponses(final Map<YangModelCmHandle,
+            CmHandleState> cmHandleStatePerCmHandle, final Collection<String> notReadyCmHandles) {
+        final List<CmHandleRegistrationResponse> cmHandleRegistrationResponses
+                = upgradeCmHandles(cmHandleStatePerCmHandle);
+        final List<CmHandleRegistrationResponse> failedCmHandleRegistrationResponses
+                = CmHandleRegistrationResponse.createFailureResponses(notReadyCmHandles, CM_HANDLES_NOT_READY);
+        failedCmHandleRegistrationResponses.forEach(cmHandleRegistrationResponses::add);
+        return cmHandleRegistrationResponses;
+    }
+
     private List<CmHandleRegistrationResponse> upgradeCmHandles(final Map<YangModelCmHandle, CmHandleState>
                                                                         cmHandleStatePerCmHandle) {
         final List<String> cmHandleIds = getCmHandleIds(cmHandleStatePerCmHandle);
index 9e4737f..0f40906 100644 (file)
@@ -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<String, TrustLevel>)
     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))
     }
index b547da7..a13f595 100644 (file)
 
 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 ->
index a11f148..8698136 100644 (file)
 
 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<ILoggingEvent>)
+
+    @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]
+    }
 }
index 00d14cd..df2ad71 100644 (file)
@@ -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'() {