package ee.telekom.workflow.facade;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import ee.telekom.workflow.TestApplicationContexts;
import ee.telekom.workflow.core.common.WorkflowEngineConfiguration;
import ee.telekom.workflow.core.node.NodeService;
import ee.telekom.workflow.core.workflowinstance.WorkflowInstanceStatus;
import ee.telekom.workflow.executor.GraphEngineFactory;
import ee.telekom.workflow.executor.WorkflowExecutor;
import ee.telekom.workflow.facade.model.CreateWorkflowInstance;
import ee.telekom.workflow.facade.model.ExecutionErrorState;
import ee.telekom.workflow.facade.model.WorkItemState;
import ee.telekom.workflow.facade.model.WorkflowInstanceState;
import ee.telekom.workflow.graph.Graph;
import ee.telekom.workflow.graph.Node;
import ee.telekom.workflow.graph.WorkItemStatus;
import ee.telekom.workflow.graph.core.GraphImpl;
import ee.telekom.workflow.graph.core.TransitionImpl;
import ee.telekom.workflow.graph.node.activity.BeanAsyncCallActivity;
import ee.telekom.workflow.graph.node.activity.HumanTaskActivity;
import ee.telekom.workflow.graph.node.event.CatchSignal;
import ee.telekom.workflow.graph.node.event.CatchTimer;
import ee.telekom.workflow.graph.node.gateway.AndFork;
import ee.telekom.workflow.graph.node.gateway.AndJoin;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = TestApplicationContexts.DEFAULT)
@DirtiesContext
public class WorkflowEngineFacadeIT extends TestApplicationContexts{
@Autowired
private WorkflowEngineFacade facade;
@Autowired
private WorkflowExecutor executor;
@Autowired
private GraphEngineFactory engineFactory;
@Autowired
private NodeService nodeService;
@Autowired
private WorkflowEngineConfiguration config;
private static final String NAME1 = "name1";
private static final int VERSION1 = 1;
private static final String NAME2 = "name2";
private static final int VERSION2 = 5;
private static final String UNKNOWN = "unknown";
private static final String SIGNAL = "go";
private static final int TIMER_MS = 600000;
private static final String BEAN = "bean1";
private static final String METHOD = "method1";
private static final String ROLE = "role1";
private static final String USER = "user1";
private static final Graph GRAPH1 = createTestGraph( NAME1, VERSION1 );
private static final Graph GRAPH2 = createTestGraph( NAME2, VERSION2 );
@Test
@DirtiesContext
public void testDeployedAndKnownWorkflowNames(){
engineFactory.getSingletonInstance().getRepository().addGraph( GRAPH1 );
engineFactory.getSingletonInstance().getRepository().addGraph( GRAPH2 );
facade.createWorkflowInstance( createRequest( UNKNOWN, null, null, null ) );
Set<String> deployedWorkflowNames = facade.getDeployedWorkflowNames();
Assert.assertEquals( 2, deployedWorkflowNames.size() );
Assert.assertTrue( deployedWorkflowNames.contains( NAME1 ) );
Assert.assertTrue( deployedWorkflowNames.contains( NAME2 ) );
Set<String> knownWorkflowNames = facade.getKnownWorkflowNames();
Assert.assertEquals( 3, knownWorkflowNames.size() );
Assert.assertTrue( knownWorkflowNames.contains( NAME1 ) );
Assert.assertTrue( knownWorkflowNames.contains( NAME2 ) );
Assert.assertTrue( knownWorkflowNames.contains( UNKNOWN ) );
}
@Test
@DirtiesContext
public void manageWorkflowIntstance(){
engineFactory.getSingletonInstance().getRepository().addGraph( GRAPH1 );
// test createWorkflowInstance
CreateWorkflowInstance request = createRequest( NAME1, null, "label1", "label2" );
facade.createWorkflowInstance( request );
Assert.assertEquals( 1, (long)request.getRefNum() );
// test findWorkflowInstance
WorkflowInstanceState woin = facade.findWorkflowInstance( 1, null );
assertInstance( woin, 1, NAME1, null, "label1", "label2", WorkflowInstanceStatus.NEW );
woin = facade.findWorkflowInstance( 1, true );
assertInstance( woin, 1, NAME1, null, "label1", "label2", WorkflowInstanceStatus.NEW );
woin = facade.findWorkflowInstance( 1, false );
Assert.assertNull( woin );
// test suspendWorkflowInstance
executor.startWorkflow( 1 );
woin = facade.findWorkflowInstance( 1, null );
assertInstance( woin, 1, NAME1, VERSION1, "label1", "label2", WorkflowInstanceStatus.EXECUTING );
facade.suspendWorkflowInstance( 1 );
woin = facade.findWorkflowInstance( 1, true );
assertInstance( woin, 1, NAME1, VERSION1, "label1", "label2", WorkflowInstanceStatus.SUSPENDED );
// test resumeWorkflowInstance
facade.resumeWorkflowInstance( 1 );
woin = facade.findWorkflowInstance( 1, true );
assertInstance( woin, 1, NAME1, VERSION1, "label1", "label2", WorkflowInstanceStatus.EXECUTING );
// test findWorkItems
List<WorkItemState> woits = facade.findWorkItems( 1, true );
Assert.assertEquals( 4, woits.size() );
assertSignal( woits.get( 3 ), 1, 1, 2, WorkItemStatus.NEW, SIGNAL );
assertTimer( woits.get( 2 ), 2, 1, 3, WorkItemStatus.NEW, false );
assertTask( woits.get( 1 ), 3, 1, 4, WorkItemStatus.NEW, BEAN, METHOD );
assertHumanTask( woits.get( 0 ), 4, 1, 5, WorkItemStatus.NEW, ROLE, null );
// test findWorkItem
WorkItemState woit1 = facade.findWorkItem( 1, true );
assertSignal( woit1, 1, 1, 2, WorkItemStatus.NEW, SIGNAL );
WorkItemState woit2 = facade.findWorkItem( 2, true );
assertTimer( woit2, 2, 1, 3, WorkItemStatus.NEW, false );
WorkItemState woit3 = facade.findWorkItem( 3, true );
assertTask( woit3, 3, 1, 4, WorkItemStatus.NEW, BEAN, METHOD );
WorkItemState woit4 = facade.findWorkItem( 4, true );
assertHumanTask( woit4, 4, 1, 5, WorkItemStatus.NEW, ROLE, null );
// test findActiveWorkItemByTokenId
woit1 = facade.findActiveWorkItemByTokenId( 1, 2 );
assertSignal( woit1, 1, 1, 2, WorkItemStatus.NEW, SIGNAL );
woit2 = facade.findActiveWorkItemByTokenId( 1, 3 );
assertTimer( woit2, 2, 1, 3, WorkItemStatus.NEW, false );
woit3 = facade.findActiveWorkItemByTokenId( 1, 4 );
assertTask( woit3, 3, 1, 4, WorkItemStatus.NEW, BEAN, METHOD );
woit4 = facade.findActiveWorkItemByTokenId( 1, 5 );
assertHumanTask( woit4, 4, 1, 5, WorkItemStatus.NEW, ROLE, null );
// test findActiveHumanTasksByRoleAndUser
List<WorkItemState> humanTasks = facade.findActiveHumanTasksByRoleAndUser( ROLE, null );
Assert.assertEquals( 1, humanTasks.size() );
assertHumanTask( humanTasks.get( 0 ), 4, 1, 5, WorkItemStatus.NEW, ROLE, null );
// test findActiveHumanTasksByRole
humanTasks = facade.findActiveHumanTasksByRole( ROLE );
Assert.assertEquals( 1, humanTasks.size() );
assertHumanTask( humanTasks.get( 0 ), 4, 1, 5, WorkItemStatus.NEW, ROLE, null );
// test findActiveHumanTasksByUser
humanTasks = facade.findActiveHumanTasksByUser( USER );
Assert.assertEquals( 0, humanTasks.size() );
// test sendSignalToWorkflowInstance()
facade.sendSignalToWorkflowInstance( 1, SIGNAL, null );
woit1 = facade.findWorkItem( 1, null );
assertSignal( woit1, 1, 1, 2, WorkItemStatus.EXECUTED, SIGNAL );
// test skipTimer()
facade.skipTimer( 2 );
woit2 = facade.findWorkItem( 2, null );
assertTimer( woit2, 2, 1, 3, WorkItemStatus.NEW, true );
// test findExecutionError
executor.executeTask( 1, 3 );
ExecutionErrorState error = facade.findExecutionError( 1 );
Assert.assertNotNull( error );
Assert.assertEquals( 1, (long)error.getWoinRefNum() );
Assert.assertEquals( 3, (long)error.getWoitRefNum() );
woin = facade.findWorkflowInstance( 1, true );
assertInstance( woin, 1, NAME1, VERSION1, "label1", "label2", WorkflowInstanceStatus.EXECUTING_ERROR );
// test retryWorkflowInstance
facade.retryWorkflowInstance( 1 );
error = facade.findExecutionError( 1 );
Assert.assertNull( error );
woin = facade.findWorkflowInstance( 1, true );
assertInstance( woin, 1, NAME1, VERSION1, "label1", "label2", WorkflowInstanceStatus.EXECUTING );
// test submitTask
facade.submitTask( 3, null );
woit3 = facade.findWorkItem( 3, null );
assertTask( woit3, 3, 1, 4, WorkItemStatus.EXECUTED, BEAN, METHOD );
// test assignHumanTask
facade.assignHumanTask( 4, USER );
woit4 = facade.findWorkItem( 4, null );
assertHumanTask( woit4, 4, 1, 5, WorkItemStatus.NEW, ROLE, USER );
facade.assignHumanTask( 4, null );
woit4 = facade.findWorkItem( 4, null );
assertHumanTask( woit4, 4, 1, 5, WorkItemStatus.NEW, ROLE, null );
facade.assignHumanTask( 4, USER );
// test again findActiveHumanTasksByRoleAndUser
humanTasks = facade.findActiveHumanTasksByRoleAndUser( ROLE, USER );
Assert.assertEquals( 1, humanTasks.size() );
assertHumanTask( humanTasks.get( 0 ), 4, 1, 5, WorkItemStatus.NEW, ROLE, USER );
// test again findActiveHumanTasksByRole
humanTasks = facade.findActiveHumanTasksByRole( ROLE );
Assert.assertEquals( 1, humanTasks.size() );
assertHumanTask( humanTasks.get( 0 ), 4, 1, 5, WorkItemStatus.NEW, ROLE, USER );
// test again findActiveHumanTasksByUser
humanTasks = facade.findActiveHumanTasksByUser( USER );
Assert.assertEquals( 1, humanTasks.size() );
assertHumanTask( humanTasks.get( 0 ), 4, 1, 5, WorkItemStatus.NEW, ROLE, USER );
// test submitHumanTask
facade.submitHumanTask( 4, null );
woit4 = facade.findWorkItem( 4, null );
assertHumanTask( woit4, 4, 1, 5, WorkItemStatus.EXECUTED, ROLE, USER );
humanTasks = facade.findActiveHumanTasksByRoleAndUser( ROLE, USER );
Assert.assertEquals( 0, humanTasks.size() );
humanTasks = facade.findActiveHumanTasksByRole( ROLE );
Assert.assertEquals( 0, humanTasks.size() );
humanTasks = facade.findActiveHumanTasksByUser( USER );
Assert.assertEquals( 0, humanTasks.size() );
// Testing abortWorkflowInstance
facade.abortWorkflowInstance( 1 );
woin = facade.findWorkflowInstance( 1, true );
assertInstance( woin, 1, NAME1, VERSION1, "label1", "label2", WorkflowInstanceStatus.ABORT );
executor.abortWorkflow( 1 );
woin = facade.findWorkflowInstance( 1, false );
assertInstance( woin, 1, NAME1, VERSION1, "label1", "label2", WorkflowInstanceStatus.ABORTED );
// test again findActiveWorkItemByTokenId
Assert.assertNull( facade.findActiveWorkItemByTokenId( 1, 2 ) );
Assert.assertNull( facade.findActiveWorkItemByTokenId( 1, 3 ) );
Assert.assertNull( facade.findActiveWorkItemByTokenId( 1, 4 ) );
Assert.assertNull( facade.findActiveWorkItemByTokenId( 1, 5 ) );
}
@Test
@DirtiesContext
public void testSendSignals(){
engineFactory.getSingletonInstance().getRepository().addGraph( GRAPH1 );
engineFactory.getSingletonInstance().getRepository().addGraph( GRAPH2 );
CreateWorkflowInstance request1 = createRequest( NAME1, null, "1", null );
CreateWorkflowInstance request2 = createRequest( NAME1, VERSION1, "1", null );
CreateWorkflowInstance request3 = createRequest( NAME2, null, "2", null );
// test createWorkflowInstances
facade.createWorkflowInstances( Arrays.asList( request1, request2, request3 ) );
Assert.assertEquals( 1, (long)request1.getRefNum() );
Assert.assertEquals( 2, (long)request2.getRefNum() );
Assert.assertEquals( 3, (long)request3.getRefNum() );
assertInstance( facade.findWorkflowInstance( 1, true ), 1, NAME1, null, "1", null, WorkflowInstanceStatus.NEW );
assertInstance( facade.findWorkflowInstance( 2, true ), 2, NAME1, VERSION1, "1", null, WorkflowInstanceStatus.NEW );
assertInstance( facade.findWorkflowInstance( 3, true ), 3, NAME2, null, "2", null, WorkflowInstanceStatus.NEW );
// test sendSignalToWorkItem
executor.startWorkflow( 1 );
assertInstance( facade.findWorkflowInstance( 1, true ), 1, NAME1, VERSION1, "1", null, WorkflowInstanceStatus.EXECUTING );
assertSignal( facade.findWorkItem( 1, true ), 1, 1, 2, WorkItemStatus.NEW, SIGNAL );
facade.sendSignalToWorkItem( 1, SIGNAL, null );
assertSignal( facade.findWorkItem( 1, true ), 1, 1, 2, WorkItemStatus.EXECUTED, SIGNAL );
// test sendSignalByLabel1
executor.startWorkflow( 2 );
assertInstance( facade.findWorkflowInstance( 2, true ), 2, NAME1, VERSION1, "1", null, WorkflowInstanceStatus.EXECUTING );
assertSignal( facade.findWorkItem( 5, true ), 5, 2, 2, WorkItemStatus.NEW, SIGNAL );
facade.sendSignalByLabel1( "1", SIGNAL, null );
assertSignal( facade.findWorkItem( 5, true ), 5, 2, 2, WorkItemStatus.EXECUTED, SIGNAL );
// test sendSignalByLabels
executor.startWorkflow( 3 );
assertInstance( facade.findWorkflowInstance( 3, true ), 3, NAME2, VERSION2, "2", null, WorkflowInstanceStatus.EXECUTING );
assertSignal( facade.findWorkItem( 9, true ), 9, 3, 2, WorkItemStatus.NEW, SIGNAL );
facade.sendSignalByLabels( "2", null, SIGNAL, null );
assertSignal( facade.findWorkItem( 9, true ), 9, 3, 2, WorkItemStatus.EXECUTED, SIGNAL );
facade.sendSignalByLabels( "x", "y", SIGNAL, null );
}
@Test
@DirtiesContext
public void testCreateWorkflowInstancesAndFindByLabel(){
CreateWorkflowInstance request1 = createRequest( NAME1, null, "1", null );
CreateWorkflowInstance request2 = createRequest( NAME1, VERSION1, "1", null );
CreateWorkflowInstance request3 = createRequest( NAME2, null, "2", null );
CreateWorkflowInstance request4 = createRequest( NAME2, VERSION2, "2", null );
CreateWorkflowInstance request5 = createRequest( UNKNOWN, null, "3", null );
// test createWorkflowInstances
facade.createWorkflowInstances( Arrays.asList( request1, request2, request3, request4, request5 ) );
Assert.assertEquals( 1, (long)request1.getRefNum() );
Assert.assertEquals( 2, (long)request2.getRefNum() );
Assert.assertEquals( 3, (long)request3.getRefNum() );
Assert.assertEquals( 4, (long)request4.getRefNum() );
Assert.assertEquals( 5, (long)request5.getRefNum() );
assertInstance( facade.findWorkflowInstance( 1, true ), 1, NAME1, null, "1", null, WorkflowInstanceStatus.NEW );
assertInstance( facade.findWorkflowInstance( 2, true ), 2, NAME1, VERSION1, "1", null, WorkflowInstanceStatus.NEW );
assertInstance( facade.findWorkflowInstance( 3, true ), 3, NAME2, null, "2", null, WorkflowInstanceStatus.NEW );
assertInstance( facade.findWorkflowInstance( 4, true ), 4, NAME2, VERSION2, "2", null, WorkflowInstanceStatus.NEW );
assertInstance( facade.findWorkflowInstance( 5, true ), 5, UNKNOWN, null, "3", null, WorkflowInstanceStatus.NEW );
// test findWorkflowInstancesByLabels
Assert.assertEquals( 2, facade.findWorkflowInstancesByLabels( "1", null, false ).size() );
Assert.assertEquals( 2, facade.findWorkflowInstancesByLabels( "1", null, false ).size() );
Assert.assertEquals( 2, facade.findWorkflowInstancesByLabel1( "1", false ).size() );
Assert.assertEquals( 2, facade.findWorkflowInstancesByLabel1( "1", false ).size() );
// test abortWorkflowInstance
facade.abortWorkflowInstance( 5 );
executor.abortWorkflow( 5 );
Assert.assertEquals( 1, facade.findWorkflowInstancesByLabels( "3", null, false ).size() );
Assert.assertEquals( 0, facade.findWorkflowInstancesByLabels( "3", null, true ).size() );
}
@Test
@DirtiesContext
public void testNextActiveTimerDueDates(){
engineFactory.getSingletonInstance().getRepository().addGraph( GRAPH1 );
facade.createWorkflowInstance( createRequest( NAME1, null, "1", null ) );
Assert.assertEquals( 0, facade.getNextActiveTimerDueDates( Collections.singletonList( 1l ) ).size() );
executor.startWorkflow( 1 );
Assert.assertEquals( 1, facade.getNextActiveTimerDueDates( Collections.singletonList( 1l ) ).size() );
facade.skipTimer( 2 );
Assert.assertEquals( 1, facade.getNextActiveTimerDueDates( Collections.singletonList( 1l ) ).size() );
executor.completeWorkItem( 1, 2 );
Assert.assertEquals( 0, facade.getNextActiveTimerDueDates( Collections.singletonList( 1l ) ).size() );
}
@Test
@DirtiesContext
public void testWorkflowInstancesWithHumanTasks(){
engineFactory.getSingletonInstance().getRepository().addGraph( GRAPH1 );
facade.createWorkflowInstance( createRequest( NAME1, null, "1", null ) );
debug();
Assert.assertEquals( 0, facade.getWorkflowInstancesWithActiveHumanTask( Collections.singletonList( 1l ) ).size() );
executor.startWorkflow( 1 );
debug();
Assert.assertEquals( 1, facade.getWorkflowInstancesWithActiveHumanTask( Collections.singletonList( 1l ) ).size() );
facade.submitHumanTask( 4, null );
debug();
Assert.assertEquals( 0, facade.getWorkflowInstancesWithActiveHumanTask( Collections.singletonList( 1l ) ).size() );
executor.completeWorkItem( 1, 4 );
debug();
Assert.assertEquals( 0, facade.getWorkflowInstancesWithActiveHumanTask( Collections.singletonList( 1l ) ).size() );
}
private void debug(){
System.out.println( "===============================================" );
for( WorkItemState woit : facade.findWorkItems( 1, true ) ){
System.out.println( ToStringBuilder.reflectionToString( woit ) );
}
}
/**
* <pre>
* +--[signal]--+
* [AND]--[timer]---[AND]
* +--[task]----+
* +--[htask]---+
* </pre>
*/
private static Graph createTestGraph( String name, int version ){
GraphImpl graph = new GraphImpl( name, version );
Node fork = new AndFork( 1 );
Node signal = new CatchSignal( 2, SIGNAL );
Node timer = new CatchTimer( 3, TIMER_MS );
Node task = new BeanAsyncCallActivity( 4, BEAN, METHOD, null, null );
Node htask = new HumanTaskActivity( 5, ROLE, null, null, null );
Node join = new AndJoin( -1 );
graph.setStartNode( fork );
graph.addNode( signal );
graph.addNode( timer );
graph.addNode( task );
graph.addNode( htask );
graph.addNode( join );
graph.addTransition( new TransitionImpl( "1_2", fork, signal ) );
graph.addTransition( new TransitionImpl( "1_3", fork, timer ) );
graph.addTransition( new TransitionImpl( "1_4", fork, task ) );
graph.addTransition( new TransitionImpl( "1_5", fork, htask ) );
graph.addTransition( new TransitionImpl( signal, join ) );
graph.addTransition( new TransitionImpl( timer, join ) );
graph.addTransition( new TransitionImpl( task, join ) );
graph.addTransition( new TransitionImpl( htask, join ) );
return graph;
}
private CreateWorkflowInstance createRequest( String name, Integer version, String label1, String label2 ){
CreateWorkflowInstance request = new CreateWorkflowInstance();
request.setWorkflowName( name );
request.setWorkflowVersion( version );
request.setLabel1( label1 );
request.setLabel2( label2 );
return request;
}
private void assertInstance( WorkflowInstanceState woin,
long refNum,
String name,
Integer version,
String label1,
String label2,
WorkflowInstanceStatus status ){
Assert.assertEquals( refNum, (long)woin.getRefNum() );
Assert.assertEquals( name, woin.getWorkflowName() );
Assert.assertEquals( version, woin.getWorkflowVersion() );
Assert.assertEquals( label1, woin.getLabel1() );
Assert.assertEquals( label2, woin.getLabel2() );
Assert.assertEquals( status.name(), woin.getStatus() );
}
private void assertSignal( WorkItemState woit, long refNum, long woinRefNum, int tokenId, WorkItemStatus status, String signal ){
assertCommon( woit, refNum, woinRefNum, tokenId, status );
Assert.assertEquals( signal, woit.getSignal() );
}
private void assertTimer( WorkItemState woit, long refNum, long woinRefNum, int tokenId, WorkItemStatus status, boolean isDue ){
assertCommon( woit, refNum, woinRefNum, tokenId, status );
Assert.assertEquals( isDue, woit.getDueDate().getTime() - System.currentTimeMillis() <= 0 );
}
private void assertTask( WorkItemState woit, long refNum, long woinRefNum, int tokenId, WorkItemStatus status, String bean, String method ){
assertCommon( woit, refNum, woinRefNum, tokenId, status );
Assert.assertEquals( bean, woit.getBean() );
Assert.assertEquals( method, woit.getMethod() );
}
private void assertHumanTask( WorkItemState woit, long refNum, long woinRefNum, int tokenId, WorkItemStatus status, String role, String user ){
assertCommon( woit, refNum, woinRefNum, tokenId, status );
Assert.assertEquals( role, woit.getRole() );
Assert.assertEquals( user, woit.getUserName() );
}
private void assertCommon( WorkItemState woit, long refNum, long woinRefNum, int tokenId, WorkItemStatus status ){
Assert.assertEquals( refNum, (long)woit.getRefNum() );
Assert.assertEquals( woinRefNum, (long)woit.getWoinRefNum() );
Assert.assertEquals( tokenId, woit.getTokenId() );
Assert.assertEquals( status.name(), woit.getStatus() );
}
@Before
public void prepareTest(){
nodeService.findOrCreateByName( config.getNodeName() );
}
}