defaultPersist) {
50 |
51 | return new DefaultStateMachinePersister<>(defaultPersist);
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/main/java/com/antkorwin/statemachine/config/SecondStateMachineConfig.java:
--------------------------------------------------------------------------------
1 | package com.antkorwin.statemachine.config;
2 |
3 | import com.antkorwin.statemachine.statemachine.Events;
4 | import com.antkorwin.statemachine.statemachine.States;
5 | import lombok.extern.slf4j.Slf4j;
6 | import org.springframework.context.annotation.Configuration;
7 | import org.springframework.messaging.Message;
8 | import org.springframework.statemachine.action.Action;
9 | import org.springframework.statemachine.config.EnableStateMachineFactory;
10 | import org.springframework.statemachine.config.EnumStateMachineConfigurerAdapter;
11 | import org.springframework.statemachine.config.builders.StateMachineConfigurationConfigurer;
12 | import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;
13 | import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;
14 | import org.springframework.statemachine.guard.Guard;
15 | import org.springframework.statemachine.listener.StateMachineListener;
16 | import org.springframework.statemachine.listener.StateMachineListenerAdapter;
17 | import org.springframework.statemachine.state.State;
18 | import org.springframework.statemachine.transition.Transition;
19 |
20 | import java.util.Optional;
21 |
22 | /**
23 | * Created by Korovin Anatolii on 05.05.2018.
24 | *
25 | * Second state machine, you can:
26 | * - use this other states and events
27 | * - make other business logic configuration
28 | *
29 | * @author Korovin Anatolii
30 | * @version 1.0
31 | */
32 | @Slf4j
33 | @Configuration
34 | @EnableStateMachineFactory(name = "secondStateMachineFactory")
35 | public class SecondStateMachineConfig extends EnumStateMachineConfigurerAdapter {
36 |
37 | @Override
38 | public void configure(StateMachineConfigurationConfigurer config) throws Exception {
39 | config.withConfiguration()
40 | .listener(listener())
41 | .autoStartup(true);
42 | }
43 |
44 | private StateMachineListener listener() {
45 |
46 | return new StateMachineListenerAdapter() {
47 | @Override
48 | public void eventNotAccepted(Message event) {
49 | log.error("SECOND: Not accepted event: {}", event);
50 | }
51 |
52 | @Override
53 | public void transition(Transition transition) {
54 | log.info("TRANS from: {} to: {}",
55 | ofNullableState(transition.getSource()),
56 | ofNullableState(transition.getTarget()));
57 | }
58 |
59 | private Object ofNullableState(State s) {
60 | return Optional.ofNullable(s)
61 | .map(State::getId)
62 | .orElse(null);
63 | }
64 | };
65 | }
66 |
67 | @Override
68 | public void configure(StateMachineStateConfigurer states) throws Exception {
69 | states.withStates()
70 | .initial(States.BACKLOG)
71 | .end(States.DONE);
72 | }
73 |
74 | @Override
75 | public void configure(StateMachineTransitionConfigurer transitions) throws Exception {
76 | transitions.withExternal()
77 | // BACKLOG --> DONE : START_FEATURE
78 | .source(States.BACKLOG)
79 | .target(States.DONE)
80 | .event(Events.START_FEATURE)
81 | .guard(failGuard())
82 | .and()
83 | // BACKLOG --> DONE : ROCK_STAR_DOUBLE_TASK
84 | .withExternal()
85 | .source(States.BACKLOG)
86 | .target(States.DONE)
87 | .action(failAction())
88 | .event(Events.ROCK_STAR_DOUBLE_TASK)
89 | .and()
90 | // BACKLOG --> TESTING : FINISH_FEATURE
91 | .withExternal()
92 | .source(States.BACKLOG)
93 | .target(States.DONE)
94 | .action(actionWithInternalError())
95 | .event(Events.FINISH_FEATURE);
96 | }
97 |
98 | private Action failAction() {
99 | return context -> {
100 | throw new RuntimeException("fail in action");
101 | };
102 | }
103 |
104 | private Guard failGuard() {
105 | return context -> {
106 | throw new RuntimeException("fail in Guard");
107 | };
108 | }
109 |
110 | private Action actionWithInternalError() {
111 | return context -> {
112 | context.getStateMachine()
113 | .setStateMachineError(new RuntimeException("fail in Action"));
114 | };
115 | }
116 |
117 |
118 | }
119 |
--------------------------------------------------------------------------------
/src/main/java/com/antkorwin/statemachine/config/StateMachineConfig.java:
--------------------------------------------------------------------------------
1 | package com.antkorwin.statemachine.config;
2 |
3 | import com.antkorwin.statemachine.statemachine.Events;
4 | import com.antkorwin.statemachine.statemachine.States;
5 | import lombok.extern.slf4j.Slf4j;
6 | import org.springframework.context.annotation.Configuration;
7 | import org.springframework.messaging.Message;
8 | import org.springframework.statemachine.action.Action;
9 | import org.springframework.statemachine.config.EnableStateMachineFactory;
10 | import org.springframework.statemachine.config.EnumStateMachineConfigurerAdapter;
11 | import org.springframework.statemachine.config.builders.StateMachineConfigurationConfigurer;
12 | import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;
13 | import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;
14 | import org.springframework.statemachine.guard.Guard;
15 | import org.springframework.statemachine.listener.StateMachineListener;
16 | import org.springframework.statemachine.listener.StateMachineListenerAdapter;
17 | import org.springframework.statemachine.state.State;
18 | import org.springframework.statemachine.transition.Transition;
19 |
20 | import java.util.Optional;
21 |
22 | /**
23 | * Created by Korovin Anatolii on 05.05.2018.
24 | *
25 | * @author Korovin Anatolii
26 | * @version 1.0
27 | */
28 | @Slf4j
29 | @Configuration
30 | @EnableStateMachineFactory
31 | public class StateMachineConfig extends EnumStateMachineConfigurerAdapter {
32 |
33 | @Override
34 | public void configure(StateMachineConfigurationConfigurer config) throws Exception {
35 | config.withConfiguration()
36 | .listener(listener())
37 | .autoStartup(true);
38 | }
39 |
40 | private StateMachineListener listener() {
41 |
42 | return new StateMachineListenerAdapter() {
43 | @Override
44 | public void eventNotAccepted(Message event) {
45 | log.error("Not accepted event: {}", event);
46 | }
47 |
48 | @Override
49 | public void transition(Transition transition) {
50 | log.warn("MOVE from: {}, to: {}",
51 | ofNullableState(transition.getSource()),
52 | ofNullableState(transition.getTarget()));
53 | }
54 |
55 | private Object ofNullableState(State s) {
56 | return Optional.ofNullable(s)
57 | .map(State::getId)
58 | .orElse(null);
59 | }
60 | };
61 | }
62 |
63 |
64 | @Override
65 | public void configure(StateMachineStateConfigurer states) throws Exception {
66 | states.withStates()
67 | .initial(States.BACKLOG, developersWakeUpAction())
68 | .state(States.IN_PROGRESS, weNeedCoffeeAction())
69 | .state(States.TESTING, qaWakeUpAction())
70 | .state(States.DONE, goToSleepAction())
71 | .end(States.DONE);
72 | }
73 |
74 | private Action developersWakeUpAction() {
75 | return stateContext -> log.warn("Просыпайтесь лентяи!");
76 | }
77 |
78 | private Action weNeedCoffeeAction() {
79 | return stateContext -> log.warn("Без кофе никак!");
80 | }
81 |
82 | private Action qaWakeUpAction() {
83 | return stateContext -> log.warn("Будим команду тестирования, солнце высоко!");
84 | }
85 |
86 | private Action goToSleepAction() {
87 | return stateContext -> log.warn("Всем спать! клиент доволен.");
88 | }
89 |
90 | @Override
91 | public void configure(StateMachineTransitionConfigurer transitions) throws Exception {
92 | transitions.withExternal()
93 | .source(States.BACKLOG)
94 | .target(States.IN_PROGRESS)
95 | .event(Events.START_FEATURE)
96 | .and()
97 | // DEVELOPERS:
98 | .withExternal()
99 | .source(States.IN_PROGRESS)
100 | .target(States.TESTING)
101 | .event(Events.FINISH_FEATURE)
102 | .guard(alreadyDeployedGuard())
103 | .and()
104 | // QA-TEAM:
105 | .withExternal()
106 | .source(States.TESTING)
107 | .target(States.DONE)
108 | .event(Events.QA_CHECKED_UC)
109 | .and()
110 | .withExternal()
111 | .source(States.TESTING)
112 | .target(States.IN_PROGRESS)
113 | .event(Events.QA_REJECTED_UC)
114 | .and()
115 | // ROCK-STAR:
116 | .withExternal()
117 | .source(States.BACKLOG)
118 | .target(States.TESTING)
119 | .event(Events.ROCK_STAR_DOUBLE_TASK)
120 | .and()
121 | // DEVOPS:
122 | .withInternal()
123 | .source(States.IN_PROGRESS)
124 | .event(Events.DEPLOY)
125 | .action(deployPreProd())
126 | .and()
127 | .withInternal()
128 | .source(States.BACKLOG)
129 | .event(Events.DEPLOY)
130 | .action(deployPreProd());
131 | }
132 |
133 | private Guard alreadyDeployedGuard() {
134 | return context -> Optional.ofNullable(context.getExtendedState().getVariables().get("deployed"))
135 | .map(v -> (boolean) v)
136 | .orElse(false);
137 | }
138 |
139 | private Action deployPreProd() {
140 | return stateContext -> {
141 | log.warn("DEPLOY: Выкатываемся на препродакшен.");
142 | stateContext.getExtendedState().getVariables().put("deployed", true);
143 | };
144 | }
145 | }
146 |
--------------------------------------------------------------------------------
/src/main/java/com/antkorwin/statemachine/persist/CustomStateMachinePersist.java:
--------------------------------------------------------------------------------
1 | package com.antkorwin.statemachine.persist;
2 |
3 | import com.antkorwin.statemachine.statemachine.Events;
4 | import com.antkorwin.statemachine.statemachine.States;
5 | import lombok.extern.slf4j.Slf4j;
6 | import org.springframework.statemachine.StateMachineContext;
7 | import org.springframework.statemachine.StateMachinePersist;
8 |
9 | import java.util.HashMap;
10 | import java.util.UUID;
11 |
12 | /**
13 | * Created by Korovin Anatolii on 06.05.2018.
14 | *
15 | * @author Korovin Anatolii
16 | * @version 1.0
17 | */
18 | @Slf4j
19 | public class CustomStateMachinePersist implements StateMachinePersist {
20 |
21 | private HashMap> storage = new HashMap<>();
22 |
23 | @Override
24 | public void write(StateMachineContext context, UUID contextObj) throws Exception {
25 | log.warn("!!!push: "+contextObj);
26 | storage.put(contextObj, context);
27 | }
28 |
29 | @Override
30 | public StateMachineContext read(UUID contextObj) throws Exception {
31 | log.warn("!!!pop: "+contextObj);
32 | return storage.get(contextObj);
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/main/java/com/antkorwin/statemachine/persist/InMemoryStateMachinePersist.java:
--------------------------------------------------------------------------------
1 | package com.antkorwin.statemachine.persist;
2 |
3 |
4 | import com.antkorwin.statemachine.statemachine.Events;
5 | import com.antkorwin.statemachine.statemachine.States;
6 | import lombok.extern.slf4j.Slf4j;
7 | import org.springframework.statemachine.StateMachineContext;
8 | import org.springframework.statemachine.StateMachinePersist;
9 |
10 | import java.util.HashMap;
11 | import java.util.UUID;
12 |
13 | /**
14 | * Created by Korovin Anatolii on 06.05.2018.
15 | *
16 | * @author Korovin Anatolii
17 | * @version 1.0
18 | */
19 | @Slf4j
20 | public class InMemoryStateMachinePersist implements StateMachinePersist {
21 |
22 | private HashMap> storage = new HashMap<>();
23 |
24 | @Override
25 | public void write(StateMachineContext context, UUID contextObj) throws Exception {
26 | storage.put(contextObj, context);
27 | }
28 |
29 | @Override
30 | public StateMachineContext read(UUID contextObj) throws Exception {
31 | return storage.get(contextObj);
32 | }
33 | }
34 |
35 |
--------------------------------------------------------------------------------
/src/main/java/com/antkorwin/statemachine/statemachine/Events.java:
--------------------------------------------------------------------------------
1 | package com.antkorwin.statemachine.statemachine;
2 |
3 | /**
4 | * Created by Korovin Anatolii on 05.05.2018.
5 | *
6 | * @author Korovin Anatolii
7 | * @version 1.0
8 | */
9 | public enum Events {
10 | START_FEATURE,
11 | FINISH_FEATURE,
12 | QA_REJECTED_UC,
13 | ROCK_STAR_DOUBLE_TASK,
14 | DEPLOY,
15 | QA_CHECKED_UC
16 | }
17 |
--------------------------------------------------------------------------------
/src/main/java/com/antkorwin/statemachine/statemachine/States.java:
--------------------------------------------------------------------------------
1 | package com.antkorwin.statemachine.statemachine;
2 |
3 | /**
4 | * Created by Korovin Anatolii on 05.05.2018.
5 | *
6 | * @author Korovin Anatolii
7 | * @version 1.0
8 | */
9 | public enum States {
10 | BACKLOG,
11 | IN_PROGRESS,
12 | TESTING,
13 | DONE
14 | }
15 |
16 |
--------------------------------------------------------------------------------
/src/main/java/com/antkorwin/statemachine/statemachine/resolver/StateMachineResolver.java:
--------------------------------------------------------------------------------
1 | package com.antkorwin.statemachine.statemachine.resolver;
2 |
3 | import org.springframework.statemachine.StateMachine;
4 |
5 | import java.util.List;
6 |
7 | /**
8 | * Created by Korovin Anatolii on 02.06.2018.
9 | *
10 | * @author Korovin Anatolii
11 | * @version 1.0
12 | */
13 | public interface StateMachineResolver {
14 |
15 | /**
16 | * Evaluate available events from current states of state-machine
17 | *
18 | * @param stateMachine state machine
19 | *
20 | * @return Events collection
21 | */
22 | List getAvailableEvents(StateMachine stateMachine);
23 | }
24 |
--------------------------------------------------------------------------------
/src/main/java/com/antkorwin/statemachine/statemachine/resolver/StateMachineResolverImpl.java:
--------------------------------------------------------------------------------
1 | package com.antkorwin.statemachine.statemachine.resolver;
2 |
3 | import org.jetbrains.annotations.NotNull;
4 | import org.springframework.statemachine.StateContext;
5 | import org.springframework.statemachine.StateMachine;
6 | import org.springframework.statemachine.support.DefaultStateContext;
7 | import org.springframework.statemachine.transition.Transition;
8 | import org.springframework.statemachine.trigger.Trigger;
9 | import org.springframework.stereotype.Component;
10 |
11 | import java.util.List;
12 |
13 | import static java.util.stream.Collectors.toList;
14 |
15 | /**
16 | * Created by Korovin Anatolii on 02.06.2018.
17 | *
18 | * @author Korovin Anatolii
19 | * @version 1.0
20 | */
21 | @Component
22 | public class StateMachineResolverImpl implements StateMachineResolver {
23 |
24 | @Override
25 | public List getAvailableEvents(StateMachine stateMachine) {
26 |
27 | return stateMachine.getTransitions()
28 | .stream()
29 | .filter(t -> isTransitionSourceFromCurrentState(t, stateMachine))
30 | .filter(t -> evaluateGuardCondition(stateMachine, t))
31 | .map(Transition::getTrigger)
32 | .map(Trigger::getEvent)
33 | .collect(toList());
34 | }
35 |
36 |
37 | private boolean isTransitionSourceFromCurrentState(Transition transition,
38 | StateMachine stateMachine) {
39 |
40 | return stateMachine.getState().getId() == transition.getSource().getId();
41 | }
42 |
43 |
44 | private boolean evaluateGuardCondition(StateMachine stateMachine,
45 | Transition transition) {
46 |
47 | if (transition.getGuard() == null) {
48 | return true;
49 | }
50 |
51 | StateContext context = makeStateContext(stateMachine, transition);
52 |
53 | try {
54 | return transition.getGuard().evaluate(context);
55 | } catch (Exception e) {
56 | return false;
57 | }
58 | }
59 |
60 |
61 | @NotNull
62 | private DefaultStateContext makeStateContext(StateMachine stateMachine,
63 | Transition transition) {
64 |
65 | return new DefaultStateContext<>(StateContext.Stage.TRANSITION,
66 | null,
67 | null,
68 | stateMachine.getExtendedState(),
69 | transition,
70 | stateMachine,
71 | stateMachine.getState(),
72 | transition.getTarget(),
73 | null);
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 | spring.output.ansi.enabled=always
--------------------------------------------------------------------------------
/src/test/java/com/antkorwin/statemachine/BaseMongoIT.java:
--------------------------------------------------------------------------------
1 | package com.antkorwin.statemachine;
2 |
3 | import org.junit.runner.RunWith;
4 | import org.springframework.beans.factory.annotation.Autowired;
5 | import org.springframework.boot.test.context.SpringBootTest;
6 | import org.springframework.data.mongodb.core.MongoTemplate;
7 | import org.springframework.test.context.junit4.SpringRunner;
8 | import org.testcontainers.containers.GenericContainer;
9 |
10 | /**
11 | * Created by Korovin A. on 28.03.2018.
12 | *
13 | * Base Integration Test with
14 | * MongoDb started in TestContainers
15 | *
16 | * @author Korovin Anatoliy
17 | * @version 1.0
18 | */
19 | @SpringBootTest
20 | @RunWith(SpringRunner.class)
21 | public abstract class BaseMongoIT {
22 |
23 | private static final Integer MONGO_PORT = 27017;
24 | private static GenericContainer mongo = new GenericContainer("mongo:latest")
25 | .withExposedPorts(MONGO_PORT);
26 |
27 | static {
28 | mongo.start();
29 | System.setProperty("spring.data.mongodb.host", mongo.getContainerIpAddress());
30 | System.setProperty("spring.data.mongodb.port", mongo.getMappedPort(MONGO_PORT).toString());
31 | }
32 |
33 | @Autowired
34 | protected MongoTemplate mongoTemplate;
35 | }
--------------------------------------------------------------------------------
/src/test/java/com/antkorwin/statemachine/InMemoryPersistTest.java:
--------------------------------------------------------------------------------
1 | package com.antkorwin.statemachine;
2 |
3 | import com.antkorwin.statemachine.statemachine.Events;
4 | import com.antkorwin.statemachine.statemachine.States;
5 | import org.assertj.core.api.Assertions;
6 | import org.junit.Test;
7 | import org.junit.runner.RunWith;
8 | import org.springframework.beans.factory.annotation.Autowired;
9 | import org.springframework.boot.test.context.SpringBootTest;
10 | import org.springframework.statemachine.StateMachine;
11 | import org.springframework.statemachine.config.StateMachineFactory;
12 | import org.springframework.statemachine.persist.StateMachinePersister;
13 | import org.springframework.test.context.ActiveProfiles;
14 | import org.springframework.test.context.junit4.SpringRunner;
15 |
16 | import java.util.UUID;
17 |
18 | /**
19 | * Created by Korovin Anatolii on 06.05.2018.
20 | *
21 | * @author Korovin Anatolii
22 | * @version 1.0
23 | */
24 | @RunWith(SpringRunner.class)
25 | @SpringBootTest
26 | @ActiveProfiles("in-memory")
27 | public class InMemoryPersistTest {
28 |
29 | @Autowired
30 | private StateMachinePersister persister;
31 |
32 | @Autowired
33 | private StateMachineFactory stateMachineFactory;
34 |
35 | @Test
36 | public void testInMemoryPersist() throws Exception {
37 | // Arrange
38 | StateMachine firstStateMachine = stateMachineFactory.getStateMachine();
39 | firstStateMachine.sendEvent(Events.START_FEATURE);
40 | firstStateMachine.sendEvent(Events.DEPLOY);
41 |
42 | StateMachine secondStateMachine = stateMachineFactory.getStateMachine();
43 |
44 | // Check Precondition
45 | Assertions.assertThat(firstStateMachine.getState().getId()).isEqualTo(States.IN_PROGRESS);
46 | Assertions.assertThat((boolean) firstStateMachine.getExtendedState().getVariables().get("deployed"))
47 | .isEqualTo(true);
48 | Assertions.assertThat(secondStateMachine.getState().getId()).isEqualTo(States.BACKLOG);
49 | Assertions.assertThat(secondStateMachine.getExtendedState().getVariables().get("deployed")).isNull();
50 |
51 | // Act
52 | persister.persist(firstStateMachine, firstStateMachine.getUuid());
53 | persister.restore(secondStateMachine, firstStateMachine.getUuid());
54 |
55 | // Asserts
56 | Assertions.assertThat(secondStateMachine.getState().getId()).isEqualTo(States.IN_PROGRESS);
57 | Assertions.assertThat((boolean) secondStateMachine.getExtendedState().getVariables().get("deployed"))
58 | .isEqualTo(true);
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/test/java/com/antkorwin/statemachine/MongoPersistTest.java:
--------------------------------------------------------------------------------
1 | package com.antkorwin.statemachine;
2 |
3 | import com.antkorwin.statemachine.statemachine.Events;
4 | import com.antkorwin.statemachine.statemachine.States;
5 | import org.assertj.core.api.Assertions;
6 | import org.bson.Document;
7 | import org.junit.Before;
8 | import org.junit.Test;
9 | import org.springframework.beans.factory.annotation.Autowired;
10 | import org.springframework.statemachine.StateMachine;
11 | import org.springframework.statemachine.config.StateMachineFactory;
12 | import org.springframework.statemachine.persist.StateMachinePersister;
13 | import org.springframework.test.context.ActiveProfiles;
14 |
15 | import java.util.List;
16 | import java.util.Set;
17 | import java.util.UUID;
18 |
19 | /**
20 | * Created by Korovin Anatolii on 06.05.2018.
21 | *
22 | * @author Korovin Anatolii
23 | * @version 1.0
24 | */
25 | @ActiveProfiles("mongo")
26 | public class MongoPersistTest extends BaseMongoIT {
27 |
28 | @Autowired
29 | private StateMachinePersister persister;
30 | @Autowired
31 | private StateMachineFactory stateMachineFactory;
32 |
33 | @Before
34 | public void setUp() throws Exception {
35 | mongoTemplate.getCollectionNames()
36 | .forEach(mongoTemplate::dropCollection);
37 | }
38 |
39 | @Test
40 | public void firstTest() {
41 | // Arrange
42 | // Act
43 | Set collectionNames = mongoTemplate.getCollectionNames();
44 | // Asserts
45 | Assertions.assertThat(collectionNames).isEmpty();
46 | }
47 |
48 | @Test
49 | public void testMongoPersist() throws Exception {
50 | // Arrange
51 | StateMachine firstStateMachine = stateMachineFactory.getStateMachine();
52 | firstStateMachine.sendEvent(Events.START_FEATURE);
53 | firstStateMachine.sendEvent(Events.DEPLOY);
54 |
55 | StateMachine secondStateMachine = stateMachineFactory.getStateMachine();
56 |
57 | // Check Precondition
58 | Assertions.assertThat(firstStateMachine.getState().getId()).isEqualTo(States.IN_PROGRESS);
59 | Assertions.assertThat((boolean) firstStateMachine.getExtendedState().getVariables().get("deployed"))
60 | .isEqualTo(true);
61 | Assertions.assertThat(secondStateMachine.getState().getId()).isEqualTo(States.BACKLOG);
62 | Assertions.assertThat(secondStateMachine.getExtendedState().getVariables().get("deployed")).isNull();
63 |
64 | // Act
65 | persister.persist(firstStateMachine, firstStateMachine.getUuid());
66 | persister.persist(secondStateMachine, secondStateMachine.getUuid());
67 | persister.restore(secondStateMachine, firstStateMachine.getUuid());
68 |
69 | // Asserts
70 | Assertions.assertThat(secondStateMachine.getState().getId())
71 | .isEqualTo(States.IN_PROGRESS);
72 |
73 | Assertions.assertThat((boolean) secondStateMachine.getExtendedState().getVariables().get("deployed"))
74 | .isEqualTo(true);
75 |
76 | // Mongo specific asserts:
77 | Assertions.assertThat(mongoTemplate.getCollectionNames())
78 | .isNotEmpty();
79 | List documents = mongoTemplate.findAll(Document.class,
80 | "MongoDbRepositoryStateMachine");
81 |
82 | Assertions.assertThat(documents).hasSize(2);
83 | Assertions.assertThat(documents)
84 | .flatExtracting(Document::values)
85 | .contains(firstStateMachine.getUuid().toString(),
86 | secondStateMachine.getUuid().toString())
87 | .contains(firstStateMachine.getState().getId().toString(),
88 | secondStateMachine.getState().getId().toString());
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/src/test/java/com/antkorwin/statemachine/MultipleFactoriesTest.java:
--------------------------------------------------------------------------------
1 | package com.antkorwin.statemachine;
2 |
3 | import com.antkorwin.statemachine.statemachine.Events;
4 | import com.antkorwin.statemachine.statemachine.States;
5 | import com.antkorwin.statemachine.statemachine.resolver.StateMachineResolver;
6 | import org.assertj.core.api.Assertions;
7 | import org.junit.Before;
8 | import org.junit.Test;
9 | import org.junit.runner.RunWith;
10 | import org.springframework.beans.factory.annotation.Autowired;
11 | import org.springframework.beans.factory.annotation.Qualifier;
12 | import org.springframework.boot.test.context.SpringBootTest;
13 | import org.springframework.statemachine.StateMachine;
14 | import org.springframework.statemachine.config.StateMachineFactory;
15 | import org.springframework.test.context.junit4.SpringRunner;
16 |
17 | import java.util.List;
18 |
19 | import static com.antkorwin.statemachine.statemachine.Events.FINISH_FEATURE;
20 | import static com.antkorwin.statemachine.statemachine.Events.ROCK_STAR_DOUBLE_TASK;
21 | import static com.antkorwin.statemachine.statemachine.Events.START_FEATURE;
22 |
23 | /**
24 | * Created by Korovin Anatolii on 06.05.2018.
25 | *
26 | * @author Korovin Anatolii
27 | * @version 1.0
28 | */
29 | @RunWith(SpringRunner.class)
30 | @SpringBootTest
31 | public class MultipleFactoriesTest {
32 |
33 | @Autowired
34 | @Qualifier("secondStateMachineFactory")
35 | private StateMachineFactory secondStateMachineFactory;
36 |
37 | private StateMachine stateMachine;
38 |
39 | @Autowired
40 | private StateMachineResolver stateMachineResolver;
41 |
42 | @Before
43 | public void setUp() throws Exception {
44 | stateMachine = secondStateMachineFactory.getStateMachine();
45 | }
46 |
47 | /**
48 | * Try to make a transition with the guard throws an exception
49 | */
50 | @Test
51 | public void failInGuardTest() {
52 | // Arrange
53 | // Act
54 | stateMachine.sendEvent(START_FEATURE);
55 | // Asserts
56 | Assertions.assertThat(stateMachine.getState().getId())
57 | .isEqualTo(States.BACKLOG);
58 | }
59 |
60 | /**
61 | * Try to make a transition when the action throws an exception
62 | */
63 | @Test
64 | public void failInActionTest() {
65 | // Arrange
66 | // Act
67 | stateMachine.sendEvent(ROCK_STAR_DOUBLE_TASK);
68 | // Asserts
69 | Assertions.assertThat(2+2).isEqualTo(4);
70 | Assertions.assertThat(stateMachine.getState().getId())
71 | .isEqualTo(States.BACKLOG);
72 | }
73 |
74 |
75 | @Test
76 | public void internalFailTest() {
77 | // Check precondition
78 | Assertions.assertThat(stateMachine.hasStateMachineError()).isFalse();
79 | // Act
80 | stateMachine.sendEvent(FINISH_FEATURE);
81 | // Asserts
82 | Assertions.assertThat(stateMachine.getState().getId())
83 | .isEqualTo(States.DONE);
84 |
85 | Assertions.assertThat(stateMachine.hasStateMachineError()).isTrue();
86 | }
87 |
88 | /**
89 | * try to evaluate available events from state with exception in guard
90 | */
91 | @Test
92 | public void testResolver() {
93 | // Arrange
94 | // Act
95 | List availableEvents = stateMachineResolver.getAvailableEvents(stateMachine);
96 |
97 | // Asserts
98 | Assertions.assertThat(availableEvents)
99 | .containsOnly(ROCK_STAR_DOUBLE_TASK, FINISH_FEATURE);
100 | }
101 |
102 |
103 | }
104 |
--------------------------------------------------------------------------------
/src/test/java/com/antkorwin/statemachine/StatemachineApplicationTests.java:
--------------------------------------------------------------------------------
1 | package com.antkorwin.statemachine;
2 |
3 | import com.antkorwin.statemachine.statemachine.Events;
4 | import com.antkorwin.statemachine.statemachine.States;
5 | import org.assertj.core.api.Assertions;
6 | import org.junit.Before;
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 | import org.springframework.beans.factory.annotation.Autowired;
10 | import org.springframework.beans.factory.annotation.Qualifier;
11 | import org.springframework.boot.test.context.SpringBootTest;
12 | import org.springframework.statemachine.StateMachine;
13 | import org.springframework.statemachine.config.StateMachineFactory;
14 | import org.springframework.test.context.junit4.SpringRunner;
15 |
16 | @RunWith(SpringRunner.class)
17 | @SpringBootTest
18 | public class StatemachineApplicationTests {
19 |
20 |
21 | private StateMachine stateMachine;
22 |
23 | @Autowired
24 | private StateMachineFactory stateMachineFactory;
25 |
26 | @Before
27 | public void setUp() throws Exception {
28 | stateMachine = stateMachineFactory.getStateMachine();
29 | }
30 |
31 | @Test
32 | public void contextLoads() {
33 | Assertions.assertThat(stateMachine).isNotNull();
34 | }
35 |
36 | @Test
37 | public void initialStateTest() {
38 | // Asserts
39 | Assertions.assertThat(stateMachine.getInitialState().getId()).isEqualTo(States.BACKLOG);
40 | }
41 |
42 | @Test
43 | public void firstStepTest() {
44 | // Act
45 | stateMachine.sendEvent(Events.START_FEATURE);
46 | // Asserts
47 | Assertions.assertThat(stateMachine.getState().getId()).isEqualTo(States.IN_PROGRESS);
48 | }
49 |
50 | @Test
51 | public void testGreenWay() {
52 | // Arrange
53 | // Act
54 | stateMachine.sendEvent(Events.START_FEATURE);
55 | stateMachine.sendEvent(Events.DEPLOY);
56 | stateMachine.sendEvent(Events.FINISH_FEATURE);
57 | stateMachine.sendEvent(Events.QA_CHECKED_UC);
58 | // Asserts
59 | Assertions.assertThat(stateMachine.getState().getId()).isEqualTo(States.DONE);
60 | }
61 |
62 | @Test
63 | public void testWrongWay() {
64 | // Arrange
65 | // Act
66 | stateMachine.sendEvent(Events.START_FEATURE);
67 | stateMachine.sendEvent(Events.QA_CHECKED_UC);
68 | // Asserts
69 | Assertions.assertThat(stateMachine.getState().getId()).isEqualTo(States.IN_PROGRESS);
70 | }
71 |
72 | @Test
73 | public void rockStarTest() {
74 | // Act
75 | stateMachine.sendEvent(Events.ROCK_STAR_DOUBLE_TASK);
76 | // Asserts
77 | Assertions.assertThat(stateMachine.getState().getId()).isEqualTo(States.TESTING);
78 | }
79 |
80 | @Test
81 | public void testingUnreachableWithoutDeploy() {
82 | // Arrange & Act
83 | stateMachine.sendEvent(Events.START_FEATURE);
84 | stateMachine.sendEvent(Events.FINISH_FEATURE);
85 | stateMachine.sendEvent(Events.QA_CHECKED_UC); // not accepted!
86 | // Asserts
87 | Assertions.assertThat(stateMachine.getState().getId()).isEqualTo(States.IN_PROGRESS);
88 | }
89 |
90 | @Test
91 | public void testDeployFromBacklog() {
92 | // Arrange
93 | // Act
94 | stateMachine.sendEvent(Events.DEPLOY);
95 | // Asserts
96 | Assertions.assertThat(stateMachine.getState().getId()).isEqualTo(States.BACKLOG);
97 | Assertions.assertThat(stateMachine.getExtendedState().getVariables().get("deployed"))
98 | .isEqualTo(true);
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/src/test/java/com/antkorwin/statemachine/statemachine/resolver/StateMachineResolverIT.java:
--------------------------------------------------------------------------------
1 | package com.antkorwin.statemachine.statemachine.resolver;
2 |
3 | import static org.junit.Assert.*;
4 |
5 | import com.antkorwin.statemachine.statemachine.Events;
6 | import com.antkorwin.statemachine.statemachine.States;
7 | import org.assertj.core.api.Assertions;
8 | import org.junit.Before;
9 | import org.junit.Test;
10 | import org.junit.runner.RunWith;
11 | import org.springframework.beans.factory.annotation.Autowired;
12 | import org.springframework.boot.test.context.SpringBootTest;
13 | import org.springframework.statemachine.StateMachine;
14 | import org.springframework.statemachine.config.StateMachineFactory;
15 | import org.springframework.test.context.junit4.SpringRunner;
16 |
17 | import java.util.List;
18 |
19 | /**
20 | * Created by Korovin Anatolii on 02.06.2018.
21 | *
22 | * @author Korovin Anatolii
23 | * @version 1.0
24 | */
25 | @RunWith(SpringRunner.class)
26 | @SpringBootTest
27 | public class StateMachineResolverIT {
28 |
29 | private StateMachine stateMachine;
30 |
31 | @Autowired
32 | private StateMachineFactory stateMachineFactory;
33 |
34 |
35 |
36 | @Before
37 | public void setUp() throws Exception {
38 | stateMachine = stateMachineFactory.getStateMachine();
39 | }
40 |
41 | @Autowired
42 | private StateMachineResolver stateMachineResolver;
43 |
44 |
45 | @Test
46 | public void testResolverWithoutGuard() {
47 | // Arrange
48 | // Act
49 | List availableEvents = stateMachineResolver.getAvailableEvents(stateMachine);
50 | // Asserts
51 | Assertions.assertThat(availableEvents)
52 | .containsOnly(Events.START_FEATURE,
53 | Events.ROCK_STAR_DOUBLE_TASK,
54 | Events.DEPLOY);
55 | }
56 |
57 | @Test
58 | public void testResolverWithGuard() {
59 | // Arrange
60 | stateMachine.sendEvent(Events.START_FEATURE);
61 | // Act
62 | List availableEvents = stateMachineResolver.getAvailableEvents(stateMachine);
63 | // Asserts
64 | Assertions.assertThat(availableEvents)
65 | .containsOnly(Events.DEPLOY);
66 | }
67 | }
--------------------------------------------------------------------------------
/src/test/resources/application.properties:
--------------------------------------------------------------------------------
1 | spring.output.ansi.enabled=always
2 |
--------------------------------------------------------------------------------
/src/test/resources/logback-test.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | %d{HH:mm:ss.SSS} [%thread] %highlight(%-5level) %cyan(%logger{15}) - %msg%n
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/uml/state_graph.puml:
--------------------------------------------------------------------------------
1 | @startuml
2 |
3 | [*] --> BACKLOG
4 |
5 | BACKLOG --> INPROGRESS : start feature
6 |
7 | INPROGRESS --> TESTING : finish feature
8 |
9 | TESTING -->DONE : QA checked UC
10 |
11 | TESTING --> INPROGRESS : QA reject
12 |
13 | DONE --> [*]
14 |
15 | BACKLOG --> TESTING : super star
16 |
17 | BACKLOG --> BACKLOG : deploy
18 | INPROGRESS --> INPROGRESS : deploy
19 |
20 | @enduml
--------------------------------------------------------------------------------