Testing
motions test
Section titled “motions test”npx motions testRuns every motions/*/tests/**/*.test.ts with the tenant’s own Vitest, which must be in its devDependencies. It passes when there are no tests. It doesn’t run tests elsewhere, such as a custom source’s sources/*/index.test.ts: run those with npx vitest run sources.
Tests run in Node, with the step code as it’ll run in a batch: the same harness, the same frozen clock and seeded randomness, and source calls answered by the same matcher as fixture mode.
runStep
Section titled “runStep”Runs one step on one company, or a batch step on several. Its options and result are in the SDK reference; how to use it is in Test a motion.
| Option | Default |
|---|---|
company |
a company keyed co_1, named after its key, with every core field null |
fixtures |
the tenant’s fixtures/sources/, found by walking up from the working directory to tenant.yaml |
crm |
an empty CRM |
rules |
{} |
calendar |
{} |
now |
the real time |
The CRM in a test filters the way the test CRM does: tax_id ignoring punctuation, tax_root by prefix, domain ignoring scheme and www., name as a case-insensitive substring. Proposals are recorded but not checked against records, so proposals[].before is null.
Fixtures
Section titled “Fixtures”A fixture is a recorded answer to a source call. The same files serve three purposes:
- Tests:
runStepanswers every source call from them, built-in and custom. - Fixture mode: a tenant with
sources: { fixtures: true }answers built-in calls from them in real batches. - Documentation: they show what each provider returns for your cases.
[ { "op": "rdap.lookup", "request": { "domain": "aurorametais.example" }, "data": { "registrant": "METALURGICA AURORA LTDA" } }, { "op": "rdap.lookup", "request": { "domain": "nuvemclara.example" }, "data": null, "note": "nobody registered it" }, { "op": "fullenrich.enrich_bulk_get", "request": {}, "sequence": [{ "data": { "status": "pending" } }, { "data": { "status": "done", "contacts": [] } }] }]Matching:
- Files in
fixtures/sources/are read in name order, entries in file order. The first entry whoseopmatches and whoserequestis a subset of the call’s request wins. - A subset means every key the fixture gives is in the call with an equal value, recursively. Arrays must be equal.
{}matches anything. - No match fails the call with
fixture_missing: “no fixture matches<op><request>; add one to fixtures/sources”.
Answers:
data: returned as the call’s result.nullreads as “not found”.error:{ "code": "rate_limited", "message": "…" }fails the call with that class. Codes:not_found,bad_input,not_allowed,no_credit,budget,rate_limited,transient,config.sequence: one answer per identical call, in turn; the last repeats. In a batch, calls are counted per batch. In tests, pass the samefixtureCallsmap to severalrunStepcalls to walk through it.
In fixture mode, calls cost nothing and aren’t cached, so editing a fixture takes effect on the next run.
Fixture mode
Section titled “Fixture mode”| Fixture mode on | Fixture mode off | |
|---|---|---|
| Built-in sources | answered from fixtures, free | call the provider, with a key, and spend |
| Custom sources | call their real API | call their real API |
| Source keys | not used | Settings → Source keys |
| Caching | none | per operation’s cache time |
Turn it on or off in tenant.yaml and push. motions check and push print which it is and how many fixtures there are.
The simulated clock
Section titled “The simulated clock”A batch can run on a simulated clock that only moves when an approver advances it. Waits, retries, deadlines, checkpoint cutoffs and calendar instants then happen in order, as fast as the steps run, so a week of a motion plays out in minutes.
It’s only available on a tenant in fixture mode on the test CRM, and is started when opening the batch through the API:
POST /api/tenants/<tenant>/motions/<motion>/batchesAuthorization: Bearer <your API token>Content-Type: application/json
{ "list": "first-batch", "label": "week rehearsal", "clock": { "start": "2026-11-09T08:00:00-03:00" } }The batch screen then shows Advance the simulated clock, to the next thing due or to a time you choose.
Motions is built by Pruma. These docs describe what the platform does today; Limitations lists what it doesn't do yet. Open the app