Expand description
An executable statement of the executor contract (I13, I14, spec §23.1).
A store proves itself against turnframe_store::conformance and a provider
adapter against providers::conformance.
The third thing an adopter writes is the WorkflowExecutor, and it is
where optimistic concurrency and idempotency actually live: a card bound to
revision N is only safe because some executor refuses a stale write, and a
turn replayed after a crash is only harmless because some executor
recognises the key it already committed. Those rules are prose on the trait,
and prose does not fail a build.
The check nobody writes for themselves is partial-batch recovery, which
is why ExecutorFactory::interrupt_after exists: the suite cannot
half-commit a batch through the trait, so it asks the implementation to.
docs/recipes.md
describes the two shapes that defect usually takes.
Nothing here panics: every check returns a result, run_all collects them
all rather than stopping at the first, and a failure names revisions,
digests, error variants and check names — never a case’s state, which is the
adopter’s data and is compared as a digest.
§Running it
Implement ExecutorFactory once, then hand it to run_all.
use turnframe_test::executors;
use turnframe_test::workflows::trip::conformance_case;
let report = executors::run_all(&conformance_case()).await;
assert!(report.passed(), "{report}");
assert_eq!(report.outcomes.len(), executors::CHECK_COUNT);InMemoryCase is the reference factory: it wires the kit’s own
InMemoryExecutor to any
PureWorkflow, and it is the smallest
complete example of what an adopter writes to point the suite at their own
executor.
While an executor is still being written, run one rule at a time: every
check_* function is public and takes the same factory.
§It never panics
Every check returns Result<(), ConformanceFailure>
and run_all collects the results into a ConformanceReport without
stopping at the first failure — a broken executor usually breaks several
rules at once, and seeing all of them is faster to fix than seeing the first
one seven times. Nothing here unwraps, asserts or panics, so the suite is
usable outside a test harness: in a migration tool, or as a boot-time gate
on a freshly written adapter.
Failure details name revisions, digests, error variants and check names.
They never render a case’s state, because that state is the adopter’s data:
two states are compared through
canonical_digest, and a
mismatch is reported as two digests.
§What is covered
| Check | Rule |
|---|---|
check_stale_expected_revision_is_a_conflict | a batch planned against a superseded revision is refused, and the case is not overwritten (I13) |
check_commit_reports_the_revision_it_reached | the revision in the commit is the one a later batch must be planned against |
check_repeated_key_replays_the_outcome | a key seen before returns the original outcome and repeats no effect (I14) |
check_repeated_key_with_another_command_is_refused | the same key carrying a different command is a mismatch, never a replay (I14) |
check_per_case_batch_is_all_or_nothing | one refused envelope discards the whole batch, and a PerCase batch may not span two cases |
check_interrupted_batch_resumes_to_the_same_state | a batch that half-committed resumes to exactly the state an uninterrupted one reaches |
check_refused_command_leaves_the_case_byte_identical | a refusal writes nothing at all, and stays a refusal when it is retried |
Structs§
- Check
Outcome - What one check concluded.
- Conformance
Failure - One rule of the executor contract that an implementation broke.
- Conformance
Report - The result of a whole
run_all. - InMemory
Case - An
ExecutorFactoryoverInMemoryExecutor, for any workflow whose transitions are a pure function. - Seeded
Case - A fresh executor, the case seeded in it, and the commands the suite drives it with.
Constants§
- CHECK_
COUNT - How many checks
run_allruns. - CONFORMANCE_
ACCOUNT - Account the reference factory seeds its case in.
- CONFORMANCE_
USER - User the reference factory acts as.
Traits§
- Executor
Factory - Builds a fresh executor with one case in it, once per check.
Functions§
- check_
commit_ reports_ the_ revision_ it_ reached - The revision a commit reports is the revision the case actually reached, and the one the next batch must be planned against.
- check_
interrupted_ batch_ resumes_ to_ the_ same_ state - A batch that half-committed resumes to exactly the state an uninterrupted one reaches (spec §23.1).
- check_
per_ case_ batch_ is_ all_ or_ nothing - A
PerCasebatch commits entirely or not at all, and never spans two cases. - check_
refused_ command_ leaves_ the_ case_ byte_ identical - A command the domain refuses leaves the case byte-identical, and stays a refusal when it is retried.
- check_
repeated_ key_ replays_ the_ outcome - A key the executor has already committed returns the original outcome and repeats no effect (I14).
- check_
repeated_ key_ with_ another_ command_ is_ refused - A key that arrives with a different command is a mismatch, never a replay (I14).
- check_
stale_ expected_ revision_ is_ a_ conflict - A batch planned against a revision that is no longer current is refused, and the case is left exactly as it was (I13).
- run_all
- Runs every check against a fresh executor each and reports.
Type Aliases§
- BatchOf
- The batch type the suite hands to the executor under test.
- Command
Of - The command type of
WorkflowOf<F>. - SeedOf
- The
SeededCaseanExecutorFactoryproduces. - Workflow
Of - The workflow an
ExecutorFactorybuilds executors for.