Skip to content

Testing processes

A process test checks that your process model does what you expect. It deploys your BPMN, DMN and forms into a real engine and starts a process instance. It then drives the instance forward (completing jobs, moving time, completing user tasks) and checks what the engine recorded.

TinyConductor runs these tests on the Embedded engine. It is the same engine as Bundled and Cluster mode, but it keeps nothing on disk, needs no network and has a virtual clock that you control.

  • Same behaviour. The path an instance takes, the records, incidents, FEEL and DMN are the same as in the production modes. The modes differ only in durability, speed, capacity and tenancy (see Embedded mode).
  • Nothing else to install. No database and no volume. An engine is created in milliseconds and thrown away with everything it held.
  • A virtual clock. Time starts at a fixed epoch (2026-07-19T00:00:00Z) and stands still until the test moves it.
  • Deterministic. Calls are processed one at a time, in order, so the same test produces the same records every run.

The clock never moves on its own. You advance it with a single call: POST /v1/embedded/clock/advance-by with {"milliseconds": 86400000}, or advance-to with {"instant": "2026-08-01T00:00:00Z"}.

The call returns only when every timer due at the new instant has fired and all the work it caused is in the record stream (jobs created, messages expired, further engine steps). Your test reads the result straight after the call. It never sleeps and never polls.

OperationServer formAnswer
read the clockGET /v1/embedded/clock{"mode":"embedded","frozen":true,"instant":…,"epochMillis":…}
advance by a durationPOST /v1/embedded/clock/advance-by {"milliseconds": n}the new clock
advance to an instantPOST /v1/embedded/clock/advance-to {"instant": "<RFC 3339>"}the new clock; 400 invalid-argument if it is in the past
start overPOST /v1/embedded/resetdrops every deployment, instance, record and document, and returns the clock to the epoch

The clock only moves forward. The v2-shaped PUT /v2/clock and POST /v2/clock/reset drive the same clock (see Embedded mode).

A record is one entry in the engine’s log of what happened, such as “a job was created” or “an element completed”. Everything the engine did is in this record stream, in order. GET /v1/embedded/records answers {"records": [...]}. Each record carries position, key, timestamp (microseconds), recordType, valueType (PROCESS_INSTANCE, JOB, USER_TASK, VARIABLE, TIMER, DECISION_EVALUATION, …), intent and a value. For example, the instance itself completed when a record has valueType PROCESS_INSTANCE, intent ELEMENT_COMPLETED, and a value whose elementId is the process id and whose processInstanceKey is your instance:

{"position": 58, "key": "94575592174780421", "timestamp": 1784505600000000,
"recordType": "EVENT", "valueType": "PROCESS_INSTANCE", "intent": "ELEMENT_COMPLETED",
"value": {"bpmnProcessId": "order-process", "processInstanceKey": "94575592174780421",
"elementId": "order-process", "bpmnElementType": "PROCESS", …}}

Keys are JSON strings on the wire, so they survive languages whose numbers lose precision above 2^53. The same records answer “which elements completed”, “which variables were set” and “was an incident raised”. The history reads (GET /api/v1/history/process-instances/{key}) and the searches work in Embedded mode too, if you prefer a summarized view.

Give every test a clean engine: create a new in-process engine per test, or call POST /v1/embedded/reset before each test against a server.

In-processServer
What runsthe engine inside your test process, as a librarytinyconductor --mode embedded, one process your tests call over HTTP
Languages.NET (SDK), C and any language with a C FFI; a Rust crate is plannedany language with an HTTP client, Rust included
Get itthe library for your language (see each guide)the registry.tinyfactory.ai/tinyblox/tinyconductor container image, or the tinyconductor program from a release archive
APItyped SDK, or JSON operations through the C ABIthe REST API — the same routes every mode serves, plus /v1/embedded/*
Isolationone engine per testPOST /v1/embedded/reset between tests

Start the server form from the container image:

Terminal window
export TINYCONDUCTOR_TOKEN=test-token
docker run --rm -d --name tinyconductor-test -p 127.0.0.1:8080:8080 \
-e CONDUCTOR_AUTH_CREDENTIALS_JSON='[{"token":"test-token","subject":"tests","tenantId":"default","kind":"service","actions":["*"]}]' \
registry.tinyfactory.ai/tinyblox/tinyconductor:0.1.0 --mode embedded

A container engine listens beyond loopback, so it needs a credential; the tests send the token from TINYCONDUCTOR_TOKEN as Authorization: Bearer ….

Or run the tinyconductor program from the release archive for your platform (the list of downloads is in Embedded mode):

Terminal window
./tinyconductor-0.1.0-linux-x86_64/tinyconductor --mode embedded

Started this way it listens on 127.0.0.1:8080 and answers local callers without a credential. In a pipeline, see CI recipes.

Every guide walks through the same test of the shared order sample (order.bpmn): deploy, start an order, complete the charge-card job, advance the clock past the 24-hour review timer, complete send-invoice, complete the confirm-shipment user task, and assert that the instance completed. Each has a runnable project in the examples download, under testing/.

LanguageFormGuideExample
C# / .NETin-process SDK.NETtesting/dotnet
Rustserver + REST (in-process crate planned)Rusttesting/rust
C, and any FFI languagein-process C ABIC and FFItesting/c
Javaserver + RESTJavatesting/java
TypeScript / Node.jsserver + RESTTypeScripttesting/typescript
Pythonserver + RESTPythontesting/python
Goserver + RESTGotesting/go

Starting the engine in a pipeline: CI recipes.

The sample order process and the runnable projects are in the examples download of the release, tinyconductor-0.1.0-examples.tar.gz, next to the engine downloads (where to download them is given with your access). Unpack it:

Terminal window
tar -xzf tinyconductor-0.1.0-examples.tar.gz

The unpacked tinyconductor-0.1.0-examples directory holds order.bpmn, order.form and order-decisions.dmn at the top, and one project per language under testing/. Each project reads the sample files from the top directory, so keep the layout as it is.

The examples need nothing but release files. The C and .NET projects look for the release archive for your platform unpacked next to the examples, and the .NET project for the TinyConductor.Testing package file, so put the downloads in one folder:

downloads/
tinyconductor-0.1.0-examples/ (the examples download, unpacked)
tinyconductor-0.1.0-linux-x86_64/ (the release archive for your platform, unpacked)
TinyConductor.Testing.0.1.0.nupkg (the .NET testing SDK, for .NET only)