MIDNAT has three kinds of test: unit and integration suites in TypeScript,
Foundry suites in Solidity, and truth suites that read the source tree and hold
the shipped prose and the live chain to it. Counts below are stated as test
files, which are verifiable from the source tree without running anything. Where
a per-test count is quoted it is the number of it( or test( lines in a file,
which is a lower bound when a suite generates cases dynamically.
From the root, every package that defines a test script runs:
pnpm run testThe packages that define a test script are services/api (@midnat/api),
apps/terminal (@midnat/terminal), apps/web (@midnat/web) and
packages/protocol-client (@midnat/protocol-client). protocol-math, db,
api-zod, api-spec, desk-chips, openrouter-ai and api-client-react ship
no test script; protocol-math is exercised through the suites of the packages
that import it.
Per package:
pnpm --filter @midnat/api run test
pnpm --filter @midnat/terminal run test
pnpm --filter @midnat/web run test
pnpm --filter @midnat/protocol-client run testFoundry, from the root:
pnpm run contracts:test # forge test| Suite | Test files | Covers |
|---|---|---|
services/api |
54 | The price engine, oracle signing, anchor posting, funding and liquidation keepers, the trade indexer, the AI desk and guardrails, the intelligence usage ledger, the routes, and the security-header and error-shape contracts. |
apps/terminal |
41 | The trading interface: rendering of positions and the vault, the pretrade flow, host config, and the chain-parity fixtures the terminal relies on. |
apps/web |
5 | The public site: the reference-feed geometry, the API reads, host routing, and the two truth suites below. |
packages/protocol-client |
6 | The generated ABIs, the clients, the capacity math, the pretrade checks, the trade-detail decoding, and the on-chain live-fire (gated by an env flag). |
contracts/test (Foundry) |
19 (104 test functions across them) | The on-chain protocol: 4626 attacks, decimals, funding conservation, access-control and oracle-authority fuzzing, open-interest and settlement fuzzing, share invariants, solvency invariants, ledger conservation, liquidation, modes and capacity, oracle attacks, the vault redemption gate, the withdraw guard, and the full vertical loop. |
The Foundry profile in contracts/foundry.toml runs fuzz at 512 runs and
invariants at 192 runs with depth 64.
These read source or live state rather than mocks, and fail when the product and its claims drift apart.
| Suite | Path | What it holds |
|---|---|---|
| No-fake-data guard | services/api/src/lib/noFakeData.test.ts |
Scans every shipped economic surface (the engine, vault, portfolio, market and open-interest, fees, funding, liquidation and settlement code, the terminal's financial state, the site, the DB layer, the API spec, the deployed contracts and their scripts, the protocol math, and the chain client) for the patterns that fabricate a financial number. It never scans tests, mocks, fixtures, generated clients or build output, because invented numbers are the point of a fixture. Also exposed as pnpm run guard. |
| Site truth | apps/web/src/site-truth.test.ts |
Holds the public site's prose to the deployment so the site cannot claim something the live engine does not do. |
| Route truth | apps/web/src/route-truth.test.ts |
Holds the site's routes and the route-truth.mjs harness to the same fingerprint, so a rename on either side fails the unit suite rather than turning the live check into a silent pass. |
| Host routing | apps/web/src/host-routing.test.ts |
Pins how the host rewrites paths for the single-page app. |
pnpm run guard runs the no-fake-data gate on its own:
pnpm run guardSeveral tests only run when an environment flag is set, because they touch the
live chain. They are off by default (see docs/development/environment.md):
services/api/src/lib/liquidation.live.test.tsruns only whenMIDNAT_LIVE=1.packages/protocol-client/src/live-fire.chain.test.tsruns only whenMIDNAT_LIVE_FIRE=1and reads a testnet keyfile path.apps/web/src/lib/liquidation-chain-parity.test.tsuses a committed evidence fixture unlessMIDNAT_CHAIN_EVIDENCEoverrides it with a live run's output.
Two harnesses check MIDNAT against reality rather than against its own documentation. Both read live state and both fail loudly.
Reads the deployed clearing house, vault and collateral token directly with
cast and prints one JSON document: block, vault NAV, share supply, cash
balances, and every open position with its equity, maintenance margin and
liquidation price as the contract computes them. Comparing an integer the
contract returned against the number the product printed is the comparison that
settles an argument.
scripts/verify/chain-truth.sh # to stdout
scripts/verify/chain-truth.sh chain.json # to a file
RPC=https://your-node scripts/verify/chain-truth.shRequires Foundry for cast. No key: every call is a read.
Drives a real browser over the deployed site and reads the rendered identity of
every route in apps/web/src/lib/site-map.ts, plus a set of invented paths. The
site is a single-page app, so the host rewrites every path to index.html and an
HTTP sweep sees 200 OK for a real page and an invented one alike. This harness
asserts that each real route renders its own identity, that an invented path
renders the not-found page, and that the two differ.
CHROME_PATH=/path/to/chrome node scripts/verify/route-truth.mjs https://midnat.xyzRequires playwright-core and a Chromium binary. It writes evidence to
evidence/route-truth/ and exits non-zero on any failure.
The full chain reconciliation lives at contracts/scripts/verify-chain.mjs. It
compares the runtime code at each address against what this repo compiles (with
constructor immutables masked and then checked one by one), reads roles and
globals off the chain, re-fetches every manifest transaction, and replays the
event log in 100-block windows. Reproduce the deployment record's checks with:
node contracts/scripts/verify-chain.mjs