Testing
1. Examples: the spec
Section titled “1. Examples: the spec”Every handler carries examples, and atlas test runs them all:
npx atlas test --dir src/capabilities| Handler | An example gives | And expects |
|---|---|---|
| Command | input, state, optional actor |
emits, writes, removes, sends, schedules, facts, or rejects |
Reaction (.on) |
fact, state |
the same effects, or rejects |
| Projection | fact |
rows (partial match, in order) |
| View query | input, rows, optional actor, state, now |
items, or rejects |
| Record query | input, state, optional actor, now |
result, or rejects |
Example state is partial: missing reads default to null. An example that sets actor (even null) enforces access; without it, it runs as SYSTEM.
2. Flows across reactions
Section titled “2. Flows across reactions”dispatch returns before reactions run. In a test, build a runtime over the memory store and drain() before asserting:
import { test } from 'node:test';import assert from 'node:assert/strict';import { createRuntime } from '@ts-atlas/core/runtime';import { loadSystem } from '@ts-atlas/core/system';
test('an order shows up in MyOrders', async () => { const system = await loadSystem('src/capabilities'); const runtime = createRuntime({ capabilities: system.capabilities.map((c) => c.capability) }); const ada = { id: 'c1', roles: [] }; await runtime.dispatch({ type: 'PlaceOrder', orderId: 'o1', lines: [{ sku: 'mug', quantity: 1, unitPriceCents: 900 }] }, { actor: ada }); await runtime.drain(); // reactions and projections are delivered after the command commits const page = await runtime.query('MyOrders', {}, { actor: ada }); assert.equal(page.outcome, 'ok');});drain() delivers until every consumer is caught up, bounded by maxSteps handler runs per call. Tests that use drain() do not need await: 'views'.
3. Time
Section titled “3. Time”Inject the clock and move it. drain() releases only scheduled entries already due by the runtime clock:
import { time } from '@ts-atlas/core/runtime';
let at = '2026-01-01T00:00:00.000Z';const runtime = createRuntime({ capabilities, clock: () => at });// ... dispatch something that schedules ExpireOrder in one hourat = time.add(at, 3_600_000);await runtime.drain();4. Concurrency
Section titled “4. Concurrency”createRuntime({ beforeCommit }) is a test seam that runs between decide and commit (commands and reactions), so a test can simulate a concurrent writer or a crash. The repository’s test/retry.test.ts and test/batch.test.ts use it.
5. Observability in tests
Section titled “5. Observability in tests”runtime.trace() returns the newest events (traceLimit, default 1000); observe(event) sees every one. Each event has capability, handler, trigger, outcome (accepted, rejected, failed, warning), code, reason, emitted, sent and attempts.
6. A custom store
Section titled “6. A custom store”Run the shared contract suite from a *.test.ts file:
storeContract('my-db', async () => ({ store, close }));It exercises the commit outcomes (conflict and poison included), depth, leases and holders. Inside the Atlas repository, Postgres tests run with npm run test:pg (serially: the delivery horizon is cluster-wide).