Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
gclaussn committed May 27, 2024
1 parent 98eba67 commit b9d0824
Show file tree
Hide file tree
Showing 32 changed files with 1,137 additions and 78 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import org.camunda.community.bpmndt.api.CustomMultiInstanceHandler;
import org.camunda.community.bpmndt.api.JobHandler;
import org.camunda.community.bpmndt.api.MessageEventHandler;
import org.camunda.community.bpmndt.api.OutboundConnectorHandler;
import org.camunda.community.bpmndt.api.SignalEventHandler;
import org.camunda.community.bpmndt.api.TestCaseExecutor;
import org.camunda.community.bpmndt.api.TestCaseInstance;
Expand Down Expand Up @@ -92,6 +93,7 @@ public void generate(GeneratorContext ctx) {
apiClasses.add(CustomMultiInstanceHandler.class);
apiClasses.add(JobHandler.class);
apiClasses.add(MessageEventHandler.class);
apiClasses.add(OutboundConnectorHandler.class);
apiClasses.add(SignalEventHandler.class);
apiClasses.add(TestCaseExecutor.class);
apiClasses.add(TestCaseInstance.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
import io.camunda.zeebe.process.test.assertions.BpmnAssert;
import io.camunda.zeebe.process.test.assertions.ProcessInstanceAssert;

/**
* Fluent API to handle call activities. The called process is simulated - see {@link TestCaseExecutor#simulateProcess(String)}
*/
public class CallActivityHandler {

// see src/main/resources/simulate-sub-process.bpmn
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import io.camunda.zeebe.process.test.assertions.ProcessInstanceAssert;

/**
* Fluent API to handle multi instances and multi instance scopes, using custom code.
* Fluent API to handle multi instances and multi instance scopes, using custom code - see {@link #execute(BiConsumer)}.
*/
public class CustomMultiInstanceHandler {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
import io.camunda.zeebe.process.test.assertions.ProcessInstanceAssert;

/**
* Fluent API to handle jobs that are handled via {@code JobHandler} by worker.
* Fluent API to handle jobs in form of service, script, send or business rule tasks as well as intermediate message throw or message end events.
*/
public class JobHandler {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
import io.camunda.zeebe.process.test.assertions.BpmnAssert;
import io.camunda.zeebe.process.test.assertions.ProcessInstanceAssert;

/**
* Fluent API to handle message catch events.
*/
public class MessageEventHandler {

private final MessageEventElement element;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,308 @@
package org.camunda.community.bpmndt.api;

import java.util.HashMap;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Consumer;

import org.camunda.community.bpmndt.api.TestCaseInstanceElement.OutboundConnectorElement;

import io.camunda.zeebe.client.ZeebeClient;
import io.camunda.zeebe.process.test.assertions.BpmnAssert;
import io.camunda.zeebe.process.test.assertions.ProcessInstanceAssert;

/**
* Fluent API to handle outbound connectors.
*/
public class OutboundConnectorHandler {

public OutboundConnectorHandler() {
// TODO
private final OutboundConnectorElement element;

private final Map<String, Object> variableMap = new HashMap<>();

private Consumer<ProcessInstanceAssert> verifier;
private BiConsumer<ZeebeClient, Long> action;
private String errorCode;
private String errorMessage;
private Object variables;

private Consumer<Map<String, String>> inputMappingConsumer;
private Consumer<Map<String, String>> outputMappingConsumer;
private Consumer<String> taskDefinitionTypeConsumer;
private Consumer<Map<String, String>> taskHeadersConsumer;

private Integer expectedRetries;
private String expectedTaskDefinitionType;

private Consumer<Integer> retriesConsumer;

public OutboundConnectorHandler(String elementId) {
if (elementId == null) {
throw new IllegalArgumentException("element ID is null");
}

element = new OutboundConnectorElement();
element.id = elementId;

complete();
}

public OutboundConnectorHandler(OutboundConnectorElement element) {
if (element == null) {
throw new IllegalArgumentException("element is null");
}
if (element.id == null) {
throw new IllegalArgumentException("element ID is null");
}

this.element = element;

complete();
}

void apply(TestCaseInstance instance, long processInstanceKey) {
if (verifier != null) {
verifier.accept(new ProcessInstanceAssert(processInstanceKey, BpmnAssert.getRecordStream()));
}

if (expectedTaskDefinitionType != null && !expectedTaskDefinitionType.equals(element.taskDefinitionType)) {
var message = "expected outbound connector %s to have task definition type '%s', but was '%s'";
throw new AssertionError(String.format(message, element.id, expectedTaskDefinitionType, element.taskDefinitionType));
}
if (taskDefinitionTypeConsumer != null) {
taskDefinitionTypeConsumer.accept(element.taskDefinitionType);
}

if (taskHeadersConsumer != null) {
taskHeadersConsumer.accept(element.taskHeaders);
}

if (inputMappingConsumer != null) {
inputMappingConsumer.accept(element.inputs);
}
if (outputMappingConsumer != null) {
outputMappingConsumer.accept(element.outputs);
}

var job = instance.getJob(processInstanceKey, element.id);

if (expectedRetries != null && !expectedRetries.equals(job.retries)) {
var message = "expected job %s to have a retry count of %d, but was %d";
throw new AssertionError(String.format(message, element.id, expectedRetries, job.retries));
}
if (retriesConsumer != null) {
retriesConsumer.accept(job.retries);
}

if (action != null) {
action.accept(instance.getClient(), job.key);
}
}

/**
* Executes an action that completes the underlying job, when the process instance is waiting at the corresponding element, using specified variables.
*
* @see #withVariable(String, Object)
* @see #withVariables(Object)
* @see #withVariableMap(Map)
*/
public void complete() {
action = this::complete;
}

/**
* Customizes the handler, using the given {@link Consumer} function. This method can be used to apply a common customization needed for different test
* cases.
*
* <pre>
* tc.handleOutboundConnector().customize(this::prepare);
* </pre>
*
* @param customizer A function that accepts a {@link OutboundConnectorHandler}.
* @return The handler.
*/
public OutboundConnectorHandler customize(Consumer<OutboundConnectorHandler> customizer) {
if (customizer != null) {
customizer.accept(this);
}
return this;
}

/**
* Executes a custom action that handles the underlying job, when the process instance is waiting at the corresponding element.
*
* @param action A specific action that accepts a {@link ZeebeClient} and the related job key.
* @see ZeebeClient#newCompleteCommand(long)
*/
public void execute(BiConsumer<ZeebeClient, Long> action) {
if (action == null) {
throw new IllegalArgumentException("action is null");
}
this.action = action;
}

/**
* Throws an BPMN error using the given error code and message as well as the specified variables.
*
* @see #withVariable(String, Object)
* @see #withVariables(Object)
* @see #withVariableMap(Map)
*/
public void throwBpmnError(String errorCode, String errorMessage) {
this.errorCode = errorCode;
this.errorMessage = errorMessage;
this.action = this::throwBpmnError;
}

/**
* Verifies the outbound connector's waiting state. This method can be used to assert the variables, created by the connector's input mapping.
*
* @param verifier Verifier that accepts an {@link ProcessInstanceAssert} instance.
* @return The handler.
*/
public OutboundConnectorHandler verify(Consumer<ProcessInstanceAssert> verifier) {
this.verifier = verifier;
return this;
}

/**
* Verifies the modeled inputs of the connector's IO mapping, using a consumer function.
*
* @param inputMappingConsumer A consumer asserting the input mapping.
* @return The handler.
*/
public OutboundConnectorHandler verifyInputMapping(Consumer<Map<String, String>> inputMappingConsumer) {
this.inputMappingConsumer = inputMappingConsumer;
return this;
}

/**
* Verifies the modeled outputs of the connector's IO mapping, using a consumer function.
*
* @param outputMappingConsumer A consumer asserting the output mapping.
* @return The handler.
*/
public OutboundConnectorHandler verifyOutputMapping(Consumer<Map<String, String>> outputMappingConsumer) {
this.outputMappingConsumer = outputMappingConsumer;
return this;
}

/**
* Verifies that the underlying job has a specific number of retries.
*
* @param expectedRetries The expected retry count.
* @return The handler.
*/
public OutboundConnectorHandler verifyRetries(Integer expectedRetries) {
this.expectedRetries = expectedRetries;
return this;
}

/**
* Verifies that the underlying job has a specific number of retries, using a consumer.
*
* @param retriesConsumer A consumer asserting the retry count.
* @return The handler.
*/
public OutboundConnectorHandler verifyRetries(Consumer<Integer> retriesConsumer) {
this.retriesConsumer = retriesConsumer;
return this;
}

/**
* Verifies that the outbound connector has the given task definition type.
*
* @param expectedTaskDefinitionType The expected task definition type.
* @return The handler.
*/
public OutboundConnectorHandler verifyTaskDefinitionType(String expectedTaskDefinitionType) {
this.expectedTaskDefinitionType = expectedTaskDefinitionType;
return this;
}

/**
* Verifies that the outbound connector has a specific task definition type, using a consumer.
*
* @param taskDefinitionTypeConsumer A consumer asserting the task definition type.
* @return The handler.
*/
public OutboundConnectorHandler verifyTaskDefinitionType(Consumer<String> taskDefinitionTypeConsumer) {
this.taskDefinitionTypeConsumer = taskDefinitionTypeConsumer;
return this;
}

/**
* Verifies the modeled task headers of the connector, using a consumer function.
*
* @param taskHeadersConsumer A consumer asserting the task headers.
* @return The handler.
*/
public OutboundConnectorHandler verifyTaskHeaders(Consumer<Map<String, String>> taskHeadersConsumer) {
this.taskHeadersConsumer = taskHeadersConsumer;
return this;
}

/**
* Sets a variable that is used to complete the underlying job.
*
* @param name The name of the variable.
* @param value The variable's value.
* @return The handler.
* @see #complete()
*/
public OutboundConnectorHandler withVariable(String name, Object value) {
if (variables != null) {
throw new IllegalStateException("either use an object (POJO) as variables or a variable map");
}
variableMap.put(name, value);
return this;
}

/**
* Sets an object as variables that is used to complete the underlying job.
*
* @param variables The variables as POJO.
* @return The handler.
* @see #complete()
*/
public OutboundConnectorHandler withVariables(Object variables) {
if (!variableMap.isEmpty()) {
throw new IllegalStateException("either use an object (POJO) as variables or a variable map");
}
this.variables = variables;
return this;
}

/**
* Sets variables that are used to complete the underlying job.
*
* @param variableMap A map of variables.
* @return The handler.
* @see #complete()
*/
public OutboundConnectorHandler withVariableMap(Map<String, Object> variableMap) {
if (variables != null) {
throw new IllegalStateException("either use an object (POJO) as variables or a variable map");
}
this.variableMap.putAll(variableMap);
return this;
}

void complete(ZeebeClient client, long jobKey) {
if (variables != null) {
client.newCompleteCommand(jobKey).variables(variables).send().join();
} else {
client.newCompleteCommand(jobKey).variables(variableMap).send().join();
}
}

void throwBpmnError(ZeebeClient client, long jobKey) {
var throwErrorCommandStep2 = client.newThrowErrorCommand(jobKey).errorCode(errorCode).errorMessage(errorMessage);

if (variables != null) {
throwErrorCommandStep2.variables(variables).send().join();
} else {
throwErrorCommandStep2.variables(variableMap).send().join();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
import io.camunda.zeebe.process.test.assertions.BpmnAssert;
import io.camunda.zeebe.process.test.assertions.ProcessInstanceAssert;

/**
* Fluent API to handle signal catch events.
*/
public class SignalEventHandler {

private final SignalEventElement element;
Expand Down
Loading

0 comments on commit b9d0824

Please sign in to comment.