Testing and evidence

Conformance, simulation, concurrency, property tests, golden and interop vectors, fuzz targets, and compiled docs.

On this page
  1. What to call
  2. Conformance: one contract, every store
  3. Property tests
  4. Golden and RFC 6962 vectors
  5. C2SP interop, both ways
  6. Fuzz targets
  7. Compiled documentation
  8. Limits

What to call

just ci                 # everything CI runs, in CI order (Docker for PostgreSQL)

Conformance: one contract, every store

The same public suite runs against MemoryStore, SQLite and PostgreSQL, and a third-party store checks itself the same way. Each check runs on a fresh store from your factory:

  • tables: one row per rejection code (plus precedence, replays, reversals, clearings, closing), each asserting the store answers exactly what the in-memory Journal answers, field for field;
  • simulation: seeded workloads of valid and mutated operations with retries of used keys, reads compared with an independent model, store invariants and an audit() at the end; by default 8 seeds of 200 steps;
  • concurrency: real tasks racing appends, batches, retries, draws on limited accounts, a seal against an append into its period, memo erasures and hold resolutions;
  • independent handles: the same races through two handles on one database, for a lock that must hold across processes.

A failure names the check, seed and step, and how to rerun it: DOUBLEENTRY_SEED=<seed> DOUBLEENTRY_CHECK=<check> cargo test ….

use doubleentry::store::MemoryStore;
use doubleentry::testing::{Config, conformance_with};

// A third-party store: `conformance(|| async { MyStore::fresh().await })`.
let config = Config { check: Some(String::from("tables/1")), ..Config::default() };
let report = conformance_with(|| async { MemoryStore::new() }, config).await?;
assert!(report.checks.iter().all(|(name, _)| name.starts_with("tables/1")));

The suite is itself tested: BrokenStore has eight faults (forgotten keys, no lock, stale balances, a late seal, lost memos, resolved holds kept open, through ignored, no open holds by account), and a test asserts the suite fails on each.

just conformance        # memory, SQLite, PostgreSQL (Docker)

Property tests

proptest checks laws over input spaces, 1 024 to 100 000 cases each: every record kind round-trips its encoding and refuses damage, random bytes never panic the decoder, a full entry and seal fit the leaf limit, allocations conserve their total, export pairs re-sum to the entry, report filters partition the balance, holds follow their model.

cargo test --all-features --test properties --test record_properties --test model

Golden and RFC 6962 vectors

tests/vectors/golden.txt pins the leaf bytes and hash of every record kind, a reference log root, the entry id, memo commitment, draft fingerprint and balance-tree constants. It is written by an independent reference encoder (tests/vectors/ref_encoder.py), and tests/golden.rs compares the crate with it byte for byte. tests/log.rs checks the RFC 6962 test-tree vectors, compares every proof up to 64 leaves with a recursive reference, and requires every deformed proof to fail.

C2SP interop, both ways

Vectors generated by the Go reference implementations are committed and read on every test run; just interop (CI job, needs Go) regenerates them and has Go verify the crate's checkpoints, requests, bundles, policies and tiles, including through the stock witness. Details: Transparency.

just golden             # crate vectors and the committed Go vectors, no Go needed
just interop            # Go → crate and crate → Go

Fuzz targets

Every parser of hostile input has a cargo-fuzz target, compiled in CI (just fuzz-build):

TargetInput
recordleaf bytes into Record::decode
notesigned notes and checkpoints
witness_responsea witness's add-checkpoint reply
policytlog-policy text
bundletlog-proof bundles
serdeJSON into every public Deserialize type
valuestext into every value parsed from a string
just fuzz record 120    # run one target for 120 s (nightly, cargo-fuzz)

Compiled documentation

Every Rust block on this site, in the README and in the rustdoc is compiled, and run unless it needs a server; the programs under examples/ run and assert their outcomes. A docs guard refuses removed API names, checks every docs.rs link against the reviewed public API, and checks that every test the GoBD matrix cites exists.

just site-verify && just docs-stale && just examples

Limits

  • No formal verification: properties are tested, not proven.
  • Concurrency checks run real races, not a model checker.