Test Trigger Connector framework
The worker-task-test-trigger library is a part of the worker-task-test framework that provides execution and assertion APIs for testing Trigger Workers. This library allows you to configure and run Triggers, modify environment configurations and beans per test class, and assert Trigger results.
Since it is part of the worker-task-test framework, its API follows the same style as the worker-task-test-jnw library. Therefore, if you are already familiar with Java Native Worker (JNW) testing, writing tests with this library will be straightforward.
Integrate worker-task-test-trigger into project
To integrate worker-task-test-trigger into an existing or new project, follow the steps below, taking into account the JNW compatibility matrix:
Add the version of worker-task-test to the
<properties>section of your Maven project:<properties>
<wf.worker-task-test.version>2.0.37</wf.worker-task-test.version>
<!—
2.0.37 is an earliest version which supports features described in this article.
You may need to look for recent version.
—>
</properties>If your project is part of the
workfusion-metapipeline, add the worker-task-test dependency to thecomponents.yamlfile of theworkfusion-metaproject:your-project:
dependencies:
- worker-task-testImport
worker-task-test-bominto the<dependencyManagement>section of your Maven project. Then, add any required module of worker-task-test-trigger-core to the<dependencies>section:<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.workfusion.jnw.test</groupId>
<artifactId>worker-task-test-bom</artifactId>
<version>${wf.worker-task-test.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>com.workfusion.jnw.test.trigger</groupId>
<artifactId>worker-task-test-trigger-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
Prepare test class and modify execution context
To prepare a test class and modify its execution context, do as follows:
Add the
@TriggerJunitConfigannotation at the class level:@TriggerJunitConfig
class TestClassName {
// tests methods
}Add the needed classes and packages.
If you have configuration classes with beans you want to override or reuse in your tests, add them to the
classesproperty of the@TriggerJunitConfigannotation:@TriggerJunitConfig(classes = {FirstConfigurationClass.class, SecondConfigurationClass.class})
class TestClassName {
// tests methods
}If your configurations and beans are located in separate packages, add them to the
basePackagesproperty of the@TriggerJunitConfigannotation:@TriggerJunitConfig(basePackages = {"user.configuration.pacage", "user.beans.package"})
class TestClassName {
// tests methods
}infoYou do not need to add the com.workfusion.spa, com.workfusion.connector.trigger, and com.workfusion.jnw.test.trigger packages to
basePackagesas they are included automatically.cautionThe framework automatically adds the test class package to
ComponentScan. If the package name ends withtask, the last part is removed. If your configuration exists in the package, it is added to the test context automatically. To exclude it, move your configuration to a different package.
To modify or add properties in the
application.ymlconfiguration file, use thepropertiesproperty in the@TriggerJunitConfigannotation:@TriggerJunitConfig(properties = {"it.test.value=testValue"})
class TestClassName {
// tests methods
}
By default, you do not need to create application.yml as it is automatically generated in the target folder. If you you already have one, place it in the resources folder. If the file has a custom name, use this construct to modify the configuration file for the current test class:
@TriggerJunitConfig
@TestPropertySource(properties = {"spring.config.location = classpath:test-configuration.yml"})
class TestClassName {
// tests methods
}
Default application.yml file
spring:
config:
import:
- "optional:classpath:application.yml"
main:
web-application-type: none
application:
name: com-workfusion-jnw-test-triger-test
profiles:
active: execution-worker,trigger
cloud:
zookeeper:
config:
enabled: false
connection-timeout: 15000
session-timeout: 40000
vault:
enabled: false
management:
logging:
enabled: false
rabbitmq:
username: guest
virtual-host: vhost
password: guest
publisher-confirm-type: correlated
publisher-returns: true
cache:
connection:
mode: connection
wf:
sms:
local:
file:
path: ${java.io.tmpdir}/sms_local
connector:
uuid: 98a10042-7cb3-4221-b3ae-516ff3387aa7
processing:
monitoring:
initial-delay-seconds: 1
interval: 1
management:
logging:
enabled: false
Run and configure processor
To execute a processor, you can choose one of the following approaches:
TriggerTaskRunner: an abstraction over the Trigger processor class and Trigger configuration. It allows you to reuse configurations with different data provider states without duplicating the entire code. This approach results in a cleaner and more maintainable codebase.SingleTriggerTaskLauncher:SingleTriggerTaskLauncherallows you to create tasks with configuration in one box.
TriggerTaskRunner approach
To initialize TriggerTaskRunner, use one of the options provided by TriggerTaskFactory:
Initialize
TriggerTaskRunnerfrom the processor class without configuration:@Test
void yourTest(TriggerTaskFactory taskFactory) {
TriggerTaskRunner triggerTaskRunner = taskFactory.createTaskRunner(TaskProcessor.class);
}Initialize
TriggerTaskRunnerusingTriggerTaskInstance, which acts as a configuration builder (similar toSingleTriggerTaskLauncher) but cannot run without creatingTriggerTaskRunner. In this case,TriggerTaskInstanceprepares data and configuration for the runner, which can then be initialized as a class variable:TriggerTaskInstance taskInstance = new TriggerTaskInstance(TaskProcessor.class)
.withConfiguration("inner json configuration")
.withBpConfiguration("BP json configuration");
@Test
void yourTest(TriggerTaskFactory taskFactory) {
TriggerTaskRunner triggerTaskRunner = taskFactory.createTaskRunner(taskInstance);
}Initialize
TriggerTaskRunnerusing the processor class and theTriggerTaskInstanceunary operator.@Test
void yourTest(TriggerTaskFactory taskFactory) {
TriggerTaskRunner triggerTaskRunner = taskFactory.createTaskRunner(TaskProcessor.class, taskInstance -> taskInstance
.withConfiguration("inner json configuration")
.withBpConfiguration("BP json configuration"));
}
SingleTriggerTaskLauncher approach
To run and configure a task with the SingleTriggerTaskLauncher builder, perform the following steps:
Inject
TriggerTaskFactoryinto your test method and callfromClass(Class<? Extend IPipelineProcessor>):@Test
void yourTest(TriggerTaskFactory taskFactory) {
//when
SingleTriggerTaskLauncher singleTaskLauncher = taskFactory.fromClass(TaskProcessor.class);
}Add configurations if required:
@Test
void yourTest(TriggerTaskFactory taskFactory) {
//when
SingleTriggerTaskLauncher singleTaskLauncher = taskFactory.fromClass(TaskProcessor.class)
.withConfiguration("inner json configuration")
.withBpConfiguration("BP json configuration");
}Run the launcher using the same methods as in
TriggerTaskRunner:@Test
void yourTest(TriggerTaskFactory taskFactory) {
//when
TriggerOutput output = taskFactory.fromClass(TaskProcessor.class)
.withConfiguration("inner json configuration")
.withBpConfiguration("BP json configuration")
.runWithTimeout(8L, TimeUnit.SECONDS);
}
Execute TriggerTaskRunner or SingleTriggerTaskLauncher
A Trigger can run continuously, for example, when monitoring an external source or using scheduled polling. In such cases, instead of the input Trigger testing framework, use stop rules to limit execution.
There are two types of stop rules:
TimeoutTriggerStopRuleRecordsTriggerStopRule
TriggerTaskRunner and SingleTriggerTaskLauncher share the same execution launch methods, so all methods described below apply to both.
TimeoutTriggerStopRule
This rule defines a time period during which the Trigger runs before returning results.
To start execution with TimeoutTriggerStopRule, use one of the following methods:
TriggerStopRuleBuilder:@Test
void yourTest(TriggerTaskFactory taskFactory) {
//when
TriggerOutput output = taskFactory.fromClass(TaskProcessor.class)
.runWithTimeoutStopRule(timeoutTriggerStopRule -> timeoutTriggerStopRule.withTimeout(5L, TimeUnit.SECONDS));
}Predefined methods for
TriggerStopRule:@Test
void yourTest(TriggerTaskFactory taskFactory) {
//when
TriggerOutput first = taskFactory.fromClass(TaskProcessor.class).runWithTimeout(5L, TimeUnit.SECONDS);
TriggerOutput second = taskFactory.fromClass(TaskProcessor.class).runWithTimeout(1500L); // this method use MILLISECONDS as time unit
}
RecordsTriggerStopRule
RecordsTriggerStopRule is a more complex rule that allows you to configure each rule parameter as execution time, check time interval, and timeout and even define a custom stop rule.
Available rule parameters and methods are as follows:
delay: time before the rule is first checked.
@Autowired
TriggerStopRuleFactory ruleFactory;
@Test
void yourTest(TriggerTaskFactory taskFactory) {
//when
ruleFactory.recordsTriggerStopRule()
.withDelay(10L) // default time unit is SECONDS
.withDelayUnit(TimeUnit.MINUTES)
.withDelay(10000L, TimeUnit.MILLISECONDS);
}interval: how often the rule is checked afterward.
@Autowired
TriggerStopRuleFactory ruleFactory;
@Test
void yourTest(TriggerTaskFactory taskFactory) {
//when
ruleFactory.recordsTriggerStopRule()
.withInterval(10L) // default time unit is SECONDS
.withIntervalTimeUnit(TimeUnit.MINUTES)
.withInterval(10000L, TimeUnit.MILLISECONDS);
}timeout: total execution duration before stopping.
@Autowired
TriggerStopRuleFactory ruleFactory;
@Test
void yourTest(TriggerTaskFactory taskFactory) {
//when
ruleFactory.recordsTriggerStopRule()
.withTimeout(10L) // default time unit is SECONDS
.withTimeoutUnit(TimeUnit.MINUTES)
.withTimeout(10000L, TimeUnit.MILLISECONDS);
}rule:
Predicatethat stops execution when it returnstrue. As a rule parameter, it receivesTriggerRecordsContainerdescribing the current result state. You can use data provider states, counters, or other logic in this rule.@Autowired
TriggerStopRuleFactory ruleFactory;
@MockBean
CustomDataProvider dataProvider;
@Test
void yourTest(TriggerTaskFactory taskFactory) {
//when
ruleFactory.recordsTriggerStopRule()
.withRule(container -> container.getRecords().size() >= 5)
.withRule(container -> dataProvider.getDoneFlag() == true);
}
RecordsTriggerStopRule provides the withTimeUnit(TimeUnit timeUnit) method to set a common time unit for all parameters.
To start execution with RecordsTriggerStopRule, you can use one of the foolowing methods:
TriggerStopRuleBuilder:@Test
void yourTest(TriggerTaskFactory taskFactory) {
//when
TriggerOutput output = taskFactory.fromClass(TaskProcessor.class)
.runWithRecordsStopRule(timeoutTriggerStopRule -> timeoutTriggerStopRule
.withTimeout(10L, TimeUnit.SECONDS)
.withRule(container -> container.getRecords().size() >= 5));
}Predefined methods for
TriggerStopRule:@Test
void yourTest(TriggerTaskFactory taskFactory) {
//when
TriggerOutput first = taskFactory.fromClass(TaskProcessor.class).runWithRecordsLimit(5);
}
Asynchronous methods
All methods described above have alternative asynchronous methods with the Async suffix. Asynchronous methods return Future<TriggerOutput> instead of TriggerOutput, allowing you to avoid waiting for the Trigger results and, for example, start the next one or change the behavior of the data provider during Trigger execution.
To get execution results, call the get() method, which returns the result if it is available or interrupts the thread until it is ready.
@MockBean
CustomDataProvider dataProvider;
@Test
void yourTest(TriggerTaskFactory taskFactory) {
//when
Future<TriggerOutput> output = taskFactory.fromClass(TaskProcessor.class)
.runWithRecordsStopRuleAsync(timeoutTriggerStopRule -> timeoutTriggerStopRule
.withTimeout(10L, TimeUnit.SECONDS)
.withRule(container -> dataProvider.getDoneFlag() == true));
Thread.sleep(2000);
dataProvider.addRow("some data");
TriggerOutput result = output.get();
}
TriggerOutput
TriggerOutput is a simple collector of RecordResult that your Trigger sends during execution. At the same time, it provides several additional methods that allow you to iterate by results or filter them with a custom rule:
getAllTaskResults()returns a collection of allRecordResultobjects.getIteratorByResult()returnsIterator<RecordResult>over all records.filterResult(Predicate<RecordResult> filter)returns a filtered list of results (List<RecordResult>).
Assert TriggerOutput
The main goal of worker-task-test-trigger-assertions is to provide a convenient way to check the status of RecordResult objects, filter by status, and use the AssertJ-based RecordResult instance for assertion.
To perform assertions, call the static assertThat method from the TriggerOutputAssert class. It supports both Collection<RecordResult> and TriggerOutput, producing equivalent results:
@Test
void yourTest(TriggerTaskFactory taskFactory) {
TriggerOutput result = taskFactory.fromClass(TaskProcessor.class).runWithRecordsLimit(5);
TriggerOutputAssert.assertThat(result).isSuccessful();
}
You can check or filter results by status using the following methods:
- For the
COMPLETEDstatus:isSuccessful()verifies that all records have the same status.containSuccessfulRecordResult()checks if at least one record isCOMPLETED.doesNotContainSuccessfulRecordResult()ensures no records areCOMPLETED.isPartiallySuccessful()checks if at least one record isCOMPLETEDand returns AssertJ-basedListRecordResultAssertfor such records. In this case, you can work with it as with a simple list assertion.
- For
FAILED,INVALID_REQUEST,NOT_AUTHORIZED, andNOT_FOUNDstatuses:isFailed()checks if all records have the same status.containFailedRecordResult()verifies if at least one record has the error status.doesNotContainFailedRecordResult()checks if the result list does not contain any record with the error status.isPartiallyFailed()verifies if at least one record has the error status and returns AssertJ-basedListIncompleteRequestResultAssertfor such records. In this case, you can work with it as with a simple list assertion.isPartialFailed(RecordProcessingStatus failureStatus)is the same asisPartiallyFailed()but you can choose the status you need.
- For
IN_PROGRESSstatus:isInProgress()ensures all records have the same status.containInProgressRecordResult()checks if at least one record has theIN_PROGRESSstatus.doesNotContainInProgressRecordResult()checks if the result list does not contain records with theIN_PROGRESSstatus.isPartiallyInProgress()checks if at least one record has theIN_PROGRESSstatus and returns AssertJ-basedListIncompleteRequestResultAssertfor such records. In this case, you can work with it as with a simple list assertion.
You can call any methods that the AbstractCollectionAssert class from AssertJ provides for asserting collections.
If you want to assert all RecordResult objects as a list, call the asListOfTriggerResult() method or use asListOfTriggerResultByStatus(RecordProcessingStatus... statuses) and choose only records with a specific status, for example, only successful and failed records.
@Test
void yourTest(TriggerTaskFactory taskFactory) {
TriggerOutput result = taskFactory.fromClass(TaskProcessor.class).runWithRecordsLimit(5);
TriggerOutputAssert.assertThat(result).asListOfTriggerResultByStatus(RecordProcessingStatus.COMPLETED, RecordProcessingStatus.FAILED)
.anySatisfy(recordResult -> {
assertThat(recordResult.getData()).hasSize(2);
});
}
RequestResult actually has some specific assertions for its values:
| Parameter | Methods |
|---|---|
requestId |
|
format |
|
statusDetails |
|
status |
|
data |
|
See additional examples of assertions. If you are familiar with the AssertJ API, you will have no trouble writing assertions for TriggerOutput and RecordResult:
@Test
void yourTest(TriggerTaskFactory taskFactory) {
TriggerOutput result = taskFactory.fromClass(TaskProcessor.class).runWithRecordsLimit(5);
TriggerResultAssert.assertThat(results).doesNotContainFailedRecordResult().isPartiallySuccessful()
.allSatisfyAssert(assertion ->
assertion.isFormatNull().hasData().singleElement().isNotEmpty().allSatisfy((key, value) ->
assertThat(value).isEqualTo("some_data")));
TriggerOutputAssert.assertThat(result).isSuccessful().allSatisfyAssert(assertion ->
assertion.dataHasOneElement().containsAnyOf(data.entrySet().toArray(new Map.Entry[0])));
}
The data parameter of RecordResult has the List<Map<String,Object>> format, and you need to check the list first and only then assert the map with the data.
Use additional modules
The worker-task-test library includes optional modules that can be useful for specific scenarios, such as database interaction, S3 support, or when working with the secret storage. The modules are not required for running task processors.
Available modules include:
ORMLite support module
The module enables you to mock ORMLite repositories in your tests. You can create repositories, populate them with data, and verify the state of the repository after processor execution.
The approach is similar to testing with the ODF 2 Data Store. For more details, refer to Bot Task JUnit | ODF 2: ORM and Entity classes.
Setup
To enable the feature, add the worker-task-test-orm-lite-module dependency to your project with a test scope and define the module version in worker-task-test-bom:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.workfusion.jnw.test</groupId>
<artifactId>worker-task-test-bom</artifactId>
<version>${wf.worker-task-test.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>com.workfusion.jnw.test.trigger</groupId>
<artifactId>worker-task-test-trigger-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.workfusion.jnw.test</groupId>
<artifactId>worker-task-test-orm-lite-module</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
Usage
To get started with ORMLite repositories, add the OrmLiteSupport parameter to your test method signature:
private static OrmLiteRepository<User> userRepository;
@BeforeAll
static void prepareDocument(OrmLiteSupport ormLiteSupport) {
ormLiteSupport.createTables(User.class, CustomRepository.class);
userRepository = ormLiteSupport.getRepository(User.class);
userRepository.create(new User(1, "firstName"))
}
@Test
void shouldGetDocumentByUuidWithRepositoryFromCode(TriggerTaskFactory taskFactory, OrmLiteSupport ormLiteSupport) {
//given
ormLiteSupport.createTables(Document.class);
OrmLiteRepository<Document> documentRepository = ormLiteSupport.getRepository(Document.class);
documentRepository.create(newDocument);
documentRepository.create(document);
//when
TriggerOutput result = taskFactory.fromClass(TaskProcessor.class).runWithRecordsLimit(5);
// Data Store test
Assertions.assertThat(documentRepository.count()).isEqualTo(2);
Assertions.assertThat(documentRepository.findAll()).containsOnly(document, newDocument);
}
The table's lifecycle depends on the test scope in which it is created. For example, if you create a table in the BeforeAll scope, it is available across all tests. However, if you create it within a specific test, it only exists for the duration of that test.
OrmLiteSupport provides the following methods used for working with tables:
createTables(Class<?>... entities)creates tables.clearTables(Class<?>... entities)andclearAllCreatedTables()clear tables.dropTables(Class<?>... entities)anddropAllCreatedTables()drop tables.getRepository(Class<T> entity)retrieves a table repository.
dropAllCreatedTables() and clearAllCreatedTables() only affect the current test scope. If you call these methods inside a test method, they clean up or delete only the tables created within that test; tables created in the Before or After scopes remain unaffected.
worker-task-test-orm-lite-module requires both DataSource and ConnectionSource (or DatabaseType) beans. By default, the module provides only the ConnectionSource bean, which modifies all table names using the versioned template from DigitalWorkerInfo.
The DataSource bean is not included. You must define it manually or use one provided by the jnw-toolkit-datastores module.
If you do not want to set default ConnectionSource or DatabaseType, you can add the worker-task-test.connection-source.provide=false or worker-task-test.database-type.provide=false properties.
If you use multiple AI Agents in the same test class and need to manage repositories for each one individually, use MultiDigitalWorkerOrmLiteSupport instead of OrmLiteSupport in your test methods. The utility allows you to handle repositories just like OrmLiteSupport. At the same time, you can add a DigitalWorkerInfo object to the method and create a repository based on the data from the AI Agent information. Methods that do not require a DigitalWorkerInfo object as a parameter use the default DigitalWorkerInfo component or the one provided in the test context.
S3 module
The module provides a mechanism for mocking an Amazon S3 service for use in your tests.
It works similarly to the ODF 2 S3 mocking mechanism. For more details, refer to Bot Task JUnit | S3 mocking.
Setup
To enable the feature, add the worker-task-test-s3-module dependency to your project with the test scope and define the module version in worker-task-test-bom:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.workfusion.jnw.test</groupId>
<artifactId>worker-task-test-bom</artifactId>
<version>${wf.worker-task-test.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>com.workfusion.jnw.test.trigger</groupId>
<artifactId>worker-task-test-trigger-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.workfusion.jnw.test</groupId>
<artifactId>worker-task-test-s3-module</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
Usage
For each test run, an in-memory S3 server is automatically initialized to handle user requests. To interact with the server, inject the com.workfusion.jnw.test.s3.S3MockClient object into any test class method annotated with @Test, @BeforeAll, @BeforeEach, @AfterEach, or @AfterAll:
@BeforeAll
static void prepareDocument(S3MockClient s3Client) {
s3Client.createBucket("test-jnw-bucket");
}
@Test
void shouldGetDocumentByUuidWithRepositoryFromCode(TriggerTaskFactory taskFactory, S3MockClient s3Client) {
// given
s3Client.createBucket("jnw-bucket");
s3Client.putObject("jnw-bucket", "test_content.txt", "test_content");
// when
TriggerOutput result = taskFactory.fromClass(TaskProcessor.class).runWithRecordsLimit(5);
// then
TriggerResultAssert.assertThat(results).doesNotContainFailedRecordResult().isPartiallySuccessful()
.allSatisfyAssert(assertion ->
assertion.isFormatNull().hasData().singleElement().isNotEmpty().allSatisfy((key, value) ->
assertThat(value).isEqualTo("test_content")));
Assertions.assertThat(s3Client.getObjectAsString("jnw-bucket", "test_content.txt")).isEqualTo("test_content");
}
Explore the S3MockClient interface to view all methods and their descriptions.
Secret module
The module provides a mechanism for working with the secret storage in your tests. Its main purpose is to offer a convenient API for creating, updating, and clearing SecretDto objects in your tests.
Setup
To enable the feature, add the worker-task-test-secret-module dependency to your project with the test scope and define the module version in worker-task-test-bom:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.workfusion.jnw.test</groupId>
<artifactId>worker-task-test-bom</artifactId>
<version>${wf.worker-task-test.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>com.workfusion.jnw.test.trigger</groupId>
<artifactId>worker-task-test-trigger-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.workfusion.jnw.test</groupId>
<artifactId>worker-task-test-secret-module</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
Usage
For each test run, an in-memory secret storage service is initialized. To interact with the service, inject the JnwSecretService object into any test class method annotated as @Test, @BeforeAll, @BeforeEach, @AfterEach, or @AfterAll:
private final SecretDto beforeEachSecret = new SecretDto("beforeAlias", "beforeKey", "beforeValue", new Date());
@BeforeEach
void beforeEach(JnwSecretService secretService) {
secretService.createSecret(beforeEachSecret.getAlias(), beforeEachSecret.getKey(), beforeEachSecret.getValue());
}
@Test
@DisplayName("should read secret dto from secret service")
void shouldReadSecretDtoFromSecretService(TriggerTaskFactory taskFactory, JnwSecretService secretService) {
//given
final String secretAlias = "alias";
final String secretKey = "key";
final String secretValue = "value";
secretService.createSecret(secretAlias, secretKey, secretValue);
//when
TriggerOutput result = taskFactory.fromClass(TaskProcessor.class).runWithRecordsLimit(5);
//then
TriggerOutputAssert.assertThat(results).isSuccessful().allSatisfyAssert(successfulRequestResultAssert ->
successfulRequestResultAssert.dataHasOneElement()
.containsEntry("key", secretKey)
.containsEntry("value", secretValue);
}
JnwSecretService provides the following methods for working with SecretDto objects:
createSecret(String alias, String key, String value)creates aSecretDtoobject.updateSecret(String alias, String key, String value)updates aSecretDtoobject.deleteSecret(String alias)deletes aSecretDtoobject.getSecret(String alias)retrieves aSecretDtoobject fromSecretService.
The module's scope mechanism works the same way as in the previous modules. A secret created within a test is only available in that test, whereas a secret created in the Before or After scopes is available across all tests.
Do not override global secrets within a test scope as they will be deleted once the test completes.
View code examples
SamplePipelineProcessorWithMockServiceTest.class
package com.workfusion.jnw.test.processor;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import com.workfusion.connector.input.api.RecordResult;
import com.workfusion.jnw.configuration.TestConfiguration;
import com.workfusion.jnw.test.pojo.TaskConfiguration;
import com.workfusion.jnw.test.service.MockDataService;
import com.workfusion.jnw.test.trigger.assertions.TriggerOutputAssert;
import com.workfusion.jnw.test.trigger.junit.TriggerJunitConfig;
import com.workfusion.jnw.test.trigger.launcher.TriggerTaskFactory;
import com.workfusion.jnw.test.trigger.launcher.runner.TriggerTaskLauncher;
@TriggerJunitConfig(classes = {TestConfiguration.class})
public class SamplePipelineProcessorWithMockServiceTest {
private static final Map<String, Object> data = new HashMap<>();
private static TriggerTaskLauncher taskLauncher;
private final ObjectMapper objectMapper = new ObjectMapper();
@Autowired
public MockDataService dataService;
@BeforeAll
static void init(TriggerTaskFactory taskFactory) {
taskLauncher = taskFactory.createTaskRunner(SamplePipelineProcessor.class);
for(int index = 0; index < 10; index++) {
data.put(UUID.randomUUID().toString(), String.format("The data of record is equal of index: %d", index));
}
}
@BeforeEach
void setUp() {
dataService.cleanup();
dataService.addData(data);
}
@Test
@DisplayName("should run taskLauncher without configuration")
void shouldRunTaskLauncherWithoutConfiguration() {
TriggerOutputAssert.assertThat(taskLauncher.run()).isSuccessful().allSatisfyAssert(successfulRequestResultAssert ->
successfulRequestResultAssert.dataHasOneElement().containsAnyOf(data.entrySet().toArray(new Map.Entry[0])));
}
@Test
@DisplayName("should execute trigger from class with timeout")
void shouldExecuteTriggerFromClass(TriggerTaskFactory taskFactory) throws JsonProcessingException {
//given
final TaskConfiguration taskConfiguration = new TaskConfiguration(3, 500L);
final Collection<RecordResult> results = taskFactory.fromClass(SamplePipelineProcessor.class)
.withConfiguration(objectMapper.writeValueAsString(taskConfiguration))
.runWithTimeout(5L, TimeUnit.SECONDS)
.getAllTaskResults();
//then
TriggerOutputAssert.assertThat(results).isSuccessful().allSatisfyAssert(successfulRequestResultAssert ->
successfulRequestResultAssert.dataHasOneElement().containsAnyOf(data.entrySet().toArray(new Map.Entry[0])));
}
}
SamplePipelineProcessorTest.class
package com.workfusion.jnw.test.processor;
import java.util.Collection;
import java.util.concurrent.TimeUnit;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.workfusion.connector.input.api.RecordResult;
import com.workfusion.jnw.test.pojo.TaskConfiguration;
import com.workfusion.jnw.test.trigger.assertions.TriggerResultAssert;
import com.workfusion.jnw.test.trigger.junit.TriggerJunitConfig;
import com.workfusion.jnw.test.trigger.launcher.TriggerTaskFactory;
import com.workfusion.jnw.test.trigger.launcher.runner.TriggerTaskLauncher;
import com.workfusion.jnw.test.trigger.launcher.stoprule.TriggerStopRule;
import com.workfusion.jnw.test.trigger.launcher.stoprule.TriggerStopRuleFactory;
import static org.assertj.core.api.Assertions.assertThat;
@TriggerJunitConfig
class SamplePipelineProcessorTest {
private static TriggerTaskLauncher taskLauncher;
private final ObjectMapper objectMapper = new ObjectMapper();
private static final Logger logger = LoggerFactory.getLogger(SamplePipelineProcessorTest.class);
@BeforeAll
static void init(TriggerTaskFactory taskFactory) {
taskLauncher = taskFactory.createTaskRunner(SamplePipelineProcessor.class);
}
@Test
@DisplayName("should run taskLauncher without configuration")
void shouldRunTaskLauncherWithoutConfiguration() {
assertResults(taskLauncher.run().getAllTaskResults());
}
@Test
@DisplayName("should execute trigger from class with timeout")
void shouldExecuteTriggerFromClass(TriggerTaskFactory taskFactory) throws JsonProcessingException {
//given
final TaskConfiguration taskConfiguration = new TaskConfiguration(3, 500L);
final Collection<RecordResult> results = taskFactory.fromClass(SamplePipelineProcessor.class)
.withConfiguration(objectMapper.writeValueAsString(taskConfiguration))
.runWithTimeout(8L, TimeUnit.SECONDS)
.getAllTaskResults();
//then
assertResults(results);
}
@Test
@DisplayName("should execute trigger with records limit")
void shouldExecuteTriggerWithRecordsLimit() throws JsonProcessingException {
//given
final TaskConfiguration taskConfiguration = new TaskConfiguration(1, 1000L);
//when
final Collection<RecordResult> results = taskLauncher.withConfiguration(objectMapper.writeValueAsString(taskConfiguration)).runWithRecordsLimit(1).getAllTaskResults();
//then
assertResults(results);
}
@Test
@DisplayName("should execute trigger with timeout rule")
void shouldExecuteTriggerWithTimeoutRule() {
//when
final Collection<RecordResult> results = taskLauncher.runWithTimeoutStopRule(timeoutTriggerStopRule -> timeoutTriggerStopRule.withTimeout(5L, TimeUnit.SECONDS)).getAllTaskResults();
//then
assertResults(results);
}
@Test
@DisplayName("should execute trigger with records rule")
void shouldExecuteTriggerWithRecordsRule() {
//when
Collection<RecordResult> results = taskLauncher.runWithRecordsStopRule(recordsTriggerStopRule -> recordsTriggerStopRule.withDelay(0L)
.withInterval(1L)
.withRule(container -> container.getRecords().size() >= 2)).getAllTaskResults();
//then
assertResults(results);
}
@Test
@DisplayName("should execute trigger with timeout in millis")
void shouldExecuteTriggerWithTimeoutInMillis() {
//when
final Collection<RecordResult> results = taskLauncher.runWithTimeout(8000L).getAllTaskResults();
//then
assertResults(results);
}
@Test
@DisplayName("should throw exception if rule timeout is done")
void shouldThrowExceptionIfRuleTimeoutIsDone(TriggerStopRuleFactory stopRuleFactory) {
//given
final TriggerStopRule rule = stopRuleFactory.recordsTriggerStopRule()
.withTimeout(2L)
.withRule(container -> false)
.build();
//then
Assertions.assertThatThrownBy(() -> taskLauncher.runWithStopRule(rule)).isInstanceOf(IllegalStateException.class).hasMessageStartingWith("Trigger execution failed with timeout");
}
@Test
@DisplayName("should throw exception if rule failed")
void shouldThrowExceptionIfRuleFailed(TriggerStopRuleFactory stopRuleFactory) {
//given
final TriggerStopRule rule = stopRuleFactory.recordsTriggerStopRule()
.withDelay(1L)
.withRule(container -> {
throw new IllegalArgumentException("Test message");
})
.build();
//then
Assertions.assertThatThrownBy(() -> taskLauncher.runWithStopRule(rule))
.isInstanceOf(IllegalStateException.class)
.hasMessageStartingWith("Rule execution failed with error: ");
}
public static boolean assertResults(Collection<RecordResult> results) {
logger.warn("Record size in result is: {}", results.size());
TriggerResultAssert.assertThat(results).doesNotContainFailedRecordResult().isPartiallySuccessful()
.allSatisfyAssert(assertion -> assertion.isFormatNull().hasData().singleElement().isNotEmpty().allSatisfy((key, value) -> assertThat(value).isEqualTo("some_data")));
return true;
}
}