Tested like it holds money

XChain processes token balances, DEX orders, and cross-chain swaps. Testing is a first-class engineering requirement, not an afterthought. Here's how the platform is verified before every release.

Live today onBitcoin·Litecoin·Dogecoin·more chains coming

39,000+automated tests
12testing disciplines
13components covered
3build targets diffed for identical VM execution

Philosophy

Different bugs need different tests

Software that handles financial assets has no room for "it works on my machine." No single testing technique is sufficient on its own; together they form a defense-in-depth strategy that gives us confidence in every release.

Defense in depth

Unit tests catch logic errors; fuzz tests catch assumptions you didn't know you made; chaos tests catch failures you assumed couldn't happen. Each discipline catches a class the others can't.

Financial-grade correctness

A single uncaught edge case in the ledger could silently misattribute tokens; a race in reorg handling could corrupt a chain's state. Thorough testing before release is a necessity, not a nicety.

Deterministic & isolated

Tests are deterministic, isolated, and fast: no sleeps, no external network calls, no dependence on execution order. A green run is a trustworthy signal, not flaky noise.

The disciplines

Twelve kinds of test, twelve kinds of bug

Each discipline targets a category of failure the others would miss. Here's what each one is for.

Unit

Each function tested in isolation with dependencies mocked. Catches logic errors, wrong branches, off-by-one mistakes, bad return values, and missing null checks.

Integration

Real interfaces against a real MariaDB. Catches SQL errors, schema mismatches, broken JOINs, pagination bugs, and response-format regressions.

End-to-end

The full pipeline on a live regtest stack: encoder → decoder → indexer → explorer. Catches pipeline breaks and missing cross-service contracts.

Smoke

Fast "is it alive" checks that a service starts, connects, and answers. Catches broken imports, missing dependencies, and config errors in seconds.

Boundary

The exact edges of valid input: max/min values, length limits, precision thresholds. Catches off-by-one limits, integer overflow, and BigNumber precision loss.

Fuzz (property-based)

Thousands of random and adversarial inputs checked against invariants. Catches crashes on unexpected input, unicode handling, and math that breaks at extremes.

Security

Known attack classes thrown at the app (SQL injection, XSS, SSRF, path traversal, info leakage), verifying the defenses actually hold.

Performance & load

Baselines plus sustained concurrency. Catches slow queries, connection-pool exhaustion, memory leaks, and response-time regressions under load.

Chaos engineering

Injected failures: dropped database connections, latency, outages (via Toxiproxy). Verifies the system degrades gracefully and recovers cleanly.

Actions

Every protocol ACTION driven end to end on a live regtest stack: sends, issuances, dispensers, DEX orders, dividends, sweeps, attestations, staking, and contract execution. Each is checked against the ledger state it produces, and checked again after a reorg.

Determinism

Re-runs the same execution across machines, processes, and cache states and checks the result is byte-identical, the property consensus depends on. Catches non-determinism from ordering, floating point, time, or hidden state.

Regression

A curated cross-discipline subset in priority tiers, run on every change so fixed bugs stay fixed and critical paths keep working.

The coverage matrix below carries a thirteenth column, Mutation: a score that deliberately breaks the source and measures how much of it these twelve suites catch. It grades the tests rather than the code, so it is a report card on the twelve, not a thirteenth kind of test.

Coverage

Where the tests live

Every component that processes, stores, or serves token data ships its own full test suite. Click any type to jump straight to those tests in the repository.

The same data as a matrix: every component down the side, every test type across the top. Darker means more tests of that type; an empty cell means none. Columns are grouped into universal disciplines (expected across components, where an empty cell flags a possible gap) and specialized ones that are component-specific by design (consensus determinism, protocol actions, mutation-score tooling, where an empty cell is not a gap).

UniversalSpecialized · component-specific
UnitIntegrationE2ESmokeBoundaryFuzzSecurityPerformanceChaosRegressionMutationDeterminismActionsTotal
decoder1,233307242834274116121,650
indexer6,10726643231001235226441266,910
explorer3,18016049392261014615541484,027
encoder680791214210165716632601,425
utxo-tracker76269369231062929311381,232
sdk3,05410431411365411328283,643
hub4,0789070162639010542812255,060
vm7751676410115572115761831091,772
sync1,69310273171645515839392,340
wallet5,51511321021044273061646,175
node1,62287575057952716121582,190
regtest-miner267802612189691352822147975
e2e-test777264391814453138801643001,860
All29,7431,6111,1744991,5457871,0482447161,35712610930039,259

Counts reflect where tests live. Some components also tag tests @regression / @tier N as a CI grep selector instead of a dedicated directory, most notably indexer, where the tag spans nearly the entire unit suite. Tagged tests are counted under their home discipline (usually Unit), so a cross-cutting tag never becomes a separate column. Where a discipline is covered only that way, the matrix marks the cell (hover for what it covers) rather than leaving it blank, so tag-homed coverage isn't misread as a gap. By the same rule, security- and performance-shaped assertions interwoven in feature, fuzz or chaos files (e.g. non-owner rejection or pipe-injection checks inside e2e-test's action suites) are credited to their home discipline; a Security or Performance column appears only where those tests have a dedicated home.

Browse the components →

How it runs

Nothing merges unverified

Every component's whole suite runs on every change, not a fast subset of it. Two further gates run on their own schedule, because the failures they catch do not arrive with a commit.

Every push, every pull request

All 13 components run their full suite on both. No tiering, no subset: the same tests that gate a release gate a typo fix.

Weekly: shipped dependencies

A vulnerability audit of production dependencies only, at high and critical severity. A CVE can be disclosed against code we already shipped, with no commit here to trigger a run.

Weekly: identical execution

The contract VM's fuzz corpus is replayed on three build targets (x86-64, ARM64, and musl libc) and their per-case consensus hashes diffed. One disagreement means two validators would fork.

Weekly: browser-engine age

The desktop wallet ships a browser engine, so it inherits that engine's vulnerabilities between releases. A weekly check reads the pinned Electron version and asks the registry how far behind the current security patch it is. It reports on a clock rather than blocking releases, and a check that cannot reach the registry goes red rather than quiet.

Weekly: credential expiry

The keys and certificates that sign releases have expiry dates, and a date nobody reads is a date that passes. A weekly job reports how close each one is to expiring. It deliberately does not block releases: once a credential enters its renewal window this goes red every week until someone renews it, and a check that halts all releases for two months would be switched off long before the certificate died.

The full-stack test, which boots every service against a real coin node and moves real value on a private chain, is heavy enough to run on demand rather than on a clock. Inside the suites, regression cases also carry priority tags, so a developer can run the critical subset in seconds without waiting on the whole thing.

The toolbox

What it's built with

Standard, battle-tested tooling, shared across every component.

Mocha

The test runner across every component.

Sinon

Mocks, stubs, and spies for true isolation.

Chai

The assertion library (explorer, SDK).

Supertest

HTTP endpoint testing against real routes.

Nock

HTTP request mocking for the SDK and explorer.

fast-check

Property-based / fuzz testing (indexer, VM, SDK).

StrykerJS

The mutation-testing framework (explorer, encoder, VM).

Toxiproxy

Network fault injection for chaos tests.

Docker Compose

Test-environment orchestration for integration, chaos, and E2E.

Go deeper

The full reference covers every discipline, the per-component breakdowns, and how to run each suite yourself.

Full testing reference → ← Back home