/** * Copyright (c) 2014 - 2017 Frank Appel * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Frank Appel - initial API and implementation */ package com.codeaffine.workflow.internal; import static com.codeaffine.test.util.lang.ThrowableCaptor.thrownBy; import static com.codeaffine.workflow.internal.DecisionVerificator.ERROR_UNREACHABLE_NODE; import static com.codeaffine.workflow.internal.FlowProcessor.ERROR_ILLEGAL_MOVE; import static com.codeaffine.workflow.internal.FlowProcessor.ERROR_UNAVAILABLE_OPERATION; import static com.codeaffine.workflow.test.util.FlowEventLog.$; import static com.codeaffine.workflow.test.util.FlowEventLog.expectedLog; import static com.codeaffine.workflow.test.util.FlowEventLogEntry.EventType.ON_NODE_ENTER; import static com.codeaffine.workflow.test.util.FlowEventLogEntry.EventType.ON_NODE_LEAVE; import static com.codeaffine.workflow.test.util.NodeDefinitionAssert.assertThat; import static com.codeaffine.workflow.test.util.WorkflowDefinitionHelper.DECISION_ID; import static com.codeaffine.workflow.test.util.WorkflowDefinitionHelper.OPERATION_ID; import static com.codeaffine.workflow.test.util.WorkflowDefinitionHelper.OPERATION_ID_1; import static com.codeaffine.workflow.test.util.WorkflowDefinitionHelper.OPERATION_ID_2; import static java.lang.String.format; import static org.assertj.core.api.Assertions.assertThat; import org.junit.Before; import org.junit.Test; import com.codeaffine.workflow.NodeDefinition; import com.codeaffine.workflow.persistence.FlowProcessorMemento; import com.codeaffine.workflow.test.util.FlowEventLog; import com.codeaffine.workflow.test.util.WorkflowDefinitionHelper.TestActivity; import com.codeaffine.workflow.test.util.WorkflowDefinitionHelper.TestDecision; import com.codeaffine.workflow.test.util.WorkflowDefinitionHelper.TestTask; public class FlowProcessorTest { private WorkflowDefinitionImpl definition; private WorkflowContextImpl context; private FlowEventNotifier notifier; private FlowProcessor flowProcessor; @Before public void setUp() { definition = new WorkflowDefinitionImpl(); notifier = new FlowEventNotifier(); context = new WorkflowContextImpl(); flowProcessor = createFlowProcessor(); } @Test public void move() { definition.addActivity( OPERATION_ID, TestActivity.class, null ); definition.setStart( OPERATION_ID ); flowProcessor.move(); NodeDefinition actual = flowProcessor.acquire(); assertThat( actual ) .hasNodeId( OPERATION_ID ) .hasType( TestActivity.class ) .hasSuccessors(); } @Test public void moveWithDecision() { definition.addDecision( DECISION_ID, TestDecision.class, OPERATION_ID, OPERATION_ID_1 ); definition.addActivity( OPERATION_ID, TestActivity.class, null ); definition.setStart( DECISION_ID ); flowProcessor.move(); NodeDefinition actual = flowProcessor.acquire(); assertThat( actual ) .hasNodeId( OPERATION_ID ) .hasType( TestActivity.class ) .hasSuccessors(); } @Test public void moveWithDecisionToUnreachableNode() { definition.addDecision( DECISION_ID, TestDecision.class, OPERATION_ID_1, OPERATION_ID_2 ); definition.addActivity( OPERATION_ID, TestActivity.class, null ); definition.setStart( DECISION_ID ); Throwable actual = thrownBy( () -> flowProcessor.move() ); assertThat( actual ) .hasMessage( format( ERROR_UNREACHABLE_NODE, OPERATION_ID, DECISION_ID ) ) .isInstanceOf( IllegalStateException.class ); } @Test public void moveRepeatedly() { definition.addActivity( OPERATION_ID_1, TestActivity.class, DECISION_ID ); definition.addDecision( DECISION_ID, TestDecision.class, OPERATION_ID, OPERATION_ID_1 ); definition.addActivity( OPERATION_ID, TestActivity.class, null ); definition.setStart( OPERATION_ID_1 ); flowProcessor.move(); flowProcessor.acquire(); flowProcessor.move(); NodeDefinition actual = flowProcessor.acquire(); assertThat( actual ) .hasNodeId( OPERATION_ID ) .hasType( TestActivity.class ) .hasSuccessors(); } @Test public void moveOnUnacquiredOperation() { definition.addActivity( OPERATION_ID, TestActivity.class, null ); definition.setStart( OPERATION_ID ); flowProcessor.move(); Throwable actual = thrownBy( () -> flowProcessor.move() ); assertThat( actual ) .isInstanceOf( IllegalStateException.class ) .hasMessage( ERROR_ILLEGAL_MOVE ); } @Test public void isAvailable() { definition.addActivity( OPERATION_ID, TestActivity.class, null ); definition.setStart( OPERATION_ID ); flowProcessor.move(); assertThat( flowProcessor.isAvailable() ).isTrue(); } @Test public void isAvailableAfterAcquire() { definition.addActivity( OPERATION_ID, TestActivity.class, null ); definition.setStart( OPERATION_ID ); flowProcessor.move(); flowProcessor.acquire(); assertThat( flowProcessor.isAvailable() ).isFalse(); } @Test public void isAvailableBeforeInitialization() { definition.addActivity( OPERATION_ID, TestActivity.class, null ); definition.setStart( OPERATION_ID ); assertThat( flowProcessor.isAvailable() ).isFalse(); } @Test public void isAvailableAfterMoveToEnd() { definition.addActivity( OPERATION_ID, TestActivity.class, null ); definition.setStart( OPERATION_ID ); flowProcessor.move(); flowProcessor.acquire(); flowProcessor.move(); assertThat( flowProcessor.isAvailable() ).isFalse(); } @Test public void acquireOnUnavailableOperation() { Throwable actual = thrownBy( () -> flowProcessor.acquire() ); assertThat( actual ) .isInstanceOf( IllegalStateException.class ) .hasMessage( ERROR_UNAVAILABLE_OPERATION ); } @Test public void flowNotifications() { definition.addTask( OPERATION_ID_1, TestTask.class, DECISION_ID ); definition.addDecision( DECISION_ID, TestDecision.class, OPERATION_ID, OPERATION_ID_1 ); definition.addTask( OPERATION_ID, TestTask.class, null ); definition.setStart( OPERATION_ID_1 ); FlowEventLog flowEventLog = FlowEventLog.registerFlowEventLog( notifier ); flowProcessor.move(); flowProcessor.acquire(); flowProcessor.move(); flowProcessor.acquire(); flowProcessor.move(); assertThat( flowEventLog.getContent() ).isEqualTo( expectedLog( $( ON_NODE_ENTER, OPERATION_ID_1 ), $( ON_NODE_LEAVE, OPERATION_ID_1 ), $( ON_NODE_ENTER, DECISION_ID ), $( ON_NODE_LEAVE, DECISION_ID ), $( ON_NODE_ENTER, OPERATION_ID ), $( ON_NODE_LEAVE, OPERATION_ID ) ) ); } @Test public void saveAndRestore() { definition.addActivity( OPERATION_ID, TestActivity.class, null ); definition.setStart( OPERATION_ID ); flowProcessor.move(); FlowProcessorMemento memento = flowProcessor.save(); FlowProcessor newFlowProcessor = createFlowProcessor(); newFlowProcessor.restore( memento ); NodeDefinition actual = newFlowProcessor.acquire(); assertThat( actual ) .hasNodeId( OPERATION_ID ) .hasType( TestActivity.class ) .hasSuccessors(); } private FlowProcessor createFlowProcessor() { return new FlowProcessor( new NodeLoaderImpl(), notifier, context, definition ); } }