Fix Modelloader failure when migration to new model is enabled 28/142328/1
authorToineSiebelink <toine.siebelink@est.tech>
Thu, 30 Oct 2025 12:52:58 +0000 (12:52 +0000)
committerToineSiebelink <toine.siebelink@est.tech>
Thu, 30 Oct 2025 15:00:41 +0000 (15:00 +0000)
- Ensure database not found exception is handled correctly when check for anchor exist
- Consistent logging for all model loaders: starting with 'Model Loader#'  (for easy tracing/demo)
- Simplified names: remove 'coordinator' part in final model loader and associated lock
- Cleaned up slogans of affect test class (no expectations is slogans!)

Issue-ID: CPS-3030
Change-Id: If37fe22b22916df06e12892af82961364ff8eb86
Signed-off-by: ToineSiebelink <toine.siebelink@est.tech>
12 files changed:
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/init/CmDataSubscriptionModelLoader.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/init/InventoryModelLoader.java
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/init/CmDataSubscriptionModelLoaderSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/init/InventoryModelLoaderSpec.groovy
cps-service/src/main/java/org/onap/cps/init/AbstractModelLoader.java
cps-service/src/main/java/org/onap/cps/init/CpsNotificationSubscriptionModelLoader.java
cps-service/src/main/java/org/onap/cps/init/ModelLoaderLock.java [moved from cps-service/src/main/java/org/onap/cps/init/ModelLoaderCoordinatorLock.java with 97% similarity]
cps-service/src/main/java/org/onap/cps/init/ModelLoadersCompletedCheck.java [moved from cps-service/src/main/java/org/onap/cps/init/ModelLoaderCoordinatorEnd.java with 64% similarity]
cps-service/src/test/groovy/org/onap/cps/init/AbstractModelLoaderSpec.groovy
cps-service/src/test/groovy/org/onap/cps/init/CpsNotificationSubscriptionModelLoaderSpec.groovy
cps-service/src/test/groovy/org/onap/cps/init/ModelLoaderLockSpec.groovy [moved from cps-service/src/test/groovy/org/onap/cps/init/ModelLoaderCoordinatorLockSpec.groovy with 90% similarity]
cps-service/src/test/groovy/org/onap/cps/init/ModelLoadersCompletedCheckSpec.groovy [moved from cps-service/src/test/groovy/org/onap/cps/init/ModelLoaderCoordinatorEndSpec.groovy with 72% similarity]

index 893d94e..2991e77 100644 (file)
@@ -28,7 +28,7 @@ import org.onap.cps.api.CpsDataService;
 import org.onap.cps.api.CpsDataspaceService;
 import org.onap.cps.api.CpsModuleService;
 import org.onap.cps.init.AbstractModelLoader;
-import org.onap.cps.init.ModelLoaderCoordinatorLock;
+import org.onap.cps.init.ModelLoaderLock;
 import org.onap.cps.init.actuator.ReadinessManager;
 import org.springframework.core.annotation.Order;
 import org.springframework.stereotype.Service;
