Skip to content

Testing your flows

Because Rungs takes its time, IDs and approvers from you, a flow is easy to test: use the memory store, a clock you move, and predictable IDs.

// Your flow, on the memory store, with a clock the test moves. No database, no waiting.
async function setup() {
let now = new Date("2026-03-02T09:00:00.000Z");
const rungs = createRungs({
store: memoryStore(),
clock: () => now,
ids: (() => {
let n = 0;
return () => `r${++n}`; // predictable ids
})(),
events: "discard",
});
await rungs.flows.register(expenseFlow);
return { rungs, advance: (hours: number) => (now = new Date(+now + hours * 3_600_000)) };
}
// Inside your test runner's it(...):
const { rungs, advance } = await setup();
const request = await rungs.submit({
flow: "expense",
requesterId: "u_req",
subject: { type: "expense", id: "EXP-1" },
payload: { amount: 10 },
});
advance(49);
await rungs.tick();
const { tasks } = await rungs.inbox({ userId: "u_dir" });
assert.deepEqual(
tasks.map((t) => t.requestId),
[request.id],
);

Runs in CI:examples/docs-snippets/src/guides/testing.ts

To explore a flow by hand, write a scenario for the playground. It prints every step, approver and decision as a readable timeline:

Terminal window
pnpm playground scenarios/02-manager-on-leave.ts

If you write your own RungsStore, the repository’s @rungsjs/store-contract suites check it against the same behavior the memory and PostgreSQL stores pass; see the store contract.