Fix sync messages in migration in delete scenario 44/141844/1
authorFrancescoFioraEst <francesco.fiora@est.tech>
Tue, 12 Aug 2025 14:28:18 +0000 (15:28 +0100)
committerFrancesco Fiora <francesco.fiora@est.tech>
Mon, 18 Aug 2025 09:07:00 +0000 (09:07 +0000)
Issue-ID: POLICY-5447
Change-Id: I0a7905cf0b1ff113d8c1a13ae67b823aa5838a35
Signed-off-by: FrancescoFioraEst <francesco.fiora@est.tech>
participant/participant-impl/participant-impl-simulator/src/main/java/org/onap/policy/clamp/acm/participant/sim/main/handler/AutomationCompositionElementHandler.java
participant/participant-impl/participant-impl-simulator/src/main/java/org/onap/policy/clamp/acm/participant/sim/main/handler/SimulatorService.java
participant/participant-impl/participant-impl-simulator/src/test/java/org/onap/policy/clamp/acm/participant/sim/main/handler/AutomationCompositionElementHandlerTest.java
participant/participant-impl/participant-impl-simulator/src/test/java/org/onap/policy/clamp/acm/participant/sim/main/handler/SimulatorServiceTest.java
participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/AutomationCompositionOutHandler.java
participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/MsgExecutor.java
participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/cache/CacheProvider.java
participant/participant-intermediary/src/test/java/org/onap/policy/clamp/acm/participant/intermediary/handler/AutomationCompositionHandlerTest.java
participant/participant-intermediary/src/test/java/org/onap/policy/clamp/acm/participant/intermediary/handler/AutomationCompositionOutHandlerTest.java
runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/scanner/SimpleScanner.java
runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/supervision/scanner/SimpleScannerTest.java

index 5e03bc4..26b53f1 100644 (file)
@@ -125,9 +125,7 @@ public class AutomationCompositionElementHandler extends AcElementListenerV4 {
             LOGGER.debug("new element scenario");
         }
         if (ElementState.REMOVED.equals(instanceElementMigrate.state())) {
-            simulatorService.undeploy(instanceElement.instanceId(), instanceElement.elementId(),
-                    instanceElement.outProperties());
-            simulatorService.delete(instanceElement.instanceId(), instanceElement.elementId());
+            simulatorService.deleteInMigration(instanceElement.instanceId(), instanceElement.elementId());
         } else {
             simulatorService.migrate(instanceElementMigrate.instanceId(), instanceElementMigrate.elementId(), stage,
                     compositionElementTarget.inProperties(), instanceElementMigrate.outProperties());
index 20979ad..c1228e9 100644 (file)
@@ -284,6 +284,22 @@ public class SimulatorService {
         }
     }
 
