Skip to content

Commit

Permalink
feat: added process instance migration, when test case is executed wi…
Browse files Browse the repository at this point in the history
…th a process definition in another version
  • Loading branch information
gclaussn committed Apr 10, 2023
1 parent db24cf6 commit 2ca09a4
Show file tree
Hide file tree
Showing 5 changed files with 205 additions and 89 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,12 @@ protected void beforeEach() {
ProcessEngineTests.init(processEngine);

instance = new TestCaseInstance();
instance.setEnd(getEnd());
instance.setProcessDefinitionKey(getProcessDefinitionKey());
instance.setProcessEnd(isProcessEnd());
instance.setProcessEngine(processEngine);
instance.setStart(getStart());
instance.setTenantId(tenantId);
instance.end = getEnd();
instance.processDefinitionKey = getProcessDefinitionKey();
instance.processEnd = isProcessEnd();
instance.processEngine = processEngine;
instance.start = getStart();
instance.tenantId = tenantId;

String deploymentName = this.getClass().getSimpleName();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

import org.camunda.bpm.engine.ProcessEngineException;
import org.camunda.bpm.engine.RuntimeService;
import org.camunda.bpm.engine.repository.ProcessDefinition;
import org.camunda.bpm.engine.migration.MigrationPlan;
import org.camunda.bpm.engine.runtime.ProcessInstance;
import org.camunda.bpm.engine.test.assertions.ProcessEngineTests;
import org.camunda.bpm.engine.test.assertions.bpmn.ProcessInstanceAssert;
Expand Down Expand Up @@ -60,14 +60,9 @@ public TestCaseExecutor customize(Consumer<TestCaseExecutor> customizer) {
* @return The newly created process instance.
*/
public ProcessInstance execute() {
// find process definition of related deployment
ProcessDefinition pd = instance.getProcessEngine().getRepositoryService()
.createProcessDefinitionQuery()
.deploymentId(instance.getDeploymentId())
.processDefinitionKey(instance.getProcessDefinitionKey())
.singleResult();
RuntimeService runtimeService = instance.getProcessEngine().getRuntimeService();

ProcessInstance pi = instance.getProcessEngine().getRuntimeService().createProcessInstanceById(pd.getId())
ProcessInstance pi = runtimeService.createProcessInstanceById(instance.getProcessDefinitionId())
.businessKey(businessKey)
.setVariables(variables)
.startBeforeActivity(instance.getStart())
Expand All @@ -86,7 +81,14 @@ public ProcessInstance execute() {
*/
public void execute(ProcessInstance pi) {
if (pi == null) {
throw new IllegalArgumentException("The given process instance is null");
throw new IllegalArgumentException("process instance is null");
}

if (!pi.getProcessDefinitionId().equals(instance.getProcessDefinitionId())) {
// migrate process instance, if it was started with another process definition
// this can happen, when the process instance is started with #startProcessInstanceByKey
// and multiple test cases deployed different instrumented versions of a process
migrate(pi);
}

// announce process instance
Expand All @@ -113,12 +115,15 @@ public void execute(ProcessInstance pi) {
*/
public ProcessInstance execute(String processInstanceId) {
if (processInstanceId == null) {
throw new IllegalArgumentException("The given process instance ID is null");
throw new IllegalArgumentException("process instance ID is null");
}

RuntimeService runtimeService = instance.getProcessEngine().getRuntimeService();

ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
ProcessInstance pi = runtimeService.createProcessInstanceQuery()
.processInstanceId(processInstanceId)
.singleResult();

if (pi == null) {
throw new IllegalArgumentException("No process instance found for the given ID");
}
Expand All @@ -128,6 +133,22 @@ public ProcessInstance execute(String processInstanceId) {
return pi;
}

protected void migrate(ProcessInstance pi) {
RuntimeService runtimeService = instance.getProcessEngine().getRuntimeService();

String source = pi.getProcessDefinitionId();
String target = instance.getProcessDefinitionId();

MigrationPlan migrationPlan = runtimeService.createMigrationPlan(source, target)
.mapEqualActivities()
.setVariables(runtimeService.getVariables(pi.getId()))
.build();

runtimeService.newMigration(migrationPlan)
.processInstanceIds(pi.getId())
.execute();
}

/**
* Unwraps and throws a possible {@link AssertionError} in case of a failed assertion within a
* {@link CallActivityHandler}'s verifier. If not unwrapped, a test will be marked as an error,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

import org.camunda.bpm.engine.ProcessEngine;
import org.camunda.bpm.engine.RepositoryService;
import org.camunda.bpm.engine.impl.bpmn.behavior.CallActivityBehavior;
import org.camunda.bpm.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.camunda.bpm.engine.impl.pvm.delegate.ActivityExecution;
import org.camunda.bpm.engine.repository.Deployment;
import org.camunda.bpm.engine.repository.DeploymentBuilder;
import org.camunda.bpm.engine.repository.DeploymentWithDefinitions;
import org.camunda.bpm.engine.repository.ProcessDefinition;
import org.camunda.bpm.engine.runtime.ProcessInstance;
import org.camunda.community.bpmndt.api.cfg.BpmndtParseListener;

Expand All @@ -26,20 +26,19 @@ public class TestCaseInstance {

private final Map<String, CallActivityHandler> callActivityHandlerMap;

private ProcessEngine processEngine;

protected String end;
/** Key of the test case related process definition. */
private String processDefinitionKey;

private String start;
private String end;

private boolean processEnd;
protected String processDefinitionKey;
protected boolean processEnd;
protected ProcessEngine processEngine;
protected String start;
protected String tenantId;

/** ID of BPMN resource deployment. */
private String deploymentId;

private String tenantId;
/** ID of the deployed process definition. */
private String processDefinitionId;

private ProcessInstance pi;

Expand Down Expand Up @@ -72,33 +71,41 @@ public void apply(UserTaskHandler handler) {
}

protected void deploy(String deploymentName, InputStream bpmnResource) {
this.register();
DeploymentBuilder deploymentBuilder = processEngine.getRepositoryService().createDeployment()
.addInputStream(String.format("%s.bpmn", getProcessDefinitionKey()), bpmnResource);

RepositoryService repositoryService = processEngine.getRepositoryService();
deploy(deploymentBuilder, deploymentName);
}

Deployment deployment = repositoryService.createDeployment()
.name(deploymentName)
.addInputStream(String.format("%s.bpmn", getProcessDefinitionKey()), bpmnResource)
.enableDuplicateFiltering(false)
.tenantId(tenantId)
.deploy();
protected void deploy(String deploymentName, String bpmnResourceName) {
DeploymentBuilder deploymentBuilder = processEngine.getRepositoryService().createDeployment()
.addClasspathResource(bpmnResourceName);

deploymentId = deployment.getId();
deploy(deploymentBuilder, deploymentName);
}

protected void deploy(String deploymentName, String bpmnResourceName) {
this.register();
private void deploy(DeploymentBuilder deploymentBuilder, String deploymentName) {
BpmndtParseListener parseListener = findParseListener();

RepositoryService repositoryService = processEngine.getRepositoryService();
// register instance at BpmndtParseListener,
// used for instrumentation of the process definition during BPMN model parsing
parseListener.setInstance(this);

Deployment deployment = repositoryService.createDeployment()
.name(deploymentName)
.addClasspathResource(bpmnResourceName)
DeploymentWithDefinitions deployment = deploymentBuilder.name(deploymentName)
.enableDuplicateFiltering(false)
.tenantId(tenantId)
.deploy();
.deployWithResult();

// deregister instance
parseListener.setInstance(null);

deploymentId = deployment.getId();

processDefinitionId = deployment.getDeployedProcessDefinitions().stream()
.filter(pd -> pd.getKey().equals(processDefinitionKey))
.map(ProcessDefinition::getId)
.findFirst()
.get();
}

/**
Expand Down Expand Up @@ -128,14 +135,15 @@ public boolean execute(ActivityExecution execution, CallActivityBehavior behavio
}
}

private Optional<BpmndtParseListener> findParseListener() {
private BpmndtParseListener findParseListener() {
ProcessEngineConfigurationImpl processEngineConfiguration =
(ProcessEngineConfigurationImpl) processEngine.getProcessEngineConfiguration();

return processEngineConfiguration.getCustomPostBPMNParseListeners().stream()
.filter((parseListener) -> (parseListener instanceof BpmndtParseListener))
.map(BpmndtParseListener.class::cast)
.findFirst();
.findFirst()
.get();
}

public String getDeploymentId() {
Expand All @@ -146,6 +154,10 @@ public String getEnd() {
return end;
}

public String getProcessDefinitionId() {
return processDefinitionId;
}

public String getProcessDefinitionKey() {
return processDefinitionKey;
}
Expand All @@ -162,50 +174,15 @@ public boolean isProcessEnd() {
return processEnd;
}

/**
* Registers the test case instance by providing a reference to the {@link BpmndtParseListener} that
* is used during BPMN model parsing.
*/
protected void register() {
findParseListener().ifPresent((parseListener) -> parseListener.setInstance(this));
}

protected void registerCallActivityHandler(String activityId, CallActivityHandler handler) {
callActivityHandlerMap.put(activityId, handler);
}

protected void setEnd(String end) {
this.end = end;
}

protected void setProcessDefinitionKey(String processDefinitionKey) {
this.processDefinitionKey = processDefinitionKey;
}

protected void setProcessEnd(boolean processEnd) {
this.processEnd = processEnd;
}

protected void setProcessEngine(ProcessEngine processEngine) {
this.processEngine = processEngine;
}

protected void setProcessInstance(ProcessInstance pi) {
this.pi = pi;
}

protected void setStart(String start) {
this.start = start;
}

protected void setTenantId(String tenantId) {
this.tenantId = tenantId;
}

protected void undeploy() {
// deregister instance
findParseListener().ifPresent((parseListener) -> parseListener.setInstance(null));

callActivityHandlerMap.clear();

if (deploymentId == null) {
Expand Down
Loading

0 comments on commit 2ca09a4

Please sign in to comment.