@@ -43,13 +43,13 @@ public class CmDataSubscriptionModelLoader extends AbstractModelLoader {
     private static final String ANCHOR_NAME = "cm-data-job-subscriptions";
     private static final String REGISTRY_DATA_NODE_NAME = "dataJob";
 
-    public CmDataSubscriptionModelLoader(final ModelLoaderCoordinatorLock modelLoaderCoordinatorLock,
+    public CmDataSubscriptionModelLoader(final ModelLoaderLock modelLoaderLock,
                                          final CpsDataspaceService cpsDataspaceService,
                                          final CpsModuleService cpsModuleService,
                                          final CpsAnchorService cpsAnchorService,
                                          final CpsDataService cpsDataService,
                                          final ReadinessManager readinessManager) {
-        super(modelLoaderCoordinatorLock, cpsDataspaceService, cpsModuleService, cpsAnchorService, cpsDataService,
+        super(modelLoaderLock, cpsDataspaceService, cpsModuleService, cpsAnchorService, cpsDataService,
             readinessManager);
     }
 
@@ -68,7 +68,7 @@ public class CmDataSubscriptionModelLoader extends AbstractModelLoader {
         createSchemaSet(NCMP_DATASPACE_NAME, SCHEMA_SET_NAME, MODEL_FILE_NAME);
         createAnchor(NCMP_DATASPACE_NAME, SCHEMA_SET_NAME, ANCHOR_NAME);
         createTopLevelDataNode(NCMP_DATASPACE_NAME, ANCHOR_NAME, REGISTRY_DATA_NODE_NAME);
-        log.info("NCMP CM Data Notification Subscription Models onboarded successfully");
+        log.info("Model Loader #3: NCMP CM Data Notification Subscription Models onboarded successfully");
     }
 
 }
index b64612b..f20c1c5 100644 (file)
@@ -30,7 +30,7 @@ import org.onap.cps.api.CpsDataService;
 import org.onap.cps.api.CpsDataspaceService;
 import org.onap.cps.api.CpsModuleService;
 import org.onap.cps.init.AbstractModelLoader;
-import org.onap.cps.init.ModelLoaderCoordinatorLock;
+import org.onap.cps.init.ModelLoaderLock;
 import org.onap.cps.init.actuator.ReadinessManager;
 import org.onap.cps.ncmp.utils.events.NcmpInventoryModelOnboardingFinishedEvent;
 import org.springframework.beans.factory.annotation.Value;
@@ -56,14 +56,14 @@ public class InventoryModelLoader extends AbstractModelLoader {
      * Creates a new {@code InventoryModelLoader} instance responsible for onboarding or upgrading
      * the NCMP inventory model schema sets and managing readiness state during migration.
      */
-    public InventoryModelLoader(final ModelLoaderCoordinatorLock modelLoaderCoordinatorLock,
+    public InventoryModelLoader(final ModelLoaderLock modelLoaderLock,
                                 final CpsDataspaceService cpsDataspaceService,
                                 final CpsModuleService cpsModuleService,
                                 final CpsAnchorService cpsAnchorService,
                                 final CpsDataService cpsDataService,
                                 final ApplicationEventPublisher applicationEventPublisher,
                                 final ReadinessManager readinessManager) {
-        super(modelLoaderCoordinatorLock, cpsDataspaceService, cpsModuleService, cpsAnchorService, cpsDataService,
+        super(modelLoaderLock, cpsDataspaceService, cpsModuleService, cpsAnchorService, cpsDataService,
             readinessManager);
         this.applicationEventPublisher = applicationEventPublisher;
     }
@@ -78,10 +78,12 @@ public class InventoryModelLoader extends AbstractModelLoader {
 
             if (isModuleRevisionInstalled(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, INVENTORY_YANG_MODULE_NAME,
                 moduleRevision)) {
-                log.info("Revision {} is already installed.", moduleRevision);
+                log.info("Model Loader #2: Revision {} is already installed.", moduleRevision);
             } else if (newRevisionEnabled && doesAnchorExist(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR)) {
+                log.info("Model Loader #2: Upgrading already installed inventory to revision {}.", moduleRevision);
                 upgradeAndMigrateInventoryModel();
             } else {
+                log.info("Model Loader #2: New installation using inventory model revision {}.", moduleRevision);
                 installInventoryModel(schemaToInstall);
             }
             applicationEventPublisher.publishEvent(new NcmpInventoryModelOnboardingFinishedEvent(this));
@@ -99,7 +101,7 @@ public class InventoryModelLoader extends AbstractModelLoader {
         createAnchor(NCMP_DATASPACE_NAME, schemaSetName, NCMP_DMI_REGISTRY_ANCHOR);
         createTopLevelDataNode(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, INVENTORY_YANG_MODULE_NAME);
         deleteOldButNotThePreviousSchemaSets();
-        log.info("Inventory model {} installed successfully,", schemaSetName);
+        log.info("Model Loader #2: Inventory model {} installed successfully,", schemaSetName);
     }
 
     private void deleteOldButNotThePreviousSchemaSets() {
@@ -113,13 +115,14 @@ public class InventoryModelLoader extends AbstractModelLoader {
         createSchemaSet(NCMP_DATASPACE_NAME, NEW_INVENTORY_SCHEMA_SET_NAME, yangFileName);
         cpsAnchorService.updateAnchorSchemaSet(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
                 NEW_INVENTORY_SCHEMA_SET_NAME);
-        log.info("Inventory upgraded successfully to model {}", NEW_INVENTORY_SCHEMA_SET_NAME);
+        log.info("Model Loader #2: Inventory upgraded successfully to model {}", NEW_INVENTORY_SCHEMA_SET_NAME);
     }
 
     private void performInventoryDataMigration() {
+
         //1. Load all the cm handles (in batch)
         //2. Copy the state and known properties
-        log.info("Inventory module data migration is completed successfully.");
+        log.info("Model Loader #2: Inventory module data migration is completed successfully.");
     }
 
     private static String toYangFileName(final String schemaSetName) {
index 425dfb8..da06c17 100644 (file)
@@ -28,7 +28,7 @@ import org.onap.cps.api.CpsDataService
 import org.onap.cps.api.CpsDataspaceService
 import org.onap.cps.api.CpsModuleService
 import org.onap.cps.api.model.Dataspace
-import org.onap.cps.init.ModelLoaderCoordinatorLock
+import org.onap.cps.init.ModelLoaderLock
 import org.onap.cps.init.actuator.ReadinessManager
 import org.slf4j.LoggerFactory
 import org.springframework.context.annotation.AnnotationConfigApplicationContext
@@ -38,13 +38,13 @@ import static org.onap.cps.ncmp.impl.inventory.NcmpPersistence.NCMP_DATASPACE_NA
 
 class CmDataSubscriptionModelLoaderSpec extends Specification {
 
-    def mockModelLoaderCoordinatorLock = Mock(ModelLoaderCoordinatorLock)
+    def mockModelLoaderLock = Mock(ModelLoaderLock)
     def mockCpsDataspaceService = Mock(CpsDataspaceService)
     def mockCpsModuleService = Mock(CpsModuleService)
     def mockCpsDataService = Mock(CpsDataService)
     def mockCpsAnchorService = Mock(CpsAnchorService)
     def mockReadinessManager = Mock(ReadinessManager)
-    def objectUnderTest = new CmDataSubscriptionModelLoader(mockModelLoaderCoordinatorLock, mockCpsDataspaceService, mockCpsModuleService, mockCpsAnchorService, mockCpsDataService, mockReadinessManager)
+    def objectUnderTest = new CmDataSubscriptionModelLoader(mockModelLoaderLock, mockCpsDataspaceService, mockCpsModuleService, mockCpsAnchorService, mockCpsDataService, mockReadinessManager)
 
     def applicationContext = new AnnotationConfigApplicationContext()
 
index b000099..092ef2c 100644 (file)
@@ -30,7 +30,7 @@ import org.onap.cps.api.CpsModuleService
 import org.onap.cps.api.exceptions.AnchorNotFoundException
 import org.onap.cps.api.model.Dataspace
 import org.onap.cps.api.model.ModuleDefinition
-import org.onap.cps.init.ModelLoaderCoordinatorLock
+import org.onap.cps.init.ModelLoaderLock
 import org.onap.cps.init.actuator.ReadinessManager
 import org.slf4j.LoggerFactory
 import org.springframework.boot.context.event.ApplicationReadyEvent
@@ -43,14 +43,14 @@ import static org.onap.cps.ncmp.impl.inventory.NcmpPersistence.NCMP_DMI_REGISTRY
 
 class InventoryModelLoaderSpec extends Specification {
 
-    def mockModelLoaderCoordinatorLock = Mock(ModelLoaderCoordinatorLock)
+    def mockModelLoaderLock = Mock(ModelLoaderLock)
     def mockCpsAdminService = Mock(CpsDataspaceService)
     def mockCpsModuleService = Mock(CpsModuleService)
     def mockCpsDataService = Mock(CpsDataService)
     def mockCpsAnchorService = Mock(CpsAnchorService)
     def mockApplicationEventPublisher = Mock(ApplicationEventPublisher)
     def mockReadinessManager = Mock(ReadinessManager)
-    def objectUnderTest = new InventoryModelLoader(mockModelLoaderCoordinatorLock, mockCpsAdminService, mockCpsModuleService, mockCpsAnchorService, mockCpsDataService, mockApplicationEventPublisher, mockReadinessManager)
+    def objectUnderTest = new InventoryModelLoader(mockModelLoaderLock, mockCpsAdminService, mockCpsModuleService, mockCpsAnchorService, mockCpsDataService, mockApplicationEventPublisher, mockReadinessManager)
 
     def applicationContext = new AnnotationConfigApplicationContext()
 
index 47f4701..b2391b9 100644 (file)
@@ -36,6 +36,7 @@ import org.onap.cps.api.CpsDataspaceService;
 import org.onap.cps.api.CpsModuleService;
 import org.onap.cps.api.exceptions.AlreadyDefinedException;
 import org.onap.cps.api.exceptions.AnchorNotFoundException;
+import org.onap.cps.api.exceptions.DataspaceNotFoundException;
 import org.onap.cps.api.exceptions.DuplicatedYangResourceException;
 import org.onap.cps.api.exceptions.ModelOnboardingException;
 import org.onap.cps.api.model.ModuleDefinition;
@@ -49,7 +50,7 @@ import org.springframework.boot.context.event.ApplicationReadyEvent;
 @RequiredArgsConstructor
 public abstract class AbstractModelLoader implements ModelLoader {
 
-    protected final ModelLoaderCoordinatorLock modelLoaderCoordinatorLock;
+    protected final ModelLoaderLock modelLoaderLock;
     protected final CpsDataspaceService cpsDataspaceService;
     private final CpsModuleService cpsModuleService;
     protected final CpsAnchorService cpsAnchorService;
@@ -133,12 +134,15 @@ public abstract class AbstractModelLoader implements ModelLoader {
      *
      * @param dataspaceName the name of the dataspace
      * @param anchorName    the name of the anchor within the dataspace
-     * @return {@code true} if the anchor exists, {@code false} otherwise
+     * @return {@code true} if the dataspace and anchor exists, {@code false} otherwise
      */
     public boolean doesAnchorExist(final String dataspaceName, final String anchorName) {
         try {
             cpsAnchorService.getAnchor(dataspaceName, anchorName);
             return true;
+        } catch (final DataspaceNotFoundException dataspaceNotFoundException) {
+            log.debug("Dataspace '{}' does not exist", dataspaceName);
+            return false;
         } catch (final AnchorNotFoundException anchorNotFoundException) {
             log.debug("Anchor '{}' not found in dataspace '{}'", anchorName, dataspaceName);
             return false;
@@ -223,7 +227,7 @@ public abstract class AbstractModelLoader implements ModelLoader {
     }
 
     void checkIfThisInstanceIsMaster() {
-        isMaster = isMaster || modelLoaderCoordinatorLock.tryLock();
+        isMaster = isMaster || modelLoaderLock.tryLock();
         if (isMaster) {
             log.info("This instance is model loader master");
         } else {
index a9f2e82..d2c51c6 100644 (file)
@@ -41,13 +41,13 @@ public class CpsNotificationSubscriptionModelLoader extends AbstractModelLoader
     private static final String CPS_DATASPACE_NAME = "CPS-Admin";
     private static final String REGISTRY_DATANODE_NAME = "dataspaces";
 
-    public CpsNotificationSubscriptionModelLoader(final ModelLoaderCoordinatorLock modelLoaderCoordinatorLock,
+    public CpsNotificationSubscriptionModelLoader(final ModelLoaderLock modelLoaderLock,
                                                   final CpsDataspaceService cpsDataspaceService,
                                                   final CpsModuleService cpsModuleService,
                                                   final CpsAnchorService cpsAnchorService,
                                                   final CpsDataService cpsDataService,
                                                   final ReadinessManager readinessManager) {
-        super(modelLoaderCoordinatorLock, cpsDataspaceService, cpsModuleService, cpsAnchorService, cpsDataService,
+        super(modelLoaderLock, cpsDataspaceService, cpsModuleService, cpsAnchorService, cpsDataService,
             readinessManager);
     }
 
@@ -67,7 +67,7 @@ public class CpsNotificationSubscriptionModelLoader extends AbstractModelLoader
         createSchemaSet(CPS_DATASPACE_NAME, SCHEMA_SET_NAME, MODEL_FILENAME);
         createAnchor(CPS_DATASPACE_NAME, SCHEMA_SET_NAME, ANCHOR_NAME);
         createTopLevelDataNode(CPS_DATASPACE_NAME, ANCHOR_NAME, REGISTRY_DATANODE_NAME);
-        log.info("CPS Data Notification Subscription models onboarded successfully");
+        log.info("Model Loader #1: CPS Data Notification Subscription models onboarded successfully");
     }
 
 }
@@ -29,7 +29,7 @@ import org.springframework.stereotype.Service;
 @Slf4j
 @Service
 @RequiredArgsConstructor
-public class ModelLoaderCoordinatorLock {
+public class ModelLoaderLock {
 
     @Qualifier("cpsCommonLocks")
     private final IMap<String, String> cpsCommonLocks;
@@ -35,14 +35,14 @@ import org.springframework.stereotype.Service;
 @Slf4j
 @Service
 @Order(LOWEST_PRECEDENCE)
-public class ModelLoaderCoordinatorEnd extends AbstractModelLoader {
+public class ModelLoadersCompletedCheck extends AbstractModelLoader {
 
     final Sleeper sleeper;
 
     /**
      * Constructor.
      *
-     * @param modelLoaderCoordinatorLock the modelLoaderCoordinatorLock
+     * @param modelLoaderLock the modelLoaderCoordinatorLock
      * @param cpsDataspaceService the cpsDataspaceService
      * @param cpsModuleService the cpsModuleService
      * @param cpsAnchorService the cpsAnchorService
@@ -50,39 +50,39 @@ public class ModelLoaderCoordinatorEnd extends AbstractModelLoader {
      * @param readinessManager the readinessManager
      * @param sleeper the sleeper
      */
-    public ModelLoaderCoordinatorEnd(final ModelLoaderCoordinatorLock modelLoaderCoordinatorLock,
-                                     final CpsDataspaceService cpsDataspaceService,
-                                     final CpsModuleService cpsModuleService,
-                                     final CpsAnchorService cpsAnchorService,
-                                     final CpsDataService cpsDataService,
-                                     final ReadinessManager readinessManager,
-                                     final Sleeper sleeper) {
-        super(modelLoaderCoordinatorLock, cpsDataspaceService, cpsModuleService, cpsAnchorService, cpsDataService,
+    public ModelLoadersCompletedCheck(final ModelLoaderLock modelLoaderLock,
+                                      final CpsDataspaceService cpsDataspaceService,
+                                      final CpsModuleService cpsModuleService,
+                                      final CpsAnchorService cpsAnchorService,
+                                      final CpsDataService cpsDataService,
+                                      final ReadinessManager readinessManager,
+                                      final Sleeper sleeper) {
+        super(modelLoaderLock, cpsDataspaceService, cpsModuleService, cpsAnchorService, cpsDataService,
             readinessManager);
         this.sleeper = sleeper;
     }
 
     @Override
     public void onboardOrUpgradeModel() {
-        log.info("Model Loader #999 Started: Coordinator End, check model loaders are completed");
+        log.info("Model Loader #LAST Started: Completion Check");
         if (isMaster) {
             releaseLock();
-            log.info("This instance is model loader master. Model loading completed");
+            log.info("Model Loader #LAST: This instance is model loader master. Model loading completed");
         } else {
-            log.info("Wait for model loader master to finish");
+            log.info("Model Loader #LAST: Wait for model loading on master to finish");
             waitForMasterToFinish();
         }
-        log.info("Model Loader #999 Completed");
+        log.info("Model Loader #LAST Completed");
     }
 
     private void releaseLock() {
-        modelLoaderCoordinatorLock.forceUnlock();
-        log.info("Model loading (on master) finished");
+        modelLoaderLock.forceUnlock();
+        log.info("Model Loader #LAST: Model loading (on master) finished");
     }
 
     private void waitForMasterToFinish() {
-        while (modelLoaderCoordinatorLock.isLocked()) {
-            log.info("Still waiting for model loader master to finish");
+        while (modelLoaderLock.isLocked()) {
+            log.info("Model Loader #LAST: Still waiting for model loading on master to finish");
             try {
                 sleeper.haveALittleRest(100);
             } catch (final InterruptedException e) {
index 1620b1a..f228262 100644 (file)
@@ -30,6 +30,7 @@ import org.onap.cps.api.CpsDataspaceService
 import org.onap.cps.api.CpsModuleService
 import org.onap.cps.api.exceptions.AlreadyDefinedException
 import org.onap.cps.api.exceptions.AnchorNotFoundException
+import org.onap.cps.api.exceptions.DataspaceNotFoundException
 import org.onap.cps.api.exceptions.DuplicatedYangResourceException
 import org.onap.cps.api.exceptions.ModelOnboardingException
 import org.onap.cps.api.model.ModuleDefinition
@@ -43,13 +44,13 @@ import spock.lang.Specification
 
 class AbstractModelLoaderSpec extends Specification {
 
-    def mockModelLoaderCoordinatorLock = Mock(ModelLoaderCoordinatorLock)
+    def mockModelLoaderLock = Mock(ModelLoaderLock)
     def mockCpsDataspaceService = Mock(CpsDataspaceService)
     def mockCpsModuleService = Mock(CpsModuleService)
     def mockCpsDataService = Mock(CpsDataService)
     def mockCpsAnchorService = Mock(CpsAnchorService)
     def mockReadinessManager = Mock(ReadinessManager)
-    def objectUnderTest = Spy(new TestModelLoader(mockModelLoaderCoordinatorLock, mockCpsDataspaceService, mockCpsModuleService, mockCpsAnchorService, mockCpsDataService, mockReadinessManager))
+    def objectUnderTest = Spy(new TestModelLoader(mockModelLoaderLock, mockCpsDataspaceService, mockCpsModuleService, mockCpsAnchorService, mockCpsDataService, mockReadinessManager))
 
     def applicationContext = new AnnotationConfigApplicationContext()
 
@@ -70,14 +71,14 @@ class AbstractModelLoaderSpec extends Specification {
         loggingListAppender.stop()
     }
 
-    def 'Application ready event triggers onboarding/upgrade'() {
-        when: 'Application (ready) event is triggered'
+    def 'Application ready.'() {
+        when: 'Application ready event is triggered'
             objectUnderTest.onApplicationEvent(Mock(ApplicationReadyEvent))
         then: 'the onboard/upgrade method is executed'
             1 * objectUnderTest.onboardOrUpgradeModel()
     }
 
-    def 'Application ready event handles startup exception'() {
+    def 'Application ready event with exception.'() {
         given: 'a startup exception is thrown during model onboarding'
             objectUnderTest.onboardOrUpgradeModel() >> { throw new ModelOnboardingException('test message','details are not logged') }
         when: 'Application (ready) event is triggered'
@@ -87,14 +88,14 @@ class AbstractModelLoaderSpec extends Specification {
             assert logs.contains('test message')
     }
 
-    def 'Creating a dataspace delegates to the service.'() {
+    def 'Creating a dataspace.'() {
         when: 'creating a dataspace'
             objectUnderTest.createDataspace('some dataspace')
         then: 'the operation is delegated to the dataspace service'
             1 * mockCpsDataspaceService.createDataspace('some dataspace')
     }
 
-    def 'Creating a dataspace handles already defined exception.'() {
+    def 'Creating a dataspace that already exists.'() {
         given: 'dataspace service throws an already defined exception'
             mockCpsDataspaceService.createDataspace(*_) >> { throw AlreadyDefinedException.forDataNodes([], 'some context') }
         when: 'creating a dataspace'
@@ -105,7 +106,7 @@ class AbstractModelLoaderSpec extends Specification {
             assert logContains('Dataspace already exists')
     }
 
-    def 'Creating a dataspace handles other exception.'() {
+    def 'Attempt to create a dataspace with other exception.'() {
         given: 'dataspace service throws a runtime exception'
             mockCpsDataspaceService.createDataspace(*_) >> { throw new RuntimeException('test message')  }
         when: 'creating a dataspace'
@@ -116,14 +117,14 @@ class AbstractModelLoaderSpec extends Specification {
             assert thrown.details.contains('test message')
     }
 
-    def 'Creating a schema set delegates to the service.'() {
+    def 'Creating a schema set.'() {
         when: 'creating a schema set'
             objectUnderTest.createSchemaSet('some dataspace','new name','cps-notification-subscriptions@2024-07-03.yang')
         then: 'the operation is delegated to the module service'
             1 * mockCpsModuleService.createSchemaSet('some dataspace','new name',_)
     }
 
-    def 'Creating a schema set handles duplicated yang resource exception'() {
+    def 'Creating a schema set with duplicated yang resource.'() {
         given: 'module service throws duplicated yang resource exception'
             mockCpsModuleService.createSchemaSet(*_) >> { throw new DuplicatedYangResourceException('my-yang-resource', 'my-yang-resource-checksum', null) }
         when: 'attempt to create a schema set'
@@ -133,7 +134,7 @@ class AbstractModelLoaderSpec extends Specification {
             assert logContains('Ignoring yang resource duplication exception. Assuming model was created by another instance')
     }
 
-    def 'Creating a schema set handles already defined exception.'() {
+    def 'Creating a schema that already exists.'() {
         given: 'the module service throws an already defined exception'
             mockCpsModuleService.createSchemaSet(*_) >>  { throw AlreadyDefinedException.forSchemaSet('name','context',null) }
         when: 'attempt to create a schema set'
@@ -143,7 +144,7 @@ class AbstractModelLoaderSpec extends Specification {
             assert logContains('Creating new schema set failed as schema set already exists')
     }
 
-    def 'Creating a schema set from a non-existing YANG file.'() {
+    def 'Attempt to creating a schema set from a non-existing YANG file.'() {
         when: 'attempting to create a schema set from a non-existing file'
             objectUnderTest.createSchemaSet('some dataspace','some name','no such yang file')
         then: 'a startup exception with correct message and details is thrown'
@@ -152,14 +153,14 @@ class AbstractModelLoaderSpec extends Specification {
             assert thrown.details.contains('unable to read file')
     }
 
-    def 'Creating an anchor delegates to the service.'() {
+    def 'Creating an anchor.'() {
         when: 'creating an anchor'
             objectUnderTest.createAnchor('some dataspace','some schema set','new name')
         then: 'the operation is delegated to the anchor service'
             1 * mockCpsAnchorService.createAnchor('some dataspace','some schema set', 'new name')
     }
 
-    def 'Creating an anchor handles already defined exception.'() {
+    def 'Creating an anchor that already exists.'() {
         given: 'the anchor service throws an already defined exception'
             mockCpsAnchorService.createAnchor(*_)>>  { throw AlreadyDefinedException.forAnchor('name','context',null) }
         when: 'attempting to create an anchor'
@@ -169,7 +170,7 @@ class AbstractModelLoaderSpec extends Specification {
             assert logContains('Creating new anchor failed as anchor already exists')
     }
 
-    def 'Creating an anchor handles other exceptions.'() {
+    def 'Attempt to create an anchor with other exception.'() {
         given: 'the anchor service throws a runtime exception'
             mockCpsAnchorService.createAnchor(*_)>>  { throw new RuntimeException('test message') }
         when: 'attempt to create anchor'
@@ -180,14 +181,14 @@ class AbstractModelLoaderSpec extends Specification {
             assert thrown.details.contains('test message')
     }
 
-    def 'Creating a top-level data node delegates to the service.'() {
+    def 'Creating a top-level data node.'() {
         when: 'top-level node is created'
             objectUnderTest.createTopLevelDataNode('dataspace','anchor','new node')
         then: 'the correct JSON is saved using the data service'
             1 * mockCpsDataService.saveData('dataspace','anchor', '{"new node":{}}',_)
     }
 
-    def 'Creating a top-level node handles already defined exception.'() {
+    def 'Creating a top-level node that already exists.'() {
         given: 'the data service throws an Already Defined exception'
             mockCpsDataService.saveData(*_) >> { throw AlreadyDefinedException.forDataNodes([], 'some context') }
         when: 'attempting to create a top-level node'
@@ -197,7 +198,7 @@ class AbstractModelLoaderSpec extends Specification {
             assert logContains('failed as data node already exists')
     }
 
-    def 'Create a top-level node with any other exception.'() {
+    def 'Attempt to create a top-level node with other exception.'() {
         given: 'the data service throws a runtime exception'
             mockCpsDataService.saveData(*_) >> { throw new RuntimeException('test message') }
         when: 'attempt to create a top-level node'
@@ -208,7 +209,7 @@ class AbstractModelLoaderSpec extends Specification {
             assert thrown.details.contains('test message')
     }
 
-    def 'Delete unused schema sets delegates to the service.'() {
+    def 'Delete unused schema sets.'() {
         when: 'unused schema sets get deleted'
             objectUnderTest.deleteUnusedSchemaSets('some dataspace','schema set 1', 'schema set 2')
         then: 'a request to delete each (without cascade) is delegated to the module service'
@@ -247,7 +248,7 @@ class AbstractModelLoaderSpec extends Specification {
             assert thrown.details.contains('test message')
     }
 
-    def 'Checking if an anchor exists'() {
+    def 'Checking if an anchor exists.'() {
         given: 'the anchor service returns an anchor without throwing an exception'
             mockCpsAnchorService.getAnchor('my-dataspace', 'my-anchor') >> {}
         when: 'checking if the anchor exists'
@@ -256,8 +257,8 @@ class AbstractModelLoaderSpec extends Specification {
             assert result == true
     }
 
-    def 'Checking if an anchor exists with unknown anchor'() {
-        given: 'the anchor service throws an exception'
+    def 'Checking if an anchor exists with unknown anchor.'() {
+        given: 'the anchor service throws an anchor not found exception'
             def anchorNotFoundException = new AnchorNotFoundException('my-dataspace', 'missing-anchor')
             mockCpsAnchorService.getAnchor('my-dataspace', 'missing-anchor') >> {throw anchorNotFoundException}
         when: 'checking if the anchor exists'
@@ -266,7 +267,17 @@ class AbstractModelLoaderSpec extends Specification {
             assert result == false
     }
 
-    def 'Checking if module revision is installed when: #scenario'() {
+    def 'Checking if an anchor exists with unknown dataspace.'() {
+        given: 'the anchor service throws a dataspace not fond exception'
+            def dataspaceNotFoundException = new DataspaceNotFoundException ('missing-dataspace')
+            mockCpsAnchorService.getAnchor('missing-dataspace', 'some anchor') >> {throw dataspaceNotFoundException}
+        when: 'checking if the anchor exists'
+            def result = objectUnderTest.doesAnchorExist('missing-dataspace', 'some anchor')
+        then: 'the expected boolean value is returned'
+            assert result == false
+    }
+
+    def 'Checking if module revision is installed when: #scenario.'() {
         given: 'the module service returns module definitions'
             mockCpsModuleService.getModuleDefinitionsByAnchorAndModule('some-dataspace', 'some-anchor', 'some-module', 'my-revision') >> moduleDefinitions
         when: 'checking if a module revision is installed'
@@ -281,7 +292,7 @@ class AbstractModelLoaderSpec extends Specification {
 
     def 'Check if this instance is master when: #scenario.'() {
         given: 'the lock acquisition returns #lockAcquired'
-            mockModelLoaderCoordinatorLock.tryLock() >> lockAcquired
+            mockModelLoaderLock.tryLock() >> lockAcquired
         when: 'checking if this instance is master'
             objectUnderTest.checkIfThisInstanceIsMaster()
         then: 'the master status is set correctly'
@@ -302,7 +313,7 @@ class AbstractModelLoaderSpec extends Specification {
         then: 'instance remains remains master'
             assert objectUnderTest.@isMaster == true
         and: 'no attempt is made to lock'
-            0 * mockModelLoaderCoordinatorLock.tryLock()
+            0 * mockModelLoaderLock.tryLock()
         and: 'log reports this instance is master'
             assert logContains('This instance is model loader master')
     }
@@ -313,13 +324,13 @@ class AbstractModelLoaderSpec extends Specification {
 
     class TestModelLoader extends AbstractModelLoader {
 
-        TestModelLoader(final ModelLoaderCoordinatorLock modelLoaderCoordinatorLock,
+        TestModelLoader(final ModelLoaderLock modelLoaderLock,
                         final CpsDataspaceService cpsDataspaceService,
                         final CpsModuleService cpsModuleService,
                         final CpsAnchorService cpsAnchorService,
                         final CpsDataService cpsDataService,
                         final ReadinessManager readinessManager) {
-            super(modelLoaderCoordinatorLock, cpsDataspaceService, cpsModuleService, cpsAnchorService, cpsDataService, readinessManager)
+            super(modelLoaderLock, cpsDataspaceService, cpsModuleService, cpsAnchorService, cpsDataService, readinessManager)
         }
 
         @Override
index 75c23c0..01d4bcc 100644 (file)
@@ -40,8 +40,8 @@ class CpsNotificationSubscriptionModelLoaderSpec extends Specification {
     def mockCpsDataService = Mock(CpsDataService)
     def mockCpsAnchorService = Mock(CpsAnchorService)
     def mockReadinessManager = Mock(ReadinessManager)
-    def mockModelLoaderCoordinatorLock = Mock(ModelLoaderCoordinatorLock)
-    def objectUnderTest = new CpsNotificationSubscriptionModelLoader(mockModelLoaderCoordinatorLock, mockCpsDataspaceService, mockCpsModuleService, mockCpsAnchorService, mockCpsDataService, mockReadinessManager)
+    def mockModelLoaderLock = Mock(ModelLoaderLock)
+    def objectUnderTest = new CpsNotificationSubscriptionModelLoader(mockModelLoaderLock, mockCpsDataspaceService, mockCpsModuleService, mockCpsAnchorService, mockCpsDataService, mockReadinessManager)
 
     def applicationContext = new AnnotationConfigApplicationContext()
 
@@ -25,16 +25,16 @@ import com.hazelcast.core.Hazelcast
 import com.hazelcast.instance.impl.HazelcastInstanceFactory
 import spock.lang.Specification
 
-class ModelLoaderCoordinatorLockSpec extends Specification {
+class ModelLoaderLockSpec extends Specification {
     def cpsCommonLocks = HazelcastInstanceFactory.getOrCreateHazelcastInstance(new Config('hazelcastInstanceName')).getMap('cpsCommonLocks')
 
-    def objectUnderTest = new ModelLoaderCoordinatorLock(cpsCommonLocks)
+    def objectUnderTest = new ModelLoaderLock(cpsCommonLocks)
 
     def cleanupSpec() {
         Hazelcast.getHazelcastInstanceByName('hazelcastInstanceName').shutdown()
     }
 
-    def 'Locking and unlocking the coordinator lock.'() {
+    def 'Locking and unlocking the model loader lock.'() {
         when: 'try to get a lock'
             assert objectUnderTest.tryLock() == true
         then: 'the lock is acquired'
@@ -24,41 +24,41 @@ import org.onap.cps.init.actuator.ReadinessManager
 import org.onap.cps.utils.Sleeper
 import spock.lang.Specification
 
-class ModelLoaderCoordinatorEndSpec extends Specification {
+class ModelLoadersCompletedCheckSpec extends Specification {
 
-    def mockModelLoaderCoordinatorLock = Mock(ModelLoaderCoordinatorLock)
+    def mockModelLoaderLock = Mock(ModelLoaderLock)
     def spiedSleeper = Spy(new Sleeper())
     def mockReadinessManager = Mock(ReadinessManager)
 
-    def objectUnderTest = new ModelLoaderCoordinatorEnd(mockModelLoaderCoordinatorLock, null, null, null, null, mockReadinessManager, spiedSleeper)
+    def objectUnderTest = new ModelLoadersCompletedCheck(mockModelLoaderLock, null, null, null, null, mockReadinessManager, spiedSleeper)
 
-    def 'Model Loader Coordinator End for master instance.'() {
+    def 'Model Loaders Completed Check for master instance.'() {
         given: 'instance is master'
             objectUnderTest.isMaster = true
         when: 'model loader is started'
             objectUnderTest.onboardOrUpgradeModel()
-        then: 'the model loader coordinator lock is released'
-            1 * mockModelLoaderCoordinatorLock.forceUnlock()
+        then: 'the model loader lock is released'
+            1 * mockModelLoaderLock.forceUnlock()
         and: 'no checks for locked are made'
-            0 * mockModelLoaderCoordinatorLock.isLocked()
+            0 * mockModelLoaderLock.isLocked()
     }
 
-    def 'Model Loader Coordinator End for non-master instance.'() {
+    def 'Model Loaders Completed Check for non-master instance.'() {
         given: 'instance is NOT master'
             objectUnderTest.isMaster = false
         and: 'the lock will be unlocked upon the third time checking (so expect 3 calls)'
-            3 * mockModelLoaderCoordinatorLock.isLocked() >>> [ true, true, false ]
+            3 * mockModelLoaderLock.isLocked() >>> [true, true, false ]
         when: 'model loader is started'
             objectUnderTest.onboardOrUpgradeModel()
         then: 'the system sleeps twice'
             2 * spiedSleeper.haveALittleRest(_)
     }
 
-    def 'Model Loader Coordinator End for non-master with exception during sleep.'() {
+    def 'Model Loaders Completed Check for non-master with exception during sleep.'() {
         given: 'instance is NOT master'
             objectUnderTest.isMaster = false
         and: 'attempting the get the lock will succeed on the second attempt'
-            mockModelLoaderCoordinatorLock.isLocked() >>> [ true, false ]
+            mockModelLoaderLock.isLocked() >>> [true, false ]
         when: 'model loader is started'
             objectUnderTest.onboardOrUpgradeModel()
         then: 'the system sleeps once (but is interrupted)'