Skip to main content

Testing

Certichain is tested in layers. Each layer proves something the others cannot: the contract suites prove the rules the chain enforces, the unit suites prove the browser and server logic in isolation, the interop suites prove that independent software reads the formats, the end-to-end journeys prove that real pages, a real local chain and a real build work together, and the manual checks cover what only a person with a real wallet and real services can confirm.

PageWhat it covers
Contract testsHardhat suites for the v0.3 registries, the timelock, the access gate and v0.2
Invariants and gasRandom action sequences checked against a model, the gas snapshot and contract sizes
Zero-knowledge testsPinned proof system, real-proof tests, key and circuit file checks
Unit and rendered testsVitest suites for src/, packages/sdk, scripts and the E2E helpers
End-to-end journeysThe Playwright harness and every journey
Interop and SDKThe independent verifier library tests and the SDK's own tests
Continuous integrationEvery job in the GitHub workflow, and the current billing hold
Manual checksStep-by-step scripts per role for a local chain and for Sepolia

The layers​

Lower layers are many, fast and narrow; higher layers are few, slow and wide. A behaviour is tested at the lowest layer that can prove it, and a journey exists for each flow a person depends on.

LayerRunnerProvesDoes not prove
ContractsHardhat (Mocha, Chai, ethers v6)Access checks, state transitions, events, reverts, pause coverage, storage bounds, upgrade and governance rulesThat the app calls them correctly
Invariants and gasHardhat with fast-checkProperties hold across 500 random sequences; gas stays within 10% of the snapshotEvery possible sequence (the search is random, not coverage guided)
ZKHardhat and scriptsReal proofs pass and tampered ones fail against the pinned verifier; pinned files and keys match their recorded hashesThat the original trusted setup was honest
Unit and renderedVitestServices, hashing, encryption, formats, parsers, server routes, page components with fake servicesBehaviour against a real chain or browser wallet
Interopnode:test with Digital Bazaar librariesA library the project did not write verifies Certichain VCs, Open Badges, status lists and API answersAnything about the chain
SDKVitestThe client refuses answers that fail signature, nonce, window or deployment checksNetwork behaviour of a real deployment
E2EPlaywrightWhole flows across pages, wallet prompts, the server routes (upload mocked), a real local chainPinata, MetaMask, Sepolia
ManualPeopleReal wallet extensions, real storage service, real public networkRepeatability

The test-first rule​

Every fix and feature is written test first: the test is run and fails for the expected reason, then the smallest change makes it pass, then the code is tidied with the tests green. A security fix is not done without a regression test that fails before the fix and passes after it. The engineering handbook (docs/conventions/testing.md, docs/AGENTS.md) states the rule; reviews also seed deliberate bugs into the code to confirm the tests catch them, and record the result in the session logs.

Other rules the suites follow: fix the implementation, not the test, unless the test is wrong; no .only and no skipped tests (Playwright sets forbidOnly in CI); and no test files under docs/, because the root Vitest run would pick them up.

Coverage gates​

ScopeGateEnforced by
Contracts (blockchain/contracts)95% lines, 90% branchesnpm run coverage in blockchain/, which runs hardhat coverage and then scripts/check-coverage.ts
src/services/** and src/lib/**80% lines, statements, functions and branchesthresholds in vitest.config.ts, checked by npm run test:coverage

The Vitest coverage excludes test files, __fixtures__ folders and the test fakes in src/services/testing/. Pages and components are tested but are not part of the coverage gate.

Where the suites live​

FolderSuiteSize (counted 2026-10-24)
blockchain/test/Contract, invariant, gas and ZK tests69 *.test.ts files
src/, packages/sdk/test/, scripts/, e2e/support/Vitest334 files, 3,369 tests (the last full run recorded in the session log of 2026-10-24)
interop/test/Interop (node:test)6 files, 44 tests
e2e/journeys/Playwright22 files, 38 journeys

Counts were taken with find and grep over the repository; re-count with the commands in Unit and rendered tests and End-to-end journeys before quoting them.

Run everything​

From the repository root, after npm ci and npm --prefix blockchain ci:

npm run lint
npm run typecheck
npm run zk:public # puts the hash-checked circuit files the browser prover needs in public/zk
npm run test:coverage # Vitest with the 80% gate
npm run build

cd blockchain
npm run compile
npm run zk:artifacts # fetches the pinned circuit files, hash-checked
npm run check:zk-keys
npm run sizes
npm run gas:check # fails above 10% of gas-snapshot.json
npm run invariants # 500 random sequences
npm run coverage # 95% lines, 90% branches
cd ..

npm --prefix interop ci
npm run interop:generate
git diff --exit-code -- interop/fixtures
npm run interop

npm --prefix packages/sdk ci
npm run sdk:build
npm run sdk:test

npx playwright install chromium
npm run test:e2e # needs ports 8545 and 3100 free

The same sequence is what the CI jobs run; see Continuous integration. As of 2026-10-24 those jobs are not starting on GitHub because of a billing hold on the account, so the checks are run locally.

Source files​

  • vitest.config.ts, playwright.config.ts, e2e/global-setup.ts
  • blockchain/hardhat.config.ts, blockchain/scripts/check-coverage.ts
  • interop/package.json, packages/sdk/package.json
  • .github/workflows/ci.yml
  • package.json and blockchain/package.json (scripts)
  • docs/conventions/testing.md (handbook conventions)