Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
gclaussn committed May 24, 2024
1 parent 12e53d2 commit 9d2cf5b
Show file tree
Hide file tree
Showing 15 changed files with 712 additions and 54 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -137,4 +137,13 @@ protected boolean isProcessStart() {
protected boolean isSignalStart() {
return false;
}

/**
* Determines if the test case's start element is a timer start event or not. This method returns {@code false}, if not overridden.
*
* @return {@code true}, if the test case's start element is a timer start event. Otherwise {@code false}.
*/
protected boolean isTimerStart() {
return false;
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package org.camunda.community.bpmndt.api;

import java.time.Duration;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.stream.StreamSupport;

Expand All @@ -30,6 +32,7 @@
import io.camunda.zeebe.protocol.record.value.MessageStartEventSubscriptionRecordValue;
import io.camunda.zeebe.protocol.record.value.ProcessEventRecordValue;
import io.camunda.zeebe.protocol.record.value.SignalSubscriptionRecordValue;
import io.camunda.zeebe.protocol.record.value.TimerRecordValue;

/**
* Fluent API to prepare and start the actual test case execution.
Expand Down Expand Up @@ -121,7 +124,7 @@ public long execute() {
if (testCase.isMessageStart()) {
// handle message start event
var publishMessageCommandStep3 = client.newPublishMessageCommand()
.messageName(findMessageName(processDefinitionKey))
.messageName(findStartMessageName(processDefinitionKey))
.correlationKey(String.format("%s.%s", testCase.testClass.getSimpleName(), testCase.testMethodName));

if (variables != null) {
Expand All @@ -141,8 +144,7 @@ public long execute() {
processInstanceKey = findProcessInstanceKey(publishMessageResponse);
} else if (testCase.isSignalStart()) {
// handle signal start event
var broadcastSignalCommandStep2 = client.newBroadcastSignalCommand()
.signalName(findSignalName(processDefinitionKey));
var broadcastSignalCommandStep2 = client.newBroadcastSignalCommand().signalName(findStartSignalName(processDefinitionKey));

if (variables != null) {
broadcastSignalCommandStep2 = broadcastSignalCommandStep2.variables(variables);
Expand All @@ -154,7 +156,24 @@ public long execute() {
broadcastSignalCommandStep2.send().join();

// find key of created process instance
processInstanceKey = findProcessInstanceKey();
processInstanceKey = findProcessInstanceKey(processDefinitionKey);
} else if (testCase.isTimerStart()) {
// handle timer start event
if (variables != null || !variableMap.isEmpty()) {
throw new IllegalStateException("not possible to create a process instance with variables, using a timer start event");
}

var startTimerDueDate = findStartTimerDueDate(processDefinitionKey);
engine.increaseTime(Duration.ofMillis(startTimerDueDate - System.currentTimeMillis()));

try {
TimeUnit.SECONDS.sleep(1L);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}

// find key of created process instance
processInstanceKey = findProcessInstanceKey(processDefinitionKey);
} else {
// handle none start event
var createProcessInstanceCommandStep3 = client.newCreateInstanceCommand()
Expand Down Expand Up @@ -374,24 +393,6 @@ void executeTestCase(ZeebeClient client, long processInstanceKey) {
}
}

String findMessageName(long processDefinitionKey) {
String messageName = null;

for (Record<MessageStartEventSubscriptionRecordValue> record : BpmnAssert.getRecordStream().messageStartEventSubscriptionRecords()) {
var recordValue = record.getValue();

if (recordValue.getProcessDefinitionKey() == processDefinitionKey) {
messageName = recordValue.getMessageName();
}
}

if (messageName == null) {
throw new RuntimeException("failed to find message name of message start event");
}

return messageName;
}

long findProcessDefinitionKey(DeploymentEvent deploymentEvent) {
return deploymentEvent.getProcesses().stream()
.filter(process -> process.getBpmnProcessId().equals(testCase.getBpmnProcessId()))
Expand All @@ -400,14 +401,14 @@ long findProcessDefinitionKey(DeploymentEvent deploymentEvent) {
.orElseThrow();
}

long findProcessInstanceKey() {
long findProcessInstanceKey(long processDefinitionKey) {
for (Record<?> record : BpmnAssert.getRecordStream().records()) {
if (record.getValueType() != ValueType.PROCESS_EVENT) {
continue;
}

var recordValue = (ProcessEventRecordValue) record.getValue();
if (recordValue.getTargetElementId().equals(testCase.getStart())) {
if (recordValue.getProcessDefinitionKey() == processDefinitionKey && recordValue.getTargetElementId().equals(testCase.getStart())) {
return recordValue.getProcessInstanceKey();
}
}
Expand All @@ -425,24 +426,39 @@ long findProcessInstanceKey(PublishMessageResponse publishMessageResponse) {
throw new RuntimeException("failed to find process instance key for message start");
}

String findSignalName(long processDefinitionKey) {
String signalName = null;
String findStartMessageName(long processDefinitionKey) {
for (Record<MessageStartEventSubscriptionRecordValue> record : BpmnAssert.getRecordStream().messageStartEventSubscriptionRecords()) {
var recordValue = record.getValue();

if (recordValue.getProcessDefinitionKey() == processDefinitionKey && recordValue.getStartEventId().equals(testCase.getStart())) {
return recordValue.getMessageName();
}
}
throw new RuntimeException(String.format("failed to find message name of message start event %s", testCase.getStart()));
}

String findStartSignalName(long processDefinitionKey) {
for (Record<?> record : BpmnAssert.getRecordStream().records()) {
if (record.getValueType() != ValueType.SIGNAL_SUBSCRIPTION) {
continue;
}

var recordValue = (SignalSubscriptionRecordValue) record.getValue();
if (recordValue.getProcessDefinitionKey() == processDefinitionKey) {
signalName = recordValue.getSignalName();
if (recordValue.getProcessDefinitionKey() == processDefinitionKey && recordValue.getCatchEventId().equals(testCase.getStart())) {
return recordValue.getSignalName();
}
}
throw new RuntimeException(String.format("failed to find signal name of signal start event %s", testCase.getStart()));
}

if (signalName == null) {
throw new RuntimeException("failed to find signal name of signal start event");
}
long findStartTimerDueDate(long processDefinitionKey) {
for (Record<TimerRecordValue> record : BpmnAssert.getRecordStream().timerRecords()) {
var recordValue = record.getValue();

return signalName;
if (recordValue.getProcessDefinitionKey() == processDefinitionKey && recordValue.getTargetElementId().equals(testCase.getStart())) {
return recordValue.getDueDate();
}
}
throw new RuntimeException(String.format("failed to find due date of timer start event %s", testCase.getStart()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,22 +40,26 @@ void apply(Record<?> record) {
System.out.println(record);
}

switch (record.getValueType()) {
case JOB:
handleJob(record);
break;
case PROCESS_MESSAGE_SUBSCRIPTION:
handleMessageSubscription(record);
break;
case PROCESS_INSTANCE:
handleProcessInstance(record);
break;
case SIGNAL_SUBSCRIPTION:
handleSignalSubscription(record);
break;
case TIMER:
handleTimer(record);
break;
try {
switch (record.getValueType()) {
case JOB:
handleJob(record);
break;
case PROCESS_MESSAGE_SUBSCRIPTION:
handleMessageSubscription(record);
break;
case PROCESS_INSTANCE:
handleProcessInstance(record);
break;
case SIGNAL_SUBSCRIPTION:
handleSignalSubscription(record);
break;
case TIMER:
handleTimer(record);
break;
}
} catch (RuntimeException e) {
System.err.println(String.format("failed to process record: %s", record));
}
}

Expand Down Expand Up @@ -161,7 +165,10 @@ private void handleTimer(Record<?> record) {
timer.id = recordValue.getTargetElementId();
timer.state = TimerIntent.CREATED;

processInstances.get(recordValue.getProcessInstanceKey()).put(timer);
var processInstance = processInstances.get(recordValue.getProcessInstanceKey());
if (processInstance != null) { // can be null, in case of timer start events
processInstance.put(timer);
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,14 +62,16 @@ public void accept(TestCaseContext ctx) {

var startElement = ctx.getTestCase().getStartElement();
if (startElement.isProcessStart()) {
var bpmnEventSupport = new BpmnEventSupport(startElement.getFlowNode(CatchEvent.class));
if (bpmnEventSupport.isMessage()) {
var eventSupport = new BpmnEventSupport(startElement.getFlowNode(CatchEvent.class));
if (eventSupport.isMessage()) {
classBuilder.addMethod(buildIsMessageStart());
} else if (bpmnEventSupport.isSignal()) {
} else if (eventSupport.isSignal()) {
classBuilder.addMethod(buildIsSignalStart());
} else if (eventSupport.isTimer()) {
classBuilder.addMethod(buildIsTimerStart());
}
} else {
classBuilder.addMethod(buildIsProcessStart());
classBuilder.addMethod(buildIsNoProcessStart());
}

addHandlerMethods(strategies, classBuilder);
Expand Down Expand Up @@ -186,7 +188,7 @@ private MethodSpec buildIsMessageStart() {
.build();
}

private MethodSpec buildIsProcessStart() {
private MethodSpec buildIsNoProcessStart() {
return MethodSpec.methodBuilder("isProcessStart")
.addAnnotation(Override.class)
.addModifiers(Modifier.PROTECTED)
Expand All @@ -204,6 +206,15 @@ private MethodSpec buildIsSignalStart() {
.build();
}

private MethodSpec buildIsTimerStart() {
return MethodSpec.methodBuilder("isTimerStart")
.addAnnotation(Override.class)
.addModifiers(Modifier.PROTECTED)
.returns(TypeName.BOOLEAN)
.addStatement("return true")
.build();
}

private CodeBlock buildJavadoc(TestCaseContext ctx) {
var builder = CodeBlock.builder();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,20 @@ void testSimpleMessageCatchEvent() {
assertThat(typeSpec.methodSpecs.get(1)).containsCode("instance.hasPassed(processInstanceKey, \"messageCatchEvent\");");
}

@Test
void testSimpleMessageStartEvent() {
generator.generateTestCases(ctx, bpmnFile);
assertThat(result.getFiles()).hasSize(1);

var typeSpec = result.getFiles().get(0).typeSpec;
assertThat(typeSpec).hasFields(0);
assertThat(typeSpec).hasMethods(8);

assertThat(typeSpec.methodSpecs.get(7)).hasName("isMessageStart");
assertThat(typeSpec.methodSpecs.get(7)).hasReturnType(TypeName.BOOLEAN);
assertThat(typeSpec.methodSpecs.get(7)).containsCode("return true");
}

@Test
void testSimpleMessageThrowEvent() {
generator.generateTestCases(ctx, bpmnFile);
Expand Down Expand Up @@ -249,6 +263,20 @@ void testSimpleSignalCatchEvent() {
assertThat(typeSpec.methodSpecs.get(1)).containsCode("instance.hasPassed(processInstanceKey, \"signalCatchEvent\");");
}

@Test
void testSimpleSignalStartEvent() {
generator.generateTestCases(ctx, bpmnFile);
assertThat(result.getFiles()).hasSize(1);

var typeSpec = result.getFiles().get(0).typeSpec;
assertThat(typeSpec).hasFields(0);
assertThat(typeSpec).hasMethods(8);

assertThat(typeSpec.methodSpecs.get(7)).hasName("isSignalStart");
assertThat(typeSpec.methodSpecs.get(7)).hasReturnType(TypeName.BOOLEAN);
assertThat(typeSpec.methodSpecs.get(7)).containsCode("return true");
}

@Test
void testSimpleSubProcess() {
generator.generateTestCases(ctx, bpmnFile);
Expand Down Expand Up @@ -295,6 +323,20 @@ void testSimpleTimerCatchEvent() {
assertThat(typeSpec.methodSpecs.get(1)).containsCode("instance.hasPassed(processInstanceKey, \"timerCatchEvent\");");
}

@Test
void testSimpleTimerStartEvent() {
generator.generateTestCases(ctx, bpmnFile);
assertThat(result.getFiles()).hasSize(1);

var typeSpec = result.getFiles().get(0).typeSpec;
assertThat(typeSpec).hasFields(0);
assertThat(typeSpec).hasMethods(8);

assertThat(typeSpec.methodSpecs.get(7)).hasName("isTimerStart");
assertThat(typeSpec.methodSpecs.get(7)).hasReturnType(TypeName.BOOLEAN);
assertThat(typeSpec.methodSpecs.get(7)).containsCode("return true");
}

@Test
void testSimpleUserTask() {
generator.generateTestCases(ctx, bpmnFile);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,18 @@ void testVerifyPropagateAllParentVariables() {
tc.createExecutor(engine).customize(this::customize).execute();
}

@Test
void testErrorContainsIncidents() {
var e = assertThrows(AssertionError.class, () -> tc.createExecutor(engine)
.withTaskTimeout(1000L)
.verify(ProcessInstanceAssert::isCompleted)
.execute()
);

assertThat(e.getMessage()).contains("found incidents:");
assertThat(e.getMessage()).contains(" - element callActivity: CALLED_ELEMENT_ERROR: Expected process with BPMN process id 'simple' to be deployed");
}

private void customize(TestCaseExecutor testCaseExecutor) {
testCaseExecutor.simulateProcess("simple");
testCaseExecutor.verify(ProcessInstanceAssert::isCompleted);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package org.camunda.community.bpmndt.api;

import static com.google.common.truth.Truth.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;

import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
Expand Down Expand Up @@ -65,6 +68,20 @@ void testExecute() {
.execute();
}

@Test
void testErrorContainsActiveElements() {
var e = assertThrows(AssertionError.class, () -> tc.createExecutor(engine)
.withTaskTimeout(1000)
.withVariable("elements", List.of(1, 2, 3))
.verify(ProcessInstanceAssert::isCompleted)
.execute()
);

assertThat(e.getMessage()).contains("found active elements:");
assertThat(e.getMessage()).contains(" - multiInstanceScope");
assertThat(e.getMessage()).contains(" - userTask");
}

private class TestCase extends AbstractJUnit5TestCase {

@Override
Expand Down
Loading

0 comments on commit 9d2cf5b

Please sign in to comment.