package brainslug.flow;
import brainslug.flow.builder.FlowBuilder;
import brainslug.flow.definition.FlowDefinition;
import brainslug.flow.definition.Identifier;
import brainslug.flow.node.*;
import brainslug.flow.node.event.AbstractEventDefinition;
import brainslug.flow.node.event.EndEvent;
import brainslug.flow.node.event.IntermediateEvent;
import brainslug.flow.node.event.StartEvent;
import brainslug.flow.node.event.timer.StartTimerDefinition;
import brainslug.flow.node.task.GoalDefinition;
import brainslug.flow.node.task.GoalPredicate;
import brainslug.flow.node.task.RetryStrategy;
import brainslug.flow.node.task.Task;
import brainslug.util.IdUtil;
import org.assertj.core.api.Assertions;
import org.junit.Test;
import java.util.Date;
import java.util.concurrent.TimeUnit;
import static brainslug.util.FlowDefinitionAssert.assertThat;
import static brainslug.util.ID.*;
public class FlowBuilderTest {
interface MyService {
}
@Test
public void buildStartEndFlow() {
FlowBuilder flowBuilder = new FlowBuilder() {
@Override
public void define() {
start(event(id(StartEvent))).end(event(id(End)));
}
};
assertThat(flowBuilder.getDefinition())
.hasTotalNodes(2)
.hasTotalEdges(1)
.hasNodesWithMarker(1, StartEvent.class)
.hasNodesWithMarker(1, EndEvent.class)
.hasEdge(StartEvent, End);
}
@Test
public void buildWaitEventFlow() {
FlowBuilder flowBuilder = new FlowBuilder() {
@Override
public void define() {
start(event(id(StartEvent))).waitFor(event(id(WaitEvent))).end(event(id(End)));
on(id(WaitEvent)).execute(task(id(Task6)).delegate(MyService.class));
}
};
assertThat(flowBuilder.getDefinition())
.hasTotalNodes(4)
.hasTotalEdges(3)
.hasNodesWithMarker(1, IntermediateEvent.class)
.hasEdge(StartEvent, WaitEvent)
.hasEdge(WaitEvent, End)
.hasEdge(WaitEvent, Task6);
}
@Test
public void buildSingleTaskFlow() {
FlowBuilder flowBuilder = new FlowBuilder() {
@Override
public void define() {
start(event(id(StartEvent))).execute(task(id(TestTask))).end(event(id(End)));
}
};
assertThat(flowBuilder.getDefinition())
.hasTotalNodes(3)
.hasTotalEdges(2)
.hasEdge(StartEvent, TestTask)
.hasEdge(TestTask, End);
}
@Test
public void buildTaskSequence() {
FlowBuilder flowBuilder = new FlowBuilder() {
@Override
public void define() {
start(event(id(StartEvent)))
.execute(task(id(TestTask)))
.execute(task(id(SecondTask)))
.end(event(id(End)));
}
};
assertThat(flowBuilder.getDefinition())
.hasTotalNodes(4)
.hasTotalEdges(3)
.hasNodesWithType(2, TaskDefinition.class)
.hasNodesWithType(2, AbstractEventDefinition.class)
.hasEdge(StartEvent, TestTask)
.hasEdge(TestTask, SecondTask)
.hasEdge(SecondTask, End);
}
@Test
public void mergeWorksOnFlowNodeDefinitions() {
final TaskDefinition secondTask = FlowBuilder.task(FlowBuilder.id(SecondTask));
final TaskDefinition thirdTask = FlowBuilder.task(FlowBuilder.id(ThirdTask));
final Identifier mergeId = FlowBuilder.id(Merge);
FlowBuilder flowBuilder = new FlowBuilder() {
@Override
public void define() {
start(secondTask).execute(thirdTask);
merge(mergeId, secondTask, thirdTask);
}
};
assertThat(flowBuilder.getDefinition())
.hasTotalNodes(3)
.hasNodesWithType(1, MergeDefinition.class)
.hasNodesWithType(2, TaskDefinition.class)
.hasEdge(secondTask.getId(), mergeId)
.hasEdge(thirdTask.getId(), mergeId);
}
@Test
public void joinWorksOnFlowNodeDefinitions() {
final TaskDefinition secondTask = FlowBuilder.task(FlowBuilder.id(SecondTask));
final TaskDefinition thirdTask = FlowBuilder.task(FlowBuilder.id(ThirdTask));
final Identifier joinId = FlowBuilder.id(Join);
FlowBuilder flowBuilder = new FlowBuilder() {
@Override
public void define() {
start(secondTask).execute(thirdTask);
join(joinId, secondTask, thirdTask);
}
};
assertThat(flowBuilder.getDefinition())
.hasTotalNodes(3)
.hasNodesWithType(1, JoinDefinition.class)
.hasNodesWithType(2, TaskDefinition.class)
.hasEdge(secondTask.getId(), joinId)
.hasEdge(thirdTask.getId(), joinId);
}
@Test
public void buildChoiceWithMergeFlow() {
FlowBuilder flowBuilder = new FlowBuilder() {
Integer meaningOfLife = 42;
@Override
public void define() {
start(event(id(StartEvent)))
.choice(id(Choice))
.when(eq(constant(meaningOfLife), 42))
.execute(task(id(SecondTask)))
.or()
.when(eq(constant(meaningOfLife), 43))
.execute(task(id(ThirdTask)));
merge(id(Merge), id(SecondTask), id(ThirdTask)).end(event(id(EndEvent2)));
}
};
assertThat(flowBuilder.getDefinition())
.hasTotalNodes(6)
.hasTotalEdges(6)
.hasNodesWithType(2, TaskDefinition.class)
.hasNodesWithType(2, AbstractEventDefinition.class)
.hasNodesWithType(1, ChoiceDefinition.class)
.hasNodesWithType(1, MergeDefinition.class)
.hasNodesWithMarker(1, EndEvent.class)
.hasNodesWithMarker(1, StartEvent.class)
.hasEdge(StartEvent, Choice)
.hasEdge(Choice, SecondTask)
.hasEdge(Choice, ThirdTask)
.hasEdge(SecondTask, Merge)
.hasEdge(ThirdTask, Merge)
.hasEdge(Merge, EndEvent2);
}
@Test
public void buildParallelWithJoinFlow() {
FlowBuilder flowBuilder = new FlowBuilder() {
@Override
public void define() {
start(event(id(StartEvent)))
.parallel(id(Parallel))
.execute(task(id(SecondTask)))
.and()
.execute(task(id(ThirdTask)));
join(id(Join), id(SecondTask), id(ThirdTask)).end(event(id(EndEvent2)));
}
};
assertThat(flowBuilder.getDefinition())
.hasTotalNodes(6)
.hasTotalEdges(6)
.hasNodesWithType(2, TaskDefinition.class)
.hasNodesWithType(2, AbstractEventDefinition.class)
.hasNodesWithType(1, ParallelDefinition.class)
.hasNodesWithType(1, JoinDefinition.class)
.hasNodesWithMarker(1, EndEvent.class)
.hasNodesWithMarker(1, StartEvent.class)
.hasEdge(StartEvent, Parallel)
.hasEdge(Parallel, SecondTask)
.hasEdge(Parallel, ThirdTask)
.hasEdge(SecondTask, Join)
.hasEdge(ThirdTask, Join)
.hasEdge(Join, EndEvent2);
}
@Test
public void connectNodesWithAfter() {
FlowBuilder flowBuilder = new FlowBuilder() {
@Override
public void define() {
start(event(id(StartEvent))).execute(task(id(TestTask)));
after(id(TestTask)).execute(task(id(SecondTask)));
}
};
assertThat(flowBuilder.getDefinition())
.hasTotalNodes(3)
.hasTotalEdges(2)
.hasNodesWithType(2, TaskDefinition.class)
.hasNodesWithType(1, AbstractEventDefinition.class)
.hasEdge(StartEvent, TestTask)
.hasEdge(TestTask, SecondTask);
}
@Test
public void buildComplexFlow() {
FlowBuilder flowBuilder = new FlowBuilder() {
Integer meaningOfLife = 42;
@Override
public void define() {
start(event(id(StartEvent))).execute(task(id(TestTask)));
after(id(TestTask))
.choice(id(Choice))
.when(eq(constant(meaningOfLife), 42))
.execute(task(id(SecondTask)))
.then()
.execute(task(id(FourthTask)))
.or()
.when(eq(constant(meaningOfLife), 43))
.execute(task(id(ThirdTask)))
.end(event(id(End)));
after(id(FourthTask))
.parallel(id(Parallel))
.execute(task(id(Task5))).end(event(id(EndEvent2)))
.and()
.waitFor(event(id(WaitEvent)));
on(id(WaitEvent)).execute(task(id(Task6)).delegate(MyService.class));
}
};
assertThat(flowBuilder.getDefinition())
.hasTotalNodes(12)
.hasTotalEdges(11)
.hasNodesWithType(6, TaskDefinition.class)
.hasNodesWithType(4, AbstractEventDefinition.class)
.hasNodesWithType(1, ParallelDefinition.class)
.hasNodesWithType(1, ChoiceDefinition.class)
.hasNodesWithMarker(2, EndEvent.class)
.hasNodesWithMarker(1, StartEvent.class)
.hasEdge(StartEvent, TestTask)
.hasEdge(TestTask, Choice)
.hasEdge(Choice, SecondTask)
.hasEdge(SecondTask, FourthTask)
.hasEdge(Choice, ThirdTask)
.hasEdge(ThirdTask, End)
.hasEdge(FourthTask, Parallel)
.hasEdge(Parallel, Task5)
.hasEdge(Task5, EndEvent2)
.hasEdge(Parallel, WaitEvent)
.hasEdge(WaitEvent, Task6);
}
@Test
public void buildFlowWithGoal() {
FlowDefinition goalFlow = new FlowBuilder() {
GoalDefinition taskExecutedGoal = goal(id("taskExecuted")).check(predicate(new GoalPredicate<Void>() {
@Override
public boolean isFulfilled(Void aVoid) {
return true;
}
}));
@Override
public void define() {
start(id("start"))
.execute(task(id("simpleTask"))
.retryAsync(true)
.goal(taskExecutedGoal))
.end(id("end"));
}
}.getDefinition();
TaskDefinition node = goalFlow.getNode(FlowBuilder.id("simpleTask"), TaskDefinition.class);
TaskDefinition taskNode = (TaskDefinition) node;
Assertions.assertThat(taskNode.isRetryAsync()).isTrue();
Assertions.assertThat(taskNode.getGoal().get().getId()).isEqualTo(FlowBuilder.id("taskExecuted"));
}
@Test
public void buildFlowWithInlineGoal() {
FlowDefinition goalFlow = new FlowBuilder() {
@Override
public void define() {
start(id("start"))
.execute(task(id("simpleTask"))
.goal(check(predicate(new GoalPredicate() {
@Override
public boolean isFulfilled(Object o) {
return false;
}
}))))
.end(id("end"));
}
}.getDefinition();
TaskDefinition taskNode = goalFlow.getNode(IdUtil.id("simpleTask"), TaskDefinition.class);
Assertions.assertThat(taskNode.getGoal()).isNotNull();
}
@Test
public void buildFlowWithRetryStrategy() {
FlowDefinition goalFlow = new FlowBuilder() {
@Override
public void define() {
RetryStrategy retryStrategy = new RetryStrategy() {
@Override
public Date nextRetry(long retryCount, Date baseDate) {
return new Date();
}
};
start(id("start"))
.execute(task(id("simpleTask")).retryAsync(true).retryStrategy(retryStrategy))
.end(id("end"));
}
}.getDefinition();
TaskDefinition taskNode = goalFlow.getNode(IdUtil.id("simpleTask"), TaskDefinition.class);
Assertions.assertThat(taskNode.isRetryAsync()).isTrue();
Assertions.assertThat(taskNode.getRetryStrategy()).isNotNull();
}
@Test
public void buildFlowWithRecurringTimer() {
FlowBuilder recurringTimerFlow = new FlowBuilder() {
@Override
public void define() {
flowId(id("recurringTimerFlow"));
Task callee = new Task<Void>() {
@Override
public void execute(Void o) {
}
};
start(event(id("start")), every(5, TimeUnit.SECONDS))
.execute(task(id("task"), callee));
}
};
assertThat(recurringTimerFlow.getDefinition())
.hasNodesWithMarker(1, StartEvent.class);
StartEvent startEvent = recurringTimerFlow.getDefinition().getNode(IdUtil.id("start"), EventDefinition.class).as(StartEvent.class);
Assertions.assertThat(startEvent.getStartTimerDefinition().get())
.isEqualTo(new StartTimerDefinition(5, TimeUnit.SECONDS));
}
}