package com.sequenceiq.cloudbreak.core.flow2.config; import static org.junit.Assert.assertEquals; import static org.mockito.BDDMockito.given; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import java.util.List; import org.junit.Before; import org.junit.Test; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.springframework.context.ApplicationContext; import org.springframework.core.task.SyncTaskExecutor; import org.springframework.messaging.Message; import org.springframework.statemachine.config.builders.StateMachineConfigurationBuilder; import org.springframework.statemachine.config.builders.StateMachineStateBuilder; import org.springframework.statemachine.config.builders.StateMachineTransitionBuilder; import org.springframework.statemachine.config.common.annotation.ObjectPostProcessor; import org.springframework.statemachine.listener.StateMachineListener; import org.springframework.statemachine.listener.StateMachineListenerAdapter; import com.sequenceiq.cloudbreak.core.flow2.AbstractAction; import com.sequenceiq.cloudbreak.core.flow2.Flow; import com.sequenceiq.cloudbreak.core.flow2.FlowEvent; import com.sequenceiq.cloudbreak.core.flow2.FlowFinalizeAction; import com.sequenceiq.cloudbreak.core.flow2.FlowState; public class AbstractFlowConfigurationTest { @InjectMocks private FlowConfiguration underTest; @Mock private ApplicationContext applicationContext; @Mock private AbstractAction<State, Event, ?, ?> action; private Flow flow; private List<FlowConfiguration.Transition<State, Event>> transitions; private FlowConfiguration.FlowEdgeConfig<State, Event> edgeConfig; @Before public void setup() throws Exception { underTest = new FlowConfiguration(); MockitoAnnotations.initMocks(this); given(applicationContext.getBean(anyString(), any(Class.class))).willReturn(action); transitions = new AbstractFlowConfiguration.Transition.Builder<State, Event>() .defaultFailureEvent(Event.FAILURE) .from(State.INIT).to(State.DO).event(Event.START).noFailureEvent() .from(State.DO).to(State.DO2).event(Event.CONTINUE).defaultFailureEvent() .from(State.DO2).to(State.FINISH).event(Event.FINISHED).failureState(State.FAILED2).failureEvent(Event.FAILURE2) .from(State.FINISH).to(State.FINAL).event(Event.FINALIZED).defaultFailureEvent() .build(); edgeConfig = new FlowConfiguration.FlowEdgeConfig(State.INIT, State.FINAL, State.FAILED, Event.FAIL_HANDLED); underTest.init(); verify(applicationContext, times(8)).getBean(anyString(), any(Class.class)); flow = underTest.createFlow("flowId"); flow.initialize(); } @Test public void testHappyFlowConfiguration() { flow.sendEvent(Event.START.name(), null); flow.sendEvent(Event.CONTINUE.name(), null); flow.sendEvent(Event.FINISHED.name(), null); flow.sendEvent(Event.FINALIZED.name(), null); } @Test public void testUnhappyFlowConfigurationWithDefaultFailureHandler() { flow.sendEvent(Event.START.name(), null); flow.sendEvent(Event.FAILURE.name(), null); flow.sendEvent(Event.FAIL_HANDLED.name(), null); } @Test public void testUnhappyFlowConfigurationWithCustomFailureHandler() { flow.sendEvent(Event.START.name(), null); flow.sendEvent(Event.CONTINUE.name(), null); flow.sendEvent(Event.FAILURE2.name(), null); assertEquals("Must be on the FAILED2 state", State.FAILED2, flow.getCurrentState()); flow.sendEvent(Event.FAIL_HANDLED.name(), null); } @Test(expected = FlowConfiguration.NotAcceptedException.class) public void testUnacceptedFlowConfiguration1() { flow.sendEvent(Event.START.name(), null); flow.sendEvent(Event.FINISHED.name(), null); } @Test(expected = FlowConfiguration.NotAcceptedException.class) public void testUnacceptedFlowConfiguration2() { flow.sendEvent(Event.START.name(), null); flow.sendEvent(Event.FAILURE2.name(), null); } @Test(expected = FlowConfiguration.NotAcceptedException.class) public void testUnacceptedFlowConfiguration3() { flow.sendEvent(Event.START.name(), null); flow.sendEvent(Event.CONTINUE.name(), null); flow.sendEvent(Event.FAIL_HANDLED.name(), null); } @Test(expected = FlowConfiguration.NotAcceptedException.class) public void testUnacceptedFlowConfiguration4() { flow.sendEvent(Event.START.name(), null); flow.sendEvent(Event.CONTINUE.name(), null); flow.sendEvent(Event.FAILURE.name(), null); } enum State implements FlowState { INIT, DO, DO2, FINISH, FAILED, FAILED2, FINAL; @Override public Class<? extends AbstractAction> action() { return FlowFinalizeAction.class; } } enum Event implements FlowEvent { START, CONTINUE, FINISHED, FAILURE, FAILURE2, FINALIZED, FAIL_HANDLED; public String event() { return name(); } } class FlowConfiguration extends AbstractFlowConfiguration<State, Event> { private FlowConfiguration() { super(State.class, Event.class); } @Override protected FlowConfiguration.MachineConfiguration<State, Event> getStateMachineConfiguration() { StateMachineConfigurationBuilder<State, Event> configurationBuilder = new StateMachineConfigurationBuilder<>(ObjectPostProcessor.QUIESCENT_POSTPROCESSOR, true); StateMachineStateBuilder<State, Event> stateBuilder = new StateMachineStateBuilder<>(ObjectPostProcessor.QUIESCENT_POSTPROCESSOR, true); StateMachineTransitionBuilder<State, Event> transitionBuilder = new StateMachineTransitionBuilder<>(ObjectPostProcessor.QUIESCENT_POSTPROCESSOR, true); StateMachineListener<State, Event> listener = new StateMachineListenerAdapter<State, Event>() { @Override public void eventNotAccepted(Message<Event> event) { throw new NotAcceptedException(); } }; return new FlowConfiguration.MachineConfiguration<>(configurationBuilder, stateBuilder, transitionBuilder, listener, new SyncTaskExecutor()); } @Override protected List<Transition<State, Event>> getTransitions() { return transitions; } @Override protected FlowEdgeConfig<State, Event> getEdgeConfig() { return edgeConfig; } @Override public Event[] getEvents() { return new Event[0]; } @Override public Event[] getInitEvents() { return new Event[]{ Event.START }; } class NotAcceptedException extends RuntimeException { } } }