+    /**
+     * Handle deleting an automation composition element in migration.
+     *
+     * @param instanceId the instanceId
+     * @param elementId  the elementId
+     */
+    public void deleteInMigration(UUID instanceId, UUID elementId) {
+        if (getConfig().isMigrateSuccess()) {
+            intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId,
+                    DeployState.DELETED, null, StateChangeResult.NO_ERROR, "Deleted");
+        } else {
+            intermediaryApi.updateAutomationCompositionElementState(instanceId, elementId,
+                    DeployState.UNDEPLOYED, null, StateChangeResult.FAILED, "Delete failed!");
+        }
+    }
+
     /**
      * Handle an update on an automation composition element.
      *
index 4b2ea56..309cf32 100644 (file)
@@ -284,9 +284,6 @@ class AutomationCompositionElementHandlerTest {
             Map.of("key", "value"), Map.of(), ElementState.REMOVED);
         acElementHandler
             .migrate(COMPOSITION_ELEMENT, compoElTargetRemove, INSTANCE_ELEMENT, inElMigratedRemove, 0);
-        verify(intermediaryApi).updateAutomationCompositionElementState(
-            INSTANCE_ELEMENT.instanceId(), INSTANCE_ELEMENT.elementId(),
-            DeployState.UNDEPLOYED, null, StateChangeResult.NO_ERROR, "Undeployed");
         verify(intermediaryApi).updateAutomationCompositionElementState(
             INSTANCE_ELEMENT.instanceId(), INSTANCE_ELEMENT.elementId(),
             DeployState.DELETED, null, StateChangeResult.NO_ERROR, "Deleted");
index ca92ad1..4562f22 100644 (file)
@@ -37,6 +37,8 @@ import org.onap.policy.clamp.acm.participant.intermediary.api.CompositionDto;
 import org.onap.policy.clamp.acm.participant.intermediary.api.ParticipantIntermediaryApi;
 import org.onap.policy.clamp.acm.participant.sim.comm.CommonTestData;
 import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionElementDefinition;
+import org.onap.policy.clamp.models.acm.concepts.DeployState;
+import org.onap.policy.clamp.models.acm.concepts.StateChangeResult;
 import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier;
 import org.onap.policy.models.tosca.authorative.concepts.ToscaNodeTemplate;
 
@@ -168,4 +170,20 @@ class SimulatorServiceTest {
         testThread.interrupt();
         testThread.join();
     }
+
+    @Test
+    void testDeleteInMigration() {
+        var intermediaryApi = mock(ParticipantIntermediaryApi.class);
+        var simulatorService = new SimulatorService(intermediaryApi);
+        var instanceId = UUID.randomUUID();
+        var elementId = UUID.randomUUID();
+        simulatorService.deleteInMigration(instanceId, elementId);
+        verify(intermediaryApi).updateAutomationCompositionElementState(instanceId, elementId,
+                DeployState.DELETED, null, StateChangeResult.NO_ERROR, "Deleted");
+
+        simulatorService.getConfig().setMigrateSuccess(false);
+        simulatorService.deleteInMigration(instanceId, elementId);
+        verify(intermediaryApi).updateAutomationCompositionElementState(instanceId, elementId,
+                DeployState.UNDEPLOYED, null, StateChangeResult.FAILED, "Delete failed!");
+    }
 }
index c95bf4c..37e0e22 100644 (file)
@@ -54,6 +54,12 @@ import org.springframework.stereotype.Component;
 @RequiredArgsConstructor
 public class AutomationCompositionOutHandler {
     private static final Logger LOGGER = LoggerFactory.getLogger(AutomationCompositionOutHandler.class);
+    private static final String MSG_NOT_PRESENT
+            = "Cannot update Automation composition element {}, {} id {} not present";
+    private static final String MSG_STATE_CHANGE = "Automation composition element {} state changed to {}";
+    private static final String MSG_AC = "Automation composition";
+    private static final String MSG_AC_ELEMENT = "AC Element";
+    private static final String MSG_STAGE = "stage";
 
     private final ParticipantMessagePublisher publisher;
     private final CacheProvider cacheProvider;
@@ -75,15 +81,13 @@ public class AutomationCompositionOutHandler {
 
         var automationComposition = cacheProvider.getAutomationComposition(instance);
         if (automationComposition == null) {
-            LOGGER.error("Cannot update Automation composition element stage, Automation composition id {} not present",
-                instance);
+            LOGGER.error(MSG_NOT_PRESENT, MSG_STAGE, MSG_AC, instance);
             return;
         }
 
         var element = automationComposition.getElements().get(elementId);
         if (element == null) {
-            var msg = "Cannot update Automation composition element stage, AC Element id {} not present";
-            LOGGER.error(msg, elementId);
+            LOGGER.error(MSG_NOT_PRESENT, MSG_STAGE, MSG_AC_ELEMENT, elementId);
             return;
         }
 
@@ -145,15 +149,13 @@ public class AutomationCompositionOutHandler {
 
         var automationComposition = cacheProvider.getAutomationComposition(instance);
         if (automationComposition == null) {
-            LOGGER.error("Cannot update Automation composition element state, Automation composition id {} not present",
-                instance);
+            LOGGER.error(MSG_NOT_PRESENT, "state", MSG_AC, instance);
             return;
         }
 
         var element = automationComposition.getElements().get(elementId);
         if (element == null) {
-            var msg = "Cannot update Automation composition element state, AC Element id {} not present";
-            LOGGER.error(msg, elementId);
+            checkElement(automationComposition, instance, elementId, deployState, stateChangeResult, message);
             return;
         }
 
@@ -166,23 +168,48 @@ public class AutomationCompositionOutHandler {
             handleLockState(automationComposition, element, lockState, stateChangeResult);
         }
 
-        var automationCompositionStateChangeAck =
-                new AutomationCompositionDeployAck(ParticipantMessageType.AUTOMATION_COMPOSITION_STATECHANGE_ACK);
-        automationCompositionStateChangeAck.setParticipantId(cacheProvider.getParticipantId());
-        automationCompositionStateChangeAck.setReplicaId(cacheProvider.getReplicaId());
-        automationCompositionStateChangeAck.setMessage(AcmUtils.validatedMessage(message));
-        automationCompositionStateChangeAck.setResponseTo(cacheProvider.getMsgIdentification().get(element.getId()));
-        automationCompositionStateChangeAck.setStateChangeResult(stateChangeResult);
-        automationCompositionStateChangeAck.setAutomationCompositionId(instance);
-        automationCompositionStateChangeAck.getAutomationCompositionResultMap().put(element.getId(),
+        var acStateChangeAck = createAutomationCompositionDeployAck();
+        acStateChangeAck.setMessage(AcmUtils.validatedMessage(message));
+        acStateChangeAck.setResponseTo(cacheProvider.getMsgIdentification().get(element.getId()));
+        acStateChangeAck.setStateChangeResult(stateChangeResult);
+        acStateChangeAck.setAutomationCompositionId(instance);
+        acStateChangeAck.getAutomationCompositionResultMap().put(element.getId(),
                 new AcElementDeployAck(element.getDeployState(), element.getLockState(), element.getOperationalState(),
                         element.getUseState(), element.getOutProperties(), true, message));
-        LOGGER.debug("Automation composition element {} state changed to {}", elementId, deployState);
-        automationCompositionStateChangeAck.setResult(true);
-        publisher.sendAutomationCompositionAck(automationCompositionStateChangeAck);
+        LOGGER.debug(MSG_STATE_CHANGE, elementId, deployState);
+        publisher.sendAutomationCompositionAck(acStateChangeAck);
         cacheProvider.getMsgIdentification().remove(element.getId());
     }
 
+    private void checkElement(AutomationComposition automationComposition, UUID instance, UUID elementId,
+            DeployState deployState, StateChangeResult stateChangeResult, String message) {
+        if ((DeployState.MIGRATING.equals(automationComposition.getDeployState())
+                || DeployState.MIGRATION_REVERTING.equals(automationComposition.getDeployState()))) {
+            var acStateChangeAck = createAutomationCompositionDeployAck();
+            acStateChangeAck.setMessage(AcmUtils.validatedMessage(message));
+            acStateChangeAck.setResponseTo(cacheProvider.getMsgIdentification().get(elementId));
+            acStateChangeAck.setStateChangeResult(stateChangeResult);
+            acStateChangeAck.setAutomationCompositionId(instance);
+            acStateChangeAck.getAutomationCompositionResultMap().put(elementId,
+                    new AcElementDeployAck(deployState, null, null,
+                            null, Map.of(), true, message));
+            LOGGER.debug(MSG_STATE_CHANGE, elementId, deployState);
+            publisher.sendAutomationCompositionAck(acStateChangeAck);
+            cacheProvider.getMsgIdentification().remove(elementId);
+        } else {
+            LOGGER.error(MSG_NOT_PRESENT, "state", MSG_AC_ELEMENT, elementId);
+        }
+    }
+
+    private AutomationCompositionDeployAck createAutomationCompositionDeployAck() {
+        var acStateChangeAck =
+                new AutomationCompositionDeployAck(ParticipantMessageType.AUTOMATION_COMPOSITION_STATECHANGE_ACK);
+        acStateChangeAck.setParticipantId(cacheProvider.getParticipantId());
+        acStateChangeAck.setReplicaId(cacheProvider.getReplicaId());
+        acStateChangeAck.setResult(true);
+        return acStateChangeAck;
+    }
+
     private void handleDeployState(AutomationComposition automationComposition, AutomationCompositionElement element,
             DeployState deployState, StateChangeResult stateChangeResult) {
         element.setDeployState(deployState);
@@ -255,15 +282,13 @@ public class AutomationCompositionOutHandler {
 
         var automationComposition = cacheProvider.getAutomationComposition(automationCompositionId);
         if (automationComposition == null) {
-            LOGGER.error("Cannot update Automation composition element state, Automation composition id {} not present",
-                    automationCompositionId);
+            LOGGER.error(MSG_NOT_PRESENT, "outProperites", MSG_AC, automationCompositionId);
             return;
         }
 
         var element = automationComposition.getElements().get(elementId);
         if (element == null) {
-            var msg = "Cannot update Automation composition element state, AC Element id {} not present";
-            LOGGER.error(msg, elementId);
+            LOGGER.error(MSG_NOT_PRESENT, "outProperites", MSG_AC_ELEMENT, elementId);
             return;
         }
         element.setOperationalState(operationalState);
index acd02a4..1ff4aab 100644 (file)
@@ -28,6 +28,8 @@ import org.onap.policy.clamp.acm.participant.intermediary.comm.ParticipantMessag
 import org.onap.policy.clamp.acm.participant.intermediary.handler.cache.AutomationCompositionMsg;
 import org.onap.policy.clamp.acm.participant.intermediary.handler.cache.CacheProvider;
 import org.onap.policy.clamp.models.acm.messages.kafka.participant.ParticipantReqSync;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.stereotype.Component;
 
 @Component
@@ -35,6 +37,7 @@ import org.springframework.stereotype.Component;
 public class MsgExecutor {
 
     private final ExecutorService executor = Context.taskWrapping(Executors.newSingleThreadExecutor());
+    private static final Logger LOGGER = LoggerFactory.getLogger(MsgExecutor.class);
 
     private final CacheProvider cacheProvider;
     private final ParticipantMessagePublisher publisher;
@@ -82,6 +85,7 @@ public class MsgExecutor {
                 message.setCompositionId(null);
                 message.setRevisionIdComposition(null);
             } else {
+                LOGGER.debug("Composition {} missing or outdated", message.getCompositionId());
                 result = false;
             }
         }
@@ -92,6 +96,7 @@ public class MsgExecutor {
                 message.setCompositionTargetId(null);
                 message.setRevisionIdCompositionTarget(null);
             } else {
+                LOGGER.debug("Composition Target {} missing or outdated", message.getCompositionTargetId());
                 result = false;
             }
         }
@@ -101,6 +106,7 @@ public class MsgExecutor {
                 message.setInstanceId(null);
                 message.setRevisionIdInstance(null);
             } else {
+                LOGGER.debug("Instance {} missing or outdated", message.getInstanceId());
                 result = false;
             }
         }
index cd24c3b..3b982e0 100644 (file)
@@ -159,7 +159,11 @@ public class CacheProvider {
      */
     public Map<String, Object> getCommonProperties(@NonNull UUID compositionId,
         @NonNull ToscaConceptIdentifier definition) {
-        var map = acElementsDefinitions.get(compositionId).getElements().get(definition);
+        var acDefinition = acElementsDefinitions.get(compositionId);
+        if (acDefinition == null) {
+            return new HashMap<>();
+        }
+        var map = acDefinition.getElements().get(definition);
         return map != null ? map.getAutomationCompositionElementToscaNodeTemplate().getProperties() : new HashMap<>();
     }
 
