Property tests
Randomized suites over the pure cores where golden fixtures only pin examples — the laws they assert, the never-widen discipline, and why a counterexample seed is committed.
Golden fixtures pin examples somebody chose. Property tests pin the laws those examples were meant
to illustrate, over inputs nobody chose. proptest is a workspace dev-dependency, pinned in the root
manifest with its rationale: randomized inputs over the pure cores — sizing, the pinned round and
format wire sites, the risk-gate parity — find edges the golden fixtures miss. It is dev-only, so it
never reaches the shipped order-signing binary's embedded SBOM, though it does join the
source-lockfile supply-chain tree, which the manifest records as the accepted cost of the workspace's
first property framework.
The suites and the laws they hold
crates/vike-model/tests/scalar_props.rs — the grid-rounding primitives every live order's price
and quantity flows through. Idempotence is the central law and also the robust statement that the
result lies exactly on the step grid: a rounded value is a fixed point of the rounding. It is
asserted with exact equality, no tolerance.
crates/vike-bridge-core/tests/format_props.rs — the primary pinned Decimal wire site. Every
step in its generator is a terminating decimal, so the internal Decimal division is exact for these
inputs, which is what lets every assertion be an exact Decimal comparison with no numeric
tolerance. As the header puts it, a failure is therefore a genuine wire bug — a real overshoot or
precision leak — never a rounding artifact to be papered over.
crates/vike-analytics/tests/sizing_props.rs — the eight position sizers. Its header records why
it exists: Sizer::size had zero direct tests, touched only indirectly by one golden run, making it
the largest money-path coverage hole in the workspace.
crates/vike-backtest/tests/riskgate_simbroker_parity.rs — the randomized live-versus-backtest
risk parity. Four real money bugs were found on one day in 2026 by manually asking "where is this
rule implemented twice?", and not one had been caught by a test. The suite's own framing is that
the pinning is the value: an unpinned divergence fails the test, and a newly pinned one is a
loud, reviewable diff rather than a silent behaviour change.
crates/vike-exec/tests/order_fsm_props.rs — six laws folded over arbitrary lifecycle-event
sequences against the one mutator of order state: terminal absorption, error purity (a refused apply
never mutates), fill monotonicity and VWAP boundedness, at most one live-to-terminal transition, the
liquidated trap, and the fill-derived restore.
Parser totality suites — crates/bridges/binance/tests/mapper_props.rs and its bybit twin feed
the real mappers arbitrary JSON, arbitrary text, and every deterministic mutation of every committed
captured fixture. The property is totality: a hostile or truncated frame may decode to nothing, but
it must never panic the user-data pump thread and must never fabricate an event flood — bounded by
MAX_EVENTS_PER_FRAME. crates/bridges/ctrader/tests/framing_props.rs does the same for the wire
framing every inbound byte crosses first, including the length-prefix denial-of-service edge: a
claimed four-gibibyte frame must not be allocated up front.
The three rules
A counterexample is a real finding, not a property to relax
The FSM suite states the failure policy for all of them: stop and report with the seed proptest
prints; never adjust the subject to satisfy a property without an explicit decision. Widening a
tolerance to make a property pass is the same move the
parity gate forbids, arriving from a different direction.
Commit the regression seed. The workspace convention, stated in the root manifest beside the
dependency itself, is that any generated proptest-regressions/ file is committed, so a CI-found
counterexample reproduces on the next run and on every developer's box.
A slack is documented or it is a fudge. scalar_props allows exactly one floating-point
epsilon — a half-step bound whose slack covers the multiply's rounding, orders of magnitude below the
half-step it guards, so it cannot hide a genuinely off-grid result. Everything else in that file and
all of format_props is exact. The sizing suite carries a few-ULP round-trip epsilon on its two cap
properties and says so inline.
A domain bound is an argument, not a convenience. scalar_props keeps values and steps inside
the real price and tick range so value / step stays under the exact-integer ceiling of f64.
Beyond that ceiling the multiple itself is unrepresentable and idempotence would fail as a
fundamental floating-point limit rather than a bug — and no venue quotes a 1e-8 tick on a 1e6
price.
The trap a property suite is uniquely prone to
A property over an arm the generator never reaches is vacuously green. The FSM suite answers that with a generator-coverage witness that fails by name when a lifecycle variant goes missing from its event generator. The binance parser suite answers it differently, by mixing real dispatch keys into the generator so it actually reaches the mapper's arms instead of bouncing off the first lookup. Both are the same defence: a randomized suite must prove its randomness arrives somewhere.
Where a property suite may live
Property suites always get their own test binary. crates/vike-backtest's tests/ are grouped into
shared binaries to cut link time, and the admission rule in that crate's own notes excludes a file
carrying a proptest regression sidecar — along with #![cfg] feature gates, #[ignore]d tests and
any process-global mutation — because one binary runs its tests as threads in one process. See
testing for the tiers, and
adding to the parity suite for the procedure.
Roster exhaustiveness
One canonical venue list, and the completeness tests that force every table and every harness to classify each member — covered, or deferred with a written reason.
Test doubles
One owned double per seam, behind a `test-support` feature a default build never compiles — the scripted WS streams, the in-memory history store, the recording broker, and the sanitized capture writer.