Testing & Simulation
Four test layers, all verified pre-release. As of v1.24.0 (2026-07-24): 1201 Rust tests green + 4 ignored (regtest opt-in), 374 svelte-check files / 0 errors / 0 warnings.
1. Unit Tests
Per crate, pure logic. cargo test --workspace covers all 18 library
crates + 6 binaries.
Key test counts (as of v1.24.0 — #[test] / #[tokio::test] attributes per
crate; the authoritative total comes from cargo test --workspace):
| Crate | Tests | What |
|---|---|---|
warppool-api | 252 | axum handler tests against in-memory SQLite + mock GitHub/SMTP servers, incl. pool_metrics + notifier, API rate-limit middleware, GBT/IPC template-source admin switch, per-wallet odds panel (Poisson kernel + endpoint) |
warppool-stratum-v2 | 138 | NOISE handshake, codec roundtrips, session frames, PoW check, stale-share detection, VarDiff parity (idle decay + best-share, incl. direct Standard-channel seeding), job-history (template-refresh tolerance), address-allowlist (Standard + Extended channel-open) |
warppool-storage | 117 | sqlx roundtrips, migrations, all tables, vardiff, audit_log, api_tokens, admin_2fa, pool_settings, goPool import, pool-size + peek_setting, single-transaction record_share + worker eviction, correlated solar/grid energy split, wallet_lifetime odds accumulator, share-outcome (personal-best + top-100 entry) |
warppool-stratum-v1 | 152 (+8 e2e) | session state machine, rate limit (incl. live auth-rate switch, CIDR IP-gate, per-IP connection cap, reject-ratio + ban-when-disabled enforcement), address-allowlist on authorize, VarDiff (per-class incl. heavy-iron max_diff, suggest/d= honoring, idle decay, EMA rescale on retarget), ntime lower bound, worker-name cap, TLS roundtrip |
warppool-telemetry | 75 | vendor probes (AxeOS / cgminer mock / Avalon estats), mDNS discovery, HTTP sweep, PoolMetrics + histogram |
dvb-warppool-setup | 51 | wizard endpoints, install marker, Umbrel env defaults, goPool path resolver |
warppool-config | 56 | TOML schema, coinbase_splits, VarDiffSettings/RetentionConfig/AutoProfileConfig::validate, retention/upnp blocks, comment-preserving save, profile-tier mapping |
warppool-health | 35 | RPC call, bitcoin-conf parser, snippet generator, warnings aggregation |
warppool-notifier | 51 | render_text, EmailSink/SlackSink construction, sink skip when env missing, MarkdownV2 escaping of miner/operator fields |
warppool-autoupdate | 41 | version compare, asset matching, download_verified, atomic_swap, cosign subprocess |
warppool-share-validator | 26 | sha256d vectors, dedup, network/pool target checks |
warppool-bitcoin-rpc | 37 (+3 regtest, #[ignore]) | RPC envelope parsing, ZMQ frames, submit_block null result, transient-error retry, multi-node failover + broadcast |
warppool-bitcoin-ipc | 12 | Cap’n Proto mining client for Bitcoin Core >= 31, TemplateSource trait, GBT/IPC selection + socket precheck |
warppool-uninstall | 22 | per-OS path plans, marker gating, JSON roundtrip |
warppool-job-builder | 26 | coinbase construction, BIP-34 height encoding, merkle branches, template mintime |
warppool-translator | 13 (+5 e2e, +3 bin) | extended-channel state machine, BIP-320 version rolling, prev-hash reverse |
warppool-hwdetect | 11 | sysinfo detection, container env, recommendation reason keys |
warppool-profiles | 7 | profile resolution, retention monotonicity, rpm→bucket rate derivation |
warppool-simulator | 4 | sim-miner personas, scenarios |
(The binaries carry their own glue-path tests: daemon 44 — supervisor loops,
notifier hydration, wallet-moment cooldown, auto-backup loop, plus one
#[ignore] per-worker regtest consensus E2E — CLI 7 for argument plumbing, and
the macOS launcher 5.)
# Everything
cargo test --workspace
# A single crate
cargo test -p warppool-share-validator
# With output
cargo test -p warppool-stratum-v2 -- --nocapture
2. Integration Tests
Multiple crates in-process, axum mock servers for external services. Per
crate under tests/.
Typical cases:
| Test | Crate | What |
|---|---|---|
e2e.rs | stratum-v1 | TCP listener + ShareValidator + sim-miner roundtrip with a real subscribe/authorize/submit flow |
regtest_e2e.rs | bitcoin-rpc | #[ignore] — against a running bitcoind regtest |
update_apply_with_cosign_verify_but_no_env_returns_500 | api | axum mock GitHub + mock asset server |
metrics_renders_pool_counters_when_pool_metrics_set | api | /metrics output against the Prometheus format |
3. Simulation (dvb-warppool-sim)
Runs against the real daemon process (or spawned in-process). Scenarios are end-to-end stories.
Built-in scenarios
| Scenario | Duration | Status | What it verifies |
|---|---|---|---|
solo-block-found | 30s | ✓ executable | sim miner connects, block-found event fires |
connection-storm-defense | 60s | ✓ executable | rate limit kicks in under N parallel auth attempts |
enterprise-load-smoke | 5min | ✓ executable | N parallel miners of mixed personas run cleanly |
small-24h-stability | 24h | plan-only | Small profile stays under 200 MB, no panics |
rpc-outage-recovery | 10min | plan-only | pool survives a 5-minute RPC outage |
Invocation
solo-block-found runs self-contained — it spawns a Stratum V1 server
in-process with network_target=[0xff;32] (every valid share becomes a
BlockFound) and a JsonStratumMiner (persona BitaxeUltra). No Bitcoin
Core required:
dvb-warppool-sim scenario solo-block-found --duration 10
# → Result: BLOCK FOUND after 55 ms (worker=bc1qsim-bitaxe, job_id=sim-job-1)
dvb-warppool-sim scenario connection-storm-defense --duration 15 --attackers 200
# → Success: 10, RateLimit: 190, OtherErr: 0
# → OK: rate limit fired 190x (burst allowance was 10)
dvb-warppool-sim scenario enterprise-load-smoke --duration 15 --miners 200
# → Accepted: 57400 (~3826 shares/s), Rejected: 0, Disconnects: 200
Personas
Sim miners mimic real hardware (hashrate, user agent, TLS support, reconnect pattern):
| Persona | Nominal hashrate | TLS | Note |
|---|---|---|---|
nerdminer-v2 | 50 GH/s | no | plain only |
bitaxe-ultra | 2 TH/s | yes | clean reconnect |
nerd-octaxe | 4.5 TH/s | yes | NerdNOS quirks |
avalon-q | 90 TH/s | no | CGMiner user agent |
antminer-s23-pro | 580 TH/s | yes | overt ASIC boost |
adversary | — | — | malicious inputs |
Failure modes
dvb-warppool-sim failures <mode> injects production problems:
rpc-down— Bitcoin Core unreachablezmq-disconnect— block notify gonetls-handshake-fail— expired certminer-hang— connection stalls after N sharesnetwork-latency— artificial RTTpacket-loss— N% packet lossinvalid-template— broken coinbase txreorg— tip gets rolled backmemory-pressure— pool under RAM pressuredisk-full— SQLite write failure
Security suite
dvb-warppool-sim security drives adversarial inputs (line flood,
connection storm, slowloris, auth flood, weak ciphers, …).
Correct outcome: every connection rejected cleanly, no panic, no OOM.
4. Regtest E2E (Phase 15b — opt-in)
For full E2E against a real bitcoind regtest:
eval "$(scripts/regtest-up.sh)" # starts bitcoind regtest + sets env vars
cargo test -p warppool-bitcoin-rpc --test regtest_e2e -- --ignored --nocapture
scripts/regtest-down.sh --purge # cleanup
Four #[ignore] tests in total — three in warppool-bitcoin-rpc:
| Test | What |
|---|---|
regtest_blockchain_info_returns_regtest_chain | chain == "regtest" |
regtest_getblocktemplate_works | template carries version + bits + prev hash |
regtest_submit_invalid_block_is_rejected | trivially invalid hex → Rejected (not Accepted, not Error) |
…plus a full consensus E2E in the daemon crate
(per_worker_regtest_full_consensus_e2e, apps/dvb-warppool-daemon/src/regtest_e2e_tests.rs):
it mines a real per-worker-coinbase block against regtest and asserts Bitcoin
Core accepts it, the reward is attributed to the right worker, the witness
commitment is correct and no cross-payment happens.
cargo test -p dvb-warppool-daemon --lib -- --ignored --nocapture per_worker
CI does not run them (no bitcoind in the runner image). Locally they are very useful before release tags. See Configuration Reference → WARPPOOL_E2E_REGTEST_*.
5. Performance Benchmarks (Phase 19 — opt-in)
Criterion suites for the hot paths:
| Crate | Bench | Hot path |
|---|---|---|
warppool-share-validator | validate | per accepted+rejected share |
warppool-job-builder | build_job | per new template |
warppool-stratum-v1 | vardiff | per accepted share |
cargo bench --workspace
# or
cargo bench -p warppool-share-validator --bench validate
See Performance Benchmarks for baseline numbers and interpretation.
6. UI side (svelte-check)
cd ui
pnpm install
pnpm svelte-check
# → 374 files / 0 errors / 0 warnings
svelte-check is clean — 0 errors, 0 warnings.
7. Fuzzing (opt-in)
The inbound Stratum parser reads bytes straight off the wire, so it is the prime hardening target. Two layers:
-
Stable smoke fuzz (runs in the normal suite, no nightly):
messages.rs::tests::fuzz_parse_never_panicsdrivesStratumRequest::parsesanitize_json_escapesthrough 20,000 deterministic mutated / pathological inputs (random bytes, byte-mutated valid messages, deep nesting, escape storms) and asserts none of them panic.
-
Coverage-guided fuzz (
crates/stratum-v1/fuzz/, needs nightly +cargo install cargo-fuzz):cargo +nightly fuzz run parse_stratum # from crates/stratum-v1/The
fuzz/crate has its own workspace root, so the maincargo build/test --workspacenever pulls in the nightly-only dependency. Crashes go intofuzz/artifacts/; minimise and add the input as a regression case infuzz_parse_never_panics.
CI
CI runs on a self-hosted Forgejo runner (git.warppool.org); the workflow
files still live under .github/workflows/ but are executed by Forgejo
Actions. There are no macOS and no Windows runners — everything below is
Linux-only. See CI porting notes.
.github/workflows/ci.yml matrix:
cargo fmt --check(no diff)cargo clippy --workspace --all-targets -- -D warnings(a few style lints allowed:field_reassign_with_default,get_first,too_many_arguments)cargo test --workspacecargo deny check(licenses + advisories — RustSec feed via deny)- UI job:
pnpm install+pnpm svelte-check+pnpm build
Separate workflows (see Architecture):
release.yml— tag-triggered: multi-arch Docker + native Linux binaries + SBOM. No macOS/Windows jobs — the.dmg/tarball assets are built manually per release withpackaging/macos/build-macos-release.sh, the.msiis pausedrepro.yml— reproducible-builds verify (build twice + sha256 diff)docs.yml— mdBook → docs.warppool.orgbenches.yml— Criterion, manual dispatch only (not per-PR, not on tag push: the fat-LTO bench profile OOM-compiles on the CI box and benches must never gate a release)