The nine axes

An axis is one kind of question you can ask of a rule. Nine of them cover the behaviour a type cannot state. You declare a rule; the library asks every question that applies to its shape.

Axis The question
01 Invariant Is it true in every state?
02 Idempotency Does doing it twice change anything?
03 Monotonicity Does it ever go backwards?
04 Conservation Did anything appear or vanish?
05 Absence Can this bad sequence ever happen?
06 Temporal Does B follow A within a deadline?
07 Liveness Does it eventually finish?
08 Confluence Does the order inputs arrive in matter?
09 Vacancy Did the test even reach the rule?

Each page is built the same way: the question in plain words, the bug you would ship, the rule you declare, what the library does, and the real failure you read. Start with whichever sounds like a bug you have had.

The rule is about Axes
One state 01 Invariant
A step from one state to the next 02 Idempotency, 03 Monotonicity, 04 Conservation
Events over time 05 Absence, 06 Temporal, 07 Liveness
The order inputs arrive in 08 Confluence
The test itself 09 Vacancy
await AutoTest.For(OrderConstraints.Instance, orders).RunAsync();
await EventAutoTest.For(rules, script, time => new Service(time), s => s.Log).RunAsync();
		
  1. the state axes: 01 to 04, 08 and 09
  2. the event axes: 05 to 07

RunAsync() throws one exception listing every axis that failed. To run one: InvariantAsync(), IdempotencyAsync(), MonotonicityAsync(), ConservationAsync(), ConfluenceAsync(), VacancyAsync(); for events AbsenceAsync(), TemporalAsync(), LivenessAsync(). For your test framework, AsDataSource() gives one test per axis the rules declare, and every case is awaited with RunAsync().

An axis the type has no rule for is reported as not applicable; it is never a silent pass. RequireAxes(...) turns "not applicable" into a failure when you expect a rule there.

Every failure has the same labels, so you learn them once.

Label Meaning
Rule broken The rule, by name, and what it required
Operation / Calls made What was done to cause it
Started from / Produced The value before and after
Events recorded For the event axes: what the service did, with times
Law The question this axis asks
Consequence What goes wrong in production if it fails
Smallest case found The library tried smaller cases and kept the smallest that still fails
.Seed("…") Pass it back to repeat exactly this case while you fix the bug

The concepts page explains smallest case and seed.