@@ -277,7 +281,7 @@ public class CacheProvider {
      */
     public CompositionElementDto createCompositionElementDto(UUID compositionId, AutomationCompositionElement element) {
         var acDefinition = acElementsDefinitions.get(compositionId);
-        var acDefinitionElement = acDefinition.getElements().get(element.getDefinition());
+        var acDefinitionElement = acDefinition != null ? acDefinition.getElements().get(element.getDefinition()) : null;
 
         return (acDefinitionElement != null) ? new CompositionElementDto(compositionId, element.getDefinition(),
                 acDefinitionElement.getAutomationCompositionElementToscaNodeTemplate().getProperties(),
index d6d47c8..e7d6e36 100644 (file)
@@ -77,6 +77,33 @@ class AutomationCompositionHandlerTest {
         assertDoesNotThrow(() -> ach.handleAutomationCompositionStateChange(automationCompositionStateChange));
     }
 
+    @Test
+    void handleAcStateChangeUndeployTest() {
+        var automationComposition = CommonTestData.getTestAutomationCompositionMap().values().iterator().next();
+        automationComposition.setCompositionId(UUID.randomUUID());
+        automationComposition.setInstanceId(UUID.randomUUID());
+        automationComposition.setCompositionTargetId(UUID.randomUUID());
+        var participantDeploy =
+                CommonTestData.createparticipantDeploy(CommonTestData.getParticipantId(), automationComposition);
+
+        var cacheProvider = new CacheProvider(CommonTestData.getParticipantParameters());
+        cacheProvider.initializeAutomationComposition(automationComposition.getCompositionId(),
+                automationComposition.getInstanceId(), participantDeploy, UUID.randomUUID());
+
+        var automationCompositionStateChange = CommonTestData.getStateChange(CommonTestData.getParticipantId(),
+                automationComposition.getInstanceId(), DeployOrder.UNDEPLOY, LockOrder.NONE);
+
+        var participantMessagePublisher = mock(ParticipantMessagePublisher.class);
+        var listener = mock(ThreadHandler.class);
+        var ach = new AutomationCompositionHandler(cacheProvider, participantMessagePublisher, listener);
+        ach.handleAutomationCompositionStateChange(automationCompositionStateChange);
+        automationComposition = cacheProvider.getAutomationComposition(automationComposition.getInstanceId());
+        verify(listener, times(automationComposition.getElements().size())).undeploy(any(), any(), any());
+        for (var element : automationComposition.getElements().values()) {
+            assertEquals(DeployState.UNDEPLOYING, element.getDeployState());
+        }
+    }
+
     @Test
     void handleAutomationCompositionStateChangeUndeployTest() {
         var automationComposition = CommonTestData.getTestAutomationCompositionMap().values().iterator().next();
index 25b5bf9..e65f7bb 100644 (file)
@@ -373,4 +373,22 @@ class AutomationCompositionOutHandlerTest {
                 .sendAutomationCompositionAck(any(AutomationCompositionDeployAck.class));
         assertEquals(compositionId, automationComposition.getCompositionId());
     }
+
+    @Test
+    void deleteFailMigrationTest() {
+        var cacheProvider = mock(CacheProvider.class);
+        when(cacheProvider.getParticipantId()).thenReturn(UUID.randomUUID());
+
+        var automationComposition = CommonTestData.getTestAutomationCompositionMap().values().iterator().next();
+        when(cacheProvider.getAutomationComposition(automationComposition.getInstanceId()))
+                .thenReturn(automationComposition);
+
+        automationComposition.setCompositionTargetId(UUID.randomUUID());
+        automationComposition.setDeployState(DeployState.MIGRATING);
+        var publisher = mock(ParticipantMessagePublisher.class);
+        var acOutHandler = new AutomationCompositionOutHandler(publisher, cacheProvider);
+        acOutHandler.updateAutomationCompositionElementState(automationComposition.getInstanceId(),
+            UUID.randomUUID(), DeployState.UNDEPLOYED, null, StateChangeResult.FAILED, "");
+        verify(publisher).sendAutomationCompositionAck(any(AutomationCompositionDeployAck.class));
+    }
 }
index 8706891..8785b59 100644 (file)
@@ -75,6 +75,14 @@ public class SimpleScanner extends AbstractScanner {
     private UpdateSync handleAcStateChange(AutomationComposition automationComposition, DocMessage message) {
         var result = new UpdateSync();
         var element = automationComposition.getElements().get(message.getInstanceElementId());
+        if (element == null && isMigration(automationComposition)
+                && StateChangeResult.FAILED.equals(message.getStateChangeResult())) {
+            // fail delete element during migration
+            automationComposition.setStateChangeResult(StateChangeResult.FAILED);
+            result.setUpdated(true);
+            result.setToBeSync(true);
+            return result;
+        }
         if (element == null || !validateStateMessage(automationComposition, message)) {
             return result;
         }
@@ -93,6 +101,11 @@ public class SimpleScanner extends AbstractScanner {
         return result;
     }
 
+    private boolean isMigration(AutomationComposition automationComposition) {
+        return DeployState.MIGRATING.equals(automationComposition.getDeployState())
+                || DeployState.MIGRATION_REVERTING.equals(automationComposition.getDeployState());
+    }
+
     private boolean validateStateMessage(AutomationComposition automationComposition, DocMessage message) {
         return !DeployState.DELETED.equals(message.getDeployState())
                 || (DeployState.DELETING.equals(automationComposition.getDeployState()));
index b155eda..e8a0254 100644 (file)
@@ -203,30 +203,15 @@ class SimpleScannerTest {
     }
 
     private AutomationComposition createDeploying() {
-        var automationComposition = InstantiationUtils.getAutomationCompositionFromResource(AC_JSON, "Crud");
-        automationComposition.setInstanceId(INSTANCE_ID);
-        automationComposition.setDeployState(DeployState.DEPLOYING);
-        automationComposition.setLockState(LockState.NONE);
-        automationComposition.setPhase(0);
-        automationComposition.setLastMsg(TimestampHelper.now());
-        automationComposition.setCompositionId(COMPOSITION_ID);
-        for (var element : automationComposition.getElements().values()) {
-            element.setDeployState(DeployState.DEPLOYING);
-            element.setLockState(LockState.NONE);
-        }
-        return automationComposition;
+        return createAutomationComposition(DeployState.DEPLOYING, LockState.NONE);
     }
 
     @Test
     void testSendAutomationCompositionMigratingPrecheck() {
-        var automationComposition = InstantiationUtils.getAutomationCompositionFromResource(AC_JSON, "Crud");
-        automationComposition.setLockState(LockState.LOCKED);
-        automationComposition.setDeployState(DeployState.DEPLOYED);
+        var automationComposition = createAutomationComposition(DeployState.DEPLOYED, LockState.LOCKED);
         automationComposition.setSubState(SubState.MIGRATION_PRECHECKING);
         for (var element : automationComposition.getElements().values()) {
-            element.setDeployState(DeployState.DEPLOYED);
             element.setSubState(SubState.NONE);
-            element.setLockState(LockState.LOCKED);
             if (ELEMENT_NAME.equals(element.getDefinition().getName())) {
                 element.setSubState(SubState.MIGRATION_PRECHECKING);
             }
@@ -236,14 +221,10 @@ class SimpleScannerTest {
 
     @Test
     void testSendAutomationCompositionPrepare() {
-        var automationComposition = InstantiationUtils.getAutomationCompositionFromResource(AC_JSON, "Crud");
-        automationComposition.setLockState(LockState.NONE);
-        automationComposition.setDeployState(DeployState.UNDEPLOYED);
+        var automationComposition = createAutomationComposition(DeployState.UNDEPLOYED, LockState.NONE);
         automationComposition.setSubState(SubState.PREPARING);
         for (var element : automationComposition.getElements().values()) {
-            element.setDeployState(DeployState.UNDEPLOYED);
             element.setSubState(SubState.NONE);
-            element.setLockState(LockState.NONE);
             if (ELEMENT_NAME.equals(element.getDefinition().getName())) {
                 element.setSubState(SubState.PREPARING);
             }
@@ -285,4 +266,39 @@ class SimpleScannerTest {
         simpleScanner.simpleScan(automationComposition, new UpdateSync());
         verify(acProvider).updateAutomationComposition(any());
     }
+
+    @Test
+    void testScanMessageMigrationFail() {
+        var automationComposition = createAutomationComposition(DeployState.MIGRATING, LockState.LOCKED);
+        var elementId = UUID.randomUUID();
+        var docMessage = new DocMessage();
+        docMessage.setMessageType(ParticipantMessageType.AUTOMATION_COMPOSITION_STATECHANGE_ACK);
+        docMessage.setStateChangeResult(StateChangeResult.FAILED);
+        docMessage.setInstanceId(INSTANCE_ID);
+        docMessage.setInstanceElementId(elementId);
+        docMessage.setDeployState(DeployState.UNDEPLOYED);
+        docMessage.setLockState(LockState.NONE);
+        var acProvider = mock(AutomationCompositionProvider.class);
+        var acRuntimeParameterGroup = CommonTestData.geParameterGroup("dbScanner");
+        var simpleScanner = new SimpleScanner(acProvider, mock(ParticipantSyncPublisher.class),
+                acRuntimeParameterGroup, new EncryptionUtils(acRuntimeParameterGroup));
+        var result = simpleScanner.scanMessage(automationComposition, docMessage);
+        assertTrue(result.isUpdated());
+        assertTrue(result.isToBeSync());
+    }
+
+    private AutomationComposition createAutomationComposition(DeployState deployState, LockState lockState) {
+        var automationComposition = InstantiationUtils.getAutomationCompositionFromResource(AC_JSON, "Crud");
+        automationComposition.setInstanceId(INSTANCE_ID);
+        automationComposition.setDeployState(deployState);
+        automationComposition.setLockState(lockState);
+        automationComposition.setPhase(0);
+        automationComposition.setLastMsg(TimestampHelper.now());
+        automationComposition.setCompositionId(COMPOSITION_ID);
+        for (var element : automationComposition.getElements().values()) {
+            element.setDeployState(deployState);
+            element.setLockState(lockState);
+        }
+        return automationComposition;
+    }
 }