Skip to content

Commit

Permalink
feat(test): Adding integration tests (#82)
Browse files Browse the repository at this point in the history
Add integration tests using testcontainers and the bonita-java-client.
Use an utility class to deploy and configure a process with the built connector implementation

Co-authored-by: Romain Bioteau <romain.bioteau@bonitasoft.com>
  • Loading branch information
mbource and rbioteau authored Sep 6, 2022
1 parent 6b8d3fc commit 458c8fd
Show file tree
Hide file tree
Showing 4 changed files with 1,480 additions and 7 deletions.
81 changes: 74 additions & 7 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,9 @@
<junit.version>4.13.2</junit.version>
<jacoco-maven-plugin.version>0.8.8</jacoco-maven-plugin.version>
<sonar-maven-plugin.version>3.9.1.2184</sonar-maven-plugin.version>
<testcontainers.version>1.17.3</testcontainers.version>
<bonita-java-client.version>0.0.9</bonita-java-client.version>
<awaitility.version>4.2.0</awaitility.version>

<!-- Maven plugins -->
<maven-compiler-plugin.version>3.10.1</maven-compiler-plugin.version>
Expand All @@ -108,6 +111,7 @@
<groovy-maven-plugin.version>2.1.1</groovy-maven-plugin.version>
<groovy.version>3.0.12</groovy.version>
<maven-surefire-plugin.version>3.0.0-M7</maven-surefire-plugin.version>
<maven-failsafe-plugin.version>3.0.0-M7</maven-failsafe-plugin.version>
<nexus-staging-maven-plugin.version>1.6.13</nexus-staging-maven-plugin.version>
<maven-javadoc-plugin.version>3.4.1</maven-javadoc-plugin.version>
<maven-source-plugin.version>3.2.1</maven-source-plugin.version>
Expand All @@ -118,6 +122,7 @@
<sonar.host.url>https://sonarcloud.io</sonar.host.url>
</properties>


<dependencyManagement>
<dependencies>
<dependency>
Expand All @@ -132,6 +137,13 @@
<artifactId>jackson-core</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers-bom</artifactId>
<version>${testcontainers.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

Expand Down Expand Up @@ -196,7 +208,24 @@
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.4.0</version>
<version>1.2.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.bonitasoft.web</groupId>
<artifactId>bonita-java-client</artifactId>
<version>${bonita-java-client.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.awaitility</groupId>
<artifactId>awaitility</artifactId>
<version>${awaitility.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
Expand All @@ -216,11 +245,21 @@
</resources>
<pluginManagement>
<plugins>
<plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.version}</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${maven-surefire-plugin.version}</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<version>${maven-failsafe-plugin.version}</version>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>${maven-assembly-plugin.version}</version>
Expand Down Expand Up @@ -252,11 +291,6 @@
</dependency>
</dependencies>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${maven-surefire-plugin.version}</version>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
Expand Down Expand Up @@ -355,6 +389,39 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<executions>
<execution>
<id>integration-tests-7.13</id>
<goals>
<goal>integration-test</goal>
</goals>
<configuration>
<systemProperties>
<bonita.version>7.13.0</bonita.version>
</systemProperties>
</configuration>
</execution>
<execution>
<id>integration-tests-7.14</id>
<goals>
<goal>integration-test</goal>
</goals>
<configuration>
<systemProperties>
<bonita.version>7.14.0</bonita.version>
</systemProperties>
</configuration>
</execution>
<execution>
<goals>
<goal>verify</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
Expand Down
218 changes: 218 additions & 0 deletions src/test/java/org/bonitasoft/connectors/rest/ConnectorTestToolkit.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
package org.bonitasoft.connectors.rest;

import static org.assertj.core.api.Assertions.assertThat;

import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import org.bonitasoft.engine.bpm.bar.BarResource;
import org.bonitasoft.engine.bpm.bar.BusinessArchive;
import org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder;
import org.bonitasoft.engine.bpm.bar.BusinessArchiveFactory;
import org.bonitasoft.engine.bpm.bar.actorMapping.Actor;
import org.bonitasoft.engine.bpm.bar.actorMapping.ActorMapping;
import org.bonitasoft.engine.bpm.connector.ConnectorEvent;
import org.bonitasoft.engine.bpm.process.DesignProcessDefinition;
import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;
import org.bonitasoft.engine.expression.ExpressionBuilder;
import org.bonitasoft.engine.expression.InvalidExpressionException;
import org.bonitasoft.engine.operation.OperationBuilder;
import org.bonitasoft.web.client.BonitaClient;
import org.bonitasoft.web.client.api.ProcessInstanceVariableApi;
import org.bonitasoft.web.client.model.ProcessInstantiationResponse;
import org.bonitasoft.web.client.services.policies.ProcessImportPolicy;

/**
* Helper for testing connector in a docker image of Bonita studio.
*/
public class ConnectorTestToolkit {

/**
* Build a connector and then install it into a dummy process with input and output process variables.
* Those variables will help to verify the input and output specified in the implementation of the connector to be tested.
*
* @param connectorId The identifier of the connector specified in the pom.xml and the definition file.
* @param versionId The version of the connector to be tested.
* @param inputs A map of variables with a content specified to be tested.
* @param outputs A map of results variables.
* @param locationJar The Jar containing the class and dependencies used by the connector.
* @return A {@link BusinessArchive}
* @throws Exception
*/
public static BusinessArchive buildConnectorToTest(String connectorId, String versionId, Map<String, String> inputs,
Map<String, Output> outputs, String locationJar) throws Exception {

// Building process with connector to setup
var process = buildConnectorInProcess(connectorId, versionId, inputs, outputs);

// Building business archive with the process and connector
return buildBusinessArchive(process, connectorId, locationJar);
}

private static BusinessArchive buildBusinessArchive(DesignProcessDefinition process, String connectorId,
String artifactId) throws Exception {
var barBuilder = new BusinessArchiveBuilder();
barBuilder.createNewBusinessArchive();
barBuilder.setProcessDefinition(process);
var foundFiles = new File("").getAbsoluteFile().toPath()
.resolve("target")
.toFile()
.listFiles(new FilenameFilter() {

@Override
public boolean accept(File dir, String name) {
return Pattern.matches(artifactId + "-.*.jar", name)
&& !name.endsWith("-sources.jar")
&& !name.endsWith("-javadoc.jar");
}
});

assertThat(foundFiles).hasSize(1);
var connectorJar = foundFiles[0];
assertThat(connectorJar).exists();
List<JarEntry> jarEntries = findJarEntries(connectorJar,
entry -> entry.getName().equals(connectorId + ".impl"));
assertThat(jarEntries).hasSize(1);
var implEntry = jarEntries.get(0);

byte[] content = null;
try (JarFile jarFile = new JarFile(connectorJar)) {
InputStream inputStream = jarFile.getInputStream(implEntry);
content = inputStream.readAllBytes();
}

barBuilder.addConnectorImplementation(
new BarResource(connectorId + ".impl", content));
barBuilder.addClasspathResource(
new BarResource(connectorJar.getName(), Files.readAllBytes(connectorJar.toPath())));
ActorMapping actorMapping = new ActorMapping();
var systemActor = new Actor("system");
systemActor.addRole("member");
actorMapping.addActor(systemActor);
barBuilder.setActorMapping(actorMapping);

return barBuilder.done();
}

private static DesignProcessDefinition buildConnectorInProcess(String connectorId, String versionId,
Map<String, String> inputs, Map<String, Output> outputs) throws Exception {
var processBuilder = new ProcessDefinitionBuilder();
var expBuilder = new ExpressionBuilder();
processBuilder.createNewInstance("PROCESS_UNDER_TEST", "1.0");
processBuilder.addActor("system");
var connectorBuilder = processBuilder.addConnector("connector-under-test", connectorId, versionId,
ConnectorEvent.ON_ENTER);
inputs.forEach((name, content) -> {
try {
connectorBuilder.addInput(name, expBuilder.createConstantStringExpression(
content));
} catch (InvalidExpressionException e) {
throw new RuntimeException(e);
}
});

if (outputs != null) {
outputs.forEach((name, output) -> {
try {
processBuilder.addData(name, output.getType(), null);
connectorBuilder.addOutput(new OperationBuilder().createSetDataOperation(name,
new ExpressionBuilder().createDataExpression(output.getName(), output.getType())));
} catch (InvalidExpressionException e) {
throw new RuntimeException(e);
}
});
}

// Add a user task to avoid the process to be already completed as soon as it's launched.
processBuilder.addUserTask("waiting task", "system");

return processBuilder.done();
}

/**
* Import the {@link BusinessArchive} and launch the dummy process containing the connector to be tested.
*
* @param barArchive The file containing the {@link BusinessArchive}
* @param client A {@link BonitaClient}
* @return The process started.
* @throws IOException
*/
public static ProcessInstantiationResponse importAndLaunchProcess(BusinessArchive barArchive, BonitaClient client)
throws IOException {
var process = barArchive.getProcessDefinition();
File processFile = null;
try {
processFile = Files.createTempFile("process", ".bar").toFile();
processFile.delete();
BusinessArchiveFactory.writeBusinessArchiveToFile(barArchive, processFile);
client.login("install", "install");
client.processes().importProcess(processFile, ProcessImportPolicy.REPLACE_DUPLICATES);
} finally {
if (processFile != null) {
processFile.delete();
}
}

var processId = client.processes().getProcess(process.getName(), process.getVersion()).getId();
return client.processes().startProcess(processId, Map.of());

}

private static List<JarEntry> findJarEntries(File file, Predicate<? super JarEntry> entryPredicate)
throws IOException {
try (JarFile jarFile = new JarFile(file)) {
return jarFile.stream()
.filter(entryPredicate)
.collect(Collectors.toList());
}
}

/**
* Getting the content value of a specific variable process.
*
* @param client A {@link BonitaClient}
* @param caseId A process instance id.
* @param variableProcessName The name of the variable process, it must have been already declared in the output map of the connector before building the
* connector to test.
* @return The content of the variable. Can be null.
*/
public static String getProcessVariableValue(BonitaClient client, String caseId, String variableProcessName) {
return client.get(ProcessInstanceVariableApi.class).getVariableByProcessInstanceId(caseId, variableProcessName)
.getValue();

}

static class Output {

private final String name;
private final String type;

public static Output create(String name, String type) {
return new Output(name, type);
}

private Output(String name, String type) {
this.name = name;
this.type = type;
}

public String getName() {
return name;
}

public String getType() {
return type;
}

}
}
Loading

0 comments on commit 458c8fd

Please sign in to comment.