From 5462caf3f1310c94660123acc36e0feb8128822d Mon Sep 17 00:00:00 2001 From: ialbert Date: Mon, 30 Jun 2025 11:09:12 +0300 Subject: [PATCH] state machine unit test --- .../session/teststate/StateMachineTest.java | 51 +-- .../config/TestStateMachineConfig.java | 293 +++++++++--------- 2 files changed, 178 insertions(+), 166 deletions(-) diff --git a/clearing-parent/clearing-service/src/test/java/ru/spcex/clearing/session/teststate/StateMachineTest.java b/clearing-parent/clearing-service/src/test/java/ru/spcex/clearing/session/teststate/StateMachineTest.java index 4a436a165..14cc43f3d 100644 --- a/clearing-parent/clearing-service/src/test/java/ru/spcex/clearing/session/teststate/StateMachineTest.java +++ b/clearing-parent/clearing-service/src/test/java/ru/spcex/clearing/session/teststate/StateMachineTest.java @@ -8,8 +8,8 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.statemachine.StateMachine; import org.springframework.statemachine.config.StateMachineFactory; -import ru.spcex.clearing.session.teststate.config.Event; -import ru.spcex.clearing.session.teststate.config.State; +import ru.spcex.clearing.session.stage.TaskType; +import ru.spcex.clearing.session.state.SsnEvent; import ru.spcex.clearing.session.teststate.config.TestStateMachineConfig; import ru.spcex.clearing.session.teststate.config.TestStateMachineExecutorsConfig; @@ -21,9 +21,9 @@ class StateMachineTest { private final Logger log = LoggerFactory.getLogger(getClass()); @Autowired - private StateMachineFactory stateMachineFactory; + private StateMachineFactory stateMachineFactory; - private StateMachine stateMachine; + private StateMachine stateMachine; @BeforeEach @@ -35,7 +35,7 @@ class StateMachineTest { @Test void test() { - log.debug("test???"); + log.debug("test started"); // stateMachine // .getStateMachineAccessor() // .doWithRegion(function -> function.addStateMachineInterceptor( @@ -84,20 +84,20 @@ class StateMachineTest { // } // })); - stateMachine.sendEvent(Event.startSession); - try { - Thread.sleep(5000); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - stateMachine.sendEvent(Event.SDF_01); - stateMachine.sendEvent(Event.SDF_04); - stateMachine.sendEvent(Event.SDF_57); - try { - Thread.sleep(10000); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } + // stateMachine.sendEvent(SsnEvent.); +// try { +// Thread.sleep(5000); +// } catch (InterruptedException e) { +// throw new RuntimeException(e); +// } +// stateMachine.sendEvent(Event.SDF_01); +// stateMachine.sendEvent(Event.SDF_04); +// stateMachine.sendEvent(Event.SDF_57); +// try { +// Thread.sleep(10000); +// } catch (InterruptedException e) { +// throw new RuntimeException(e); +// } // stateMachine.sendEvent(Event.continueRevise); // log.info("'ve send a continueRevise"); // try { @@ -109,7 +109,20 @@ class StateMachineTest { // stateMachine.stop(); // log.info("SECOND STOP"); // stateMachine.stop(); + sleepForNSec(5); + stateMachine.sendEvent(SsnEvent.SDF_57); + sleepForNSec(5); + stateMachine.sendEvent(SsnEvent.CONTINUE); + sleepForNSec(5); log.info("So. We end with SM in state: {}", stateMachine.getState().getId());; } + public static void sleepForNSec(int i) { + try { + Thread.sleep((i * 1000L)); + } catch (InterruptedException e) { + System.out.println("***** INTERRUPTED"); + } + } + } \ No newline at end of file diff --git a/clearing-parent/clearing-service/src/test/java/ru/spcex/clearing/session/teststate/config/TestStateMachineConfig.java b/clearing-parent/clearing-service/src/test/java/ru/spcex/clearing/session/teststate/config/TestStateMachineConfig.java index 017a2443c..8664a4282 100644 --- a/clearing-parent/clearing-service/src/test/java/ru/spcex/clearing/session/teststate/config/TestStateMachineConfig.java +++ b/clearing-parent/clearing-service/src/test/java/ru/spcex/clearing/session/teststate/config/TestStateMachineConfig.java @@ -1,15 +1,14 @@ package ru.spcex.clearing.session.teststate.config; -import java.util.ArrayList; +import java.util.Arrays; import java.util.EnumSet; -import java.util.List; +import java.util.HashSet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Configuration; import org.springframework.core.task.TaskExecutor; -import org.springframework.messaging.Message; import org.springframework.statemachine.StateMachine; import org.springframework.statemachine.action.Action; import org.springframework.statemachine.config.EnableStateMachineFactory; @@ -19,17 +18,18 @@ import org.springframework.statemachine.config.builders.StateMachineStateConfigu import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer; import org.springframework.statemachine.listener.StateMachineListenerAdapter; import org.springframework.statemachine.transition.Transition; -import ru.spcex.clearing.session.teststate.config.action.ContinueReviseTestAction; -import ru.spcex.clearing.session.teststate.config.action.InitialAction; -import ru.spcex.clearing.session.teststate.config.action.SelfAction; -import ru.spcex.clearing.session.teststate.config.action.Send56TestAction; -import ru.spcex.clearing.session.teststate.config.guard.NoActiveSessionGuard; -import ru.spcex.clearing.util.StateMachineUtil; +import ru.spcex.clearing.session.stage.TaskType; +import ru.spcex.clearing.session.state.SsnEvent; +import ru.spcex.clearing.session.state.action.SdfReceivedAction; +import ru.spcex.clearing.session.state.guard.PaymentsWereCreatedGuard; +import ru.spcex.clearing.session.state.guard.SdfGuard; +import ru.spcex.clearing.session.state.guard.SdfGuardExtractorAfterAssets; +import static ru.spcex.clearing.util.StateMachineUtil.chain; import ru.spcex.platform.utils.log.ExceptionUtils; @Configuration @EnableStateMachineFactory -public class TestStateMachineConfig extends EnumStateMachineConfigurerAdapter { +public class TestStateMachineConfig extends EnumStateMachineConfigurerAdapter { private final Logger log = LoggerFactory.getLogger(getClass()); @Autowired @@ -38,179 +38,178 @@ public class TestStateMachineConfig extends EnumStateMachineConfigurerAdapter states) throws Exception { + public void configure(StateMachineStateConfigurer states) throws Exception { states .withStates() - .initial(State.initial, StateMachineUtil.chain(new InitialAction(), new SelfAction())) - .state(State.initial) - .state(State.startRevise) - .state(State.continueRevise, Event.triggerInternal) - .state(State.stateWithInternalTransition) - .state(State.stateAutoTransitionAfterInternalAutoTransition) - .state(State.stateWaitingForSdf) - .state(State.stateAfterSdfReceived) - ; + .initial(TaskType.StartRevise, chain( + context -> log.info("create session action"), + context -> log.info("send sdf56 action"))) + .states(new HashSet<>(Arrays.asList( + TaskType.StartRevise, + TaskType.StartRevisePart1, + TaskType.DealsPrepare, + TaskType.RequirementsAndObligationsCreate, + TaskType.ObligationsAdmission, + TaskType.InclusionToPool, + TaskType.InspectionObligations, + TaskType.FormingRegistersOnOS, + TaskType.FormingPaymentInstruction, + TaskType.FinishingSession, + TaskType.EndStageNotification, + TaskType.PSEUDO_waitingForSdf, + TaskType.PSEUDO_waitingForObligationAdmission + ))) + .end(TaskType.EndStageNotification); } @Override - public void configure(StateMachineTransitionConfigurer transitions) throws Exception { + public void configure(StateMachineTransitionConfigurer transitions) throws Exception { transitions .withExternal() - .source(State.initial) - .target(State.startRevise) - .event(Event.startSession) - .guard(new NoActiveSessionGuard()) - .action(new Send56TestAction()) - .and() + .event(SsnEvent.SDF_57) + .source(TaskType.StartRevise).target(TaskType.StartRevisePart1) + .action(context -> log.info("reviseStage1Action")) + .and() .withExternal() - .source(State.startRevise) - .target(State.continueRevise) - .action(new ContinueReviseTestAction()) - - //.and() - //.withExternal() - // .source(State.continueRevise) - // .target(State.newState) - // .action(context -> log.info("action without an event")) - .and() + .source(TaskType.StartRevisePart1).target(TaskType.DealsPrepare) + .action(context -> log.info("dealsPrepareAction")) + .and() .withExternal() - .source(State.continueRevise) - .target(State.stateWithInternalTransition) - .action(context -> { - log.info("ACTION without an event"); -// context.getStateMachine().sendEvent(Event.triggerInternal); - }) - .and() - .withInternal() - .source(State.stateWithInternalTransition) - .action(context -> { - log.info("ACTION INTERNAL without an event"); - }) - .action(context -> { - log.info("ACTION INTERNAL second without an event"); - }) - .and() + .source(TaskType.DealsPrepare).target(TaskType.RequirementsAndObligationsCreate) + .action(context -> log.info("reqAndOblAction")) + .and() .withExternal() - .source(State.stateWithInternalTransition) - .timerOnce(1) - .target(State.stateAutoTransitionAfterInternalAutoTransition) - .action(ctx -> { - log.info("ACTION after internal transition"); - List list = new ArrayList<>(); - list.add("fasdfasdf"); - ctx.getExtendedState().getVariables().put("payments", list); - }) - .and() - .withExternal() - .source(State.stateAutoTransitionAfterInternalAutoTransition) - .target(State.stateAfterSdfReceived) - .action(ctx -> log.info("ACTION stateAutoTransitionAfterInternalAutoTransition to stateAfterSdfReceived without waiting")) - .guard(ctx -> { - List list = ctx.getExtendedState().get("payments", List.class); - return list != null && list.isEmpty(); - }) - .and() + .source(TaskType.RequirementsAndObligationsCreate).target(TaskType.ObligationsAdmission) + .action(context -> log.info("obligationAdmissionAction")) + .and() .withExternal() - .source(State.stateAutoTransitionAfterInternalAutoTransition) - .target(State.stateWaitingForSdf) - .action(ctx -> log.info("ACTION stateAutoTransitionAfterInternalAutoTransition to stateWaitingForSdf")) - .guard(ctx -> { - List list = ctx.getExtendedState().get("payments", List.class); - return list != null && !list.isEmpty(); - }) - .and() + .source(TaskType.ObligationsAdmission) + .target(TaskType.PSEUDO_waitingForObligationAdmission) + .guard(context -> true) + .and() .withExternal() - .source(State.stateWaitingForSdf) - .target(State.stateAfterSdfReceived) - .guard(ctx -> { - Boolean sdf01Received = ctx.getExtendedState().get(Event.SDF_01.name(), Boolean.class); - Boolean sdf04Received = ctx.getExtendedState().get(Event.SDF_04.name(), Boolean.class); - Boolean sdf57Received = ctx.getExtendedState().get(Event.SDF_57.name(), Boolean.class); - log.info("GUARD received sdfs: sdf01={}, sdf04={}, sdf57={}", - sdf01Received, sdf04Received, sdf57Received); - return Boolean.TRUE.equals(sdf01Received) - && Boolean.TRUE.equals(sdf04Received) - && Boolean.TRUE.equals(sdf57Received); - }); - - for (var event : EnumSet.of(Event.SDF_01, Event.SDF_04, Event.SDF_57)) { + .source(TaskType.PSEUDO_waitingForObligationAdmission).target(TaskType.InclusionToPool) + .action(chain(ctx -> log.info("obligationAdmissionContinueAction"), ctx -> log.info("inclusionToPoolAction"))) + .event(SsnEvent.CONTINUE) + .and() + .withExternal() + .source(TaskType.PSEUDO_waitingForObligationAdmission).target(TaskType.ObligationsAdmission) + .action(chain(act("discardOblAdmStash"), act("obligationAdmissionAction"))) + .event(SsnEvent.REPEAT) + .and() + .withExternal() + .source(TaskType.ObligationsAdmission).target(TaskType.InclusionToPool) + .guard(context -> false) //invert(oblAdmGuard) + .action(chain(act("obligationAdmissionContinueAction"), act("inclusionToPoolAction"))) + .and() + .withExternal() + .source(TaskType.InclusionToPool).target(TaskType.InspectionObligations) + .action(act("inspOblDepositReturnAction")) + .action(act("inspectionObligationsV2Action")) + .and() + .withExternal() + .source(TaskType.InspectionObligations).target(TaskType.FormingPaymentInstruction) + .action(act("formingPaymentInstructionReturnMkrAction")) + .action(act("formingPaymentInstructionAssetsAction")) + .and() + //если не создалось paymentInstruction'ов + .withExternal() + .source(TaskType.FormingPaymentInstruction) + .target(TaskType.FinishingSession) + .guard(ctx -> true) //PaymentsWereNotCreatedGuard.instance + .action(act("finishingSessionAction")) + .and() + //если создались paymentInstruction, переходим в режим ожидания + .withExternal() + .source(TaskType.FormingPaymentInstruction) + .target(TaskType.PSEUDO_waitingForSdf) + .guard(PaymentsWereCreatedGuard.instance) + .and() + .withExternal() + .source(TaskType.PSEUDO_waitingForSdf) + .target(TaskType.FinishingSession) + .guard(new SdfGuard(SdfGuardExtractorAfterAssets.instance)) + .action(act("finishingSessionAction")) + .and() + .withExternal() + .source(TaskType.FinishingSession) + .target(TaskType.EndStageNotification) + .action(act("endStageNotificationAction")); + for (var event: EnumSet.of(SsnEvent.SDF_01, SsnEvent.SDF_57, SsnEvent.SDF_04)) { transitions .withInternal() - .source(State.stateWaitingForSdf) + .source(TaskType.PSEUDO_waitingForSdf) .event(event) - .action(eventReceivedAction()); + .action(SdfReceivedAction.instance); } } - - public Action eventReceivedAction() { - return ctx -> { - log.info("event {} flag saving received", ctx.getEvent().name()); - ctx.getExtendedState().getVariables().put(ctx.getEvent().name(), true); - }; + private Action act(String actionName) { + return ctx -> log.info(actionName); } @Override - public void configure(StateMachineConfigurationConfigurer config) throws Exception { - StateMachineListenerAdapter loggingChangeStateListener = new StateMachineListenerAdapter<>() { - @Override - public void stateEntered(org.springframework.statemachine.state.State state) { - State enteredState = state != null ? state.getId() : null; - log.info(String.format("LISTENER stateEntered: %s", enteredState)); - } + public void configure(StateMachineConfigurationConfigurer config) throws Exception { + StateMachineListenerAdapter loggingChangeStateListener = new StateMachineListenerAdapter<>() { +// @Override +// public void stateEntered(org.springframework.statemachine.state.State state) { +// TaskType enteredState = state != null ? state.getId() : null; +// log.info(String.format("LISTENER stateEntered: %s", enteredState)); +// } + +// @Override +// public void eventNotAccepted(Message event) { +// SsnEvent payload = event != null ? event.getPayload() : null; +// log.info(String.format("LISTENER eventNotAccepted: %s", payload)); +// } @Override - public void eventNotAccepted(Message event) { - Event payload = event != null ? event.getPayload() : null; - log.info(String.format("LISTENER eventNotAccepted: %s", payload)); - } - - @Override - public void transition(Transition transition) { - State source = transition.getSource() != null ? transition.getSource().getId() : null; - State target = transition.getTarget().getId(); + public void transition(Transition transition) { + TaskType source = transition.getSource() != null ? transition.getSource().getId() : null; + TaskType target = transition.getTarget().getId(); log.info("LISTENER transition: source {} target {}", source, target); } - @Override - public void stateChanged(org.springframework.statemachine.state.State from, org.springframework.statemachine.state.State to) { - State source = from != null ? from.getId() : null; - State target = to.getId(); - log.info("LISTENER stateChanged: source {} target {}", - source, target); - } +// @Override +// public void stateChanged(org.springframework.statemachine.state.State from, org.springframework.statemachine.state.State to) { +// TaskType source = from != null ? from.getId() : null; +// TaskType target = to.getId(); +// log.info("LISTENER stateChanged: source {} target {}", +// source, target); +// } + +// @Override +// public void stateExited(org.springframework.statemachine.state.State state) { +// TaskType whichOne = state != null ? state.getId() : null; +// log.info("LISTENER stateExited: {}", whichOne); +// } + +// @Override +// public void transitionEnded(Transition transition) { +// TaskType source = transition.getSource() != null ? transition.getSource().getId() : null; +// TaskType target = transition.getTarget().getId(); +// log.info("LISTENER transitionEnded: source {} target {}", +// source, target); +// } @Override - public void stateExited(org.springframework.statemachine.state.State state) { - State whichOne = state != null ? state.getId() : null; - log.info("LISTENER stateExited: {}", whichOne); - } - - @Override - public void transitionEnded(Transition transition) { - State source = transition.getSource() != null ? transition.getSource().getId() : null; - State target = transition.getTarget().getId(); - log.info("LISTENER transitionEnded: source {} target {}", - source, target); - } - - @Override - public void stateMachineError(StateMachine stateMachine, Exception exception) { + public void stateMachineError(StateMachine stateMachine, Exception exception) { log.info("LISTENER stateMachineError: {}", (ExceptionUtils.getStackTrace(exception))); } - @Override - public void transitionStarted(Transition transition) { - State source = transition.getSource() != null ? transition.getSource().getId() : null; - State target = transition.getTarget().getId(); - log.info("LISTENER transitionStarted: source {} target {}", - source, target); - } +// @Override +// public void transitionStarted(Transition transition) { +// TaskType source = transition.getSource() != null ? transition.getSource().getId() : null; +// TaskType target = transition.getTarget().getId(); +// log.info("LISTENER transitionStarted: source {} target {}", +// source, target); +// } }; config .withConfiguration() + .machineId("test-machine") .listener(loggingChangeStateListener) // .taskExecutor(taskExecutor) ;