← Blog

61 tests — what they cover, why it matters


The contract has 61 tests. They run in 0.15 seconds. cargo test on a clean checkout, no fixtures, no network. This post is the map — what each test file proves, why we organized it that way, and where the gaps are.

Source: github.com/fairb-dev/Fairfoundry/tree/main/contracts/fairfoundry/src/test.

The breakdown

File # What it proves
services.rs ~30 The whole service-fabric pipeline — create order, accept, submit, attest, settle, redeem, cancel, multi-stage, view/filter
flows.rs ~6 End-to-end happy-path lot lifecycle: escrow → create lot → QA commit → counts → reinspect → respond → execute_payment
negative.rs ~10 #[should_panic] tests covering auth failures, role mismatches, state-violation transitions, escrow underflow, fee-rate validation
scenarios.rs ~6 Multi-step integration: oracle staleness, governance proposal/timelock/execute, QA unstake delay, challenge response paths
properties.rs ~3 Conservation laws — escrow + locked sums constant under any operation; SVT counter monotonic; fee math reconciles to bps
invariants.rs ~2 Cross-operation invariants when challenges interleave with settlements
fairfoundry-svt 4 SVT contract: init with AlreadyInitialized guard, mint, view_svt, duplicate-SVT rejection

Counts above are rounded since the file split has migrated as features landed. The exact 61/0.15s comes from cargo test on the current main.

Why this organization

The split is deliberate. Each file has a single intent, and reviewers can answer "is this risk covered?" by reading one file rather than grep-ing across a flat 2000-line test module.

flows.rs

One test per critical happy path. These are the "if any of these break, nothing works" tests. They're the baseline that protects against accidental regressions when refactoring storage layout or auth patterns. They are NOT exhaustive; they are smoke tests for the cross-cutting wiring.

negative.rs

Auth + state-machine tests. Every public mutator gets at least one negative: wrong caller, wrong status, wrong amount, wrong role. Every panic_with_error! branch in the contract has at least one test that exercises it.

#[test]
#[should_panic(expected = "Error(Contract, #2)")]  // InvalidState
fn settle_service_rejects_unvalidated_order() {
    // ...setup with Delivered (not Validated) status...
    client.settle_service(&order_id, &false);
}

This is the most important file for a security review. If a guard isn't tested here, assume it's not enforced.

properties.rs

Conservation tests. Property-style assertions that hold across any sequence of operations. Two examples:

Escrow conservation: at any time, state.escrow_balance + sum(orders.escrow_locked) + sum(credits) + total_fees_collected equals the total deposited minus settlements paid out. If a settlement double-spends, this asserts trips. We've caught two bugs this way during development.

SVT monotonicity: state.next_svt_id is strictly increasing across the lifetime of the contract. Once issued, an SVT id never recycles even after order settlement. Catches accidental decrements from refactor.

scenarios.rs

Multi-step end-to-end flows that touch oracle staleness, governance proposals, and unstake timelocks. These are the slowest tests in the suite (relatively — they still run in milliseconds) but they exercise time-advancing operations that simpler tests skip. A scenario typically does 8–15 contract calls.

invariants.rs

Catches regressions where two operations are individually correct but their interleaving violates a global invariant. Specifically: what happens when a challenge is opened on a lot that's already mid-settlement on a different lot, and a separate QA stake change drops the locked-stake ratio below the floor.

What's NOT covered yet

Honest list. After the recent C02–C04 hardening, three coverage gaps surface:

Try-call negative tests for the new C02 auth guard. The setup uses env.mock_all_auths() which auto-satisfies require_auth, so the existing tests still pass even after we added order.factory.require_auth() for credit-mode settlement. A proper "non-factory caller passing as_credit=true panics with NotAuthorized" test needs try_settle_service without mock_all_auths. Backlog.

Late post-deadline reinspection responses. The C04 fix rejects QA responses after due_by. Test: advance ledger time past due_by, then call qa_reinspect_respond, assert TooEarlyOrLate. Same backlog.

Re-init. The C03 AlreadyInitialized guard is mirrored from the SVT contract, which has its test. The settlement contract doesn't yet. Trivial test, just needs writing.

Fee BPS rounding at small order values. When agreed_price * fee_rate_bps / 10_000 rounds to zero, a cheap order pays zero platform fee. Acceptable behavior, but worth an explicit test asserting the boundary — otherwise a refactor that changes the order of multiplication and division could silently break it.

Artifact-root mismatch. The factory submits an artifacts Merkle root via submit_artifacts; the oracle attests with a (possibly different) root via attest_completion. The contract doesn't currently compare them. Test should assert: when the roots differ, attestation should reject. This is also a feature gap, not just a test gap.

Why 0.15 seconds matters

The full suite runs in 0.15 seconds. cargo test ships green or red before cargo build would have finished a warm rebuild. The result: tests run after every save during contract work. Small surface area + fast suite = very tight feedback loops.

Slow test suites are quietly the biggest predictor of where bugs slip in. If a developer can't run the tests during refactor, they don't — they write the change and check at the end. Fast suites give you property-style invariants for free, because asserting after every state-mutation step costs nothing.

This is also why we wrote test helpers carefully. setup_full in services.rs is 60 lines, but it does an entire OEM/Factory/QA/Oracle init in one call. That's deliberate: the marginal cost of writing the next test is one helper call plus the assertions you actually care about.


Jisoo Lee is CEO of Fairbuild. Source: github.com/fairb-dev/Fairfoundry/tree/main/contracts/fairfoundry/src/test.

Want to read the contract?

Two contracts, ~3000 lines of Rust, 61 tests, CC BY-NC 4.0. Run cargo test against a clean checkout to verify.