Aviary

Testing

Unit-test workflows with an in-memory engine harness, crash/flaky-step injection, and replay assertions — no Postgres, no Redis, no real time.

@dudousxd/nestjs-durable-testing runs a whole workflow in a unit test — in-memory store and transport, a clock you control, and assertions that read back the recorded state.

pnpm add -D @dudousxd/nestjs-durable-testing

A test engine

import { createTestEngine, assertRunStatus, assertOutput } from '@dudousxd/nestjs-durable-testing';

const t = createTestEngine(); // { engine, store, transport, clock, tick }
t.engine.register('checkout', '1', async (ctx) => {
  await ctx.step('reserve', () => reserve());
  return ctx.step('ship', () => ship());
});

const { runId } = await t.engine.start('checkout', order, 'run1'); // enqueues → { status: 'pending' }
await t.engine.waitForRun(runId);                                  // resolves when the run settles
await assertRunStatus(t.store, 'run1', 'completed');

Control time (durable sleep)

tick(ms) advances the clock and resumes any durable sleep that is now due — no waiting:

t.engine.register('digest', '1', async (ctx) => {
  await ctx.step('draft', () => draft());
  await ctx.sleep('7 days');
  await ctx.step('send', () => send());
});

const { runId } = await t.engine.start('digest', {}, 'run1'); // enqueues → { status: 'pending' }
await t.engine.waitForRun(runId);                             // settles on the durable sleep → suspended
await t.tick(7 * 24 * 60 * 60 * 1000);                        // the sleep is due → completes
await assertRunStatus(t.store, 'run1', 'completed');

Inject crashes & retries

failOnce / failTimes make a step throw before succeeding — to drive retries and resume:

import { failOnce, assertStepAttempts } from '@dudousxd/nestjs-durable-testing';

t.engine.register('wf', '1', async (ctx) =>
  ctx.step('charge', failOnce({ ok: true }), { retries: 3 }),
);
const { runId } = await t.engine.start('wf', {}, 'run1');
await t.engine.waitForRun(runId);
await assertStepAttempts(t.store, 'run1', 'charge', 2); // failed once, then succeeded

Assertions

assertRunStatus, assertOutput, assertStepsRan, assertStepAttempts, and recordedSteps — all read the store, so they work against any run the engine produced.

On this page