Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Testing & Simulation

Seven test layers, all verified pre-release. As of v1.39.2 (2026-10-11): 1703 Rust tests green + 6 ignored (regtest + network + real-journal opt-in), 376 svelte-check files / 0 errors / 0 warnings.

1. Unit Tests

Per crate, pure logic. cargo test --workspace covers all 19 library crates + 7 binaries.

Key test counts (as of v1.39.2 — #[test] / #[tokio::test] attributes per crate; the authoritative total comes from cargo test --workspace):

CrateTestsWhat
warppool-api283axum 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), WarpTune snapshot join + badge, admin tune toggle, wallet push token flow, read-only API listener guard (GET-only, auth/admin refused, bootstrap unreachable), admin worker removal (idle only, 409 for a connected worker, audit line also for refused and failed attempts, exact name match, current_is_connected, leftovers without a worker entry)
warppool-stratum-v2162NOISE handshake, codec roundtrips, session frames, PoW check, stale-share detection, VarDiff parity (idle decay + best-share, incl. direct Standard-channel seeding) and per-device VarDiff isolation under one payout address, job-history (template-refresh tolerance), address-allowlist (Standard + Extended channel-open), per-connection extranonce prefixes (incl. a guard on the live listener constructors), extranonce sizing against the coinbase gap (exact fill on channel-open, refusal above it), cross-job probe for low-difficulty rejects
warppool-storage124sqlx 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 three-way energy split (solar/battery/grid), wallet_lifetime odds accumulator, share-outcome (personal-best + top-100 entry), worker deletion in one transaction (row, best shares, VarDiff memory V1 + Sv2)
warppool-stratum-v1162 (+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, the NerdMiner class’s ×2 brake on the way up (finite step limits, an operator’s lower max kept), suggest/d= honoring, idle decay, EMA rescale on retarget), ntime lower bound, worker-name cap, TLS roundtrip
warppool-tune383 (+1 real journal, #[ignore])dvb-WarpTune auto-clock sidecar: incremental journal windows for the savings refresh (equal to the full read), restores held under the power limit (board_limit_w, firmware maxPower, built-in board table) and built-in core-current lines per board, calibration (voltage floor search to the regulator’s clean range, clock search with the stability rule on three-poll means, power-supply reserve and shared-supply groups, confirmation window, latched chip domains with restore bump and journal memory, chain re-init at the hand point (confirmed off before on, /on sent even without a confirmed off, /on retries then reboot, recovery of an open chain_off after a restart, READY timeout, chain-less and NerdNOS boards, blind window armed from the real READY, deferred re-init retried after the settle), the yield rule on window means of chip errors (live gate 1 %), quick calibration (screen → candidate → confirm, candidate up to the reference, the 28-day re-check on Max miners only, no_counters without error readings), predicted core-current guard on every way up and on restores, re-check journaling), ladder + voltage curve (vendor table, device options for any ASIC, Avalon work modes), regulator decisions (temperature bands with the fan-target floor, fan-aware thermal budget, reject guard with reasons, power budget, step memory, Eco rule + eco_cap_mhz, point-change settle/judge, exploring rule for unknown steps, voltage trial with share vote over a window that reaches back to 200 shares, dead-chip guard with restart (a stopped or ramping chain is held, a FAILED one is not; a share-path stall with the chip counters running is restarted without marking), per-chip readings from /api/v2/dashboard (parser, error share, chip outlier), power collapse guard, chip death as a ladder ceiling, urgent step-down with share vote), device identity (MAC/DNA/fingerprint, swap detection), pool coupling (masked-wallet worker match, energy governor with surplus margin on entry, enter/exit debounce per miner and a provisional Eco fallback without the feed), journal (unstable steps, trial memory, known-good steps, identity epochs), snapshot store + HTTP endpoint, snapshot push client, runner (energy-mode prepare, pool feed fold-in, fan-target clamp), Avalon estats/summary parsing and the workmode,set write, yield table (points per share-derived TH/s)
warppool-telemetry80vendor probes (AxeOS / cgminer mock / Avalon estats), mDNS discovery, HTTP sweep, PoolMetrics + histogram
dvb-warppool-setup51wizard endpoints, install marker, Umbrel env defaults, goPool path resolver
warppool-config70TOML schema, solar surplus threshold (smart-meter vs sub-meter feed), coinbase_splits, VarDiffSettings/RetentionConfig/AutoProfileConfig::validate, retention/upnp blocks, comment-preserving save, profile-tier mapping
warppool-health46RPC call, bitcoin-conf parser, snippet generator, warnings aggregation, Bitcoin Core release comparison (tag parsing, numeric version decoding, same-line vs newer-major policy)
warppool-notifier51render_text, EmailSink/SlackSink construction, sink skip when env missing, MarkdownV2 escaping of miner/operator fields
warppool-autoupdate41 (+1 network, #[ignore])version compare, asset matching, download_verified, atomic_swap, cosign subprocess
warppool-share-validator26sha256d vectors, dedup, network/pool target checks
warppool-bitcoin-rpc39 (+3 regtest, #[ignore])RPC envelope parsing, ZMQ frames, submit_block null result, transient-error retry, multi-node failover + broadcast, GBT template source (ZMQ dedupe, ZMQ-liveness counter for the silent-ZMQ warning)
warppool-bitcoin-ipc13Cap’n Proto mining client for Bitcoin Core >= 31, TemplateSource trait, GBT↔IPC parity (identical StratumJob from both sources), socket precheck, timed fetch, order-preserving parallel template mapping
warppool-uninstall25per-OS path plans, marker gating, JSON roundtrip
warppool-job-builder28coinbase construction, BIP-34 height encoding, merkle branches, template mintime, coinbase invalidity when the extranonce overflows its gap
warppool-translator14 (+5 e2e, +3 bin)extended-channel state machine, BIP-320 version rolling, prev-hash reverse
warppool-hwdetect11sysinfo detection, container env, recommendation reason keys
warppool-profiles7profile resolution, retention monotonicity, rpm→bucket rate derivation
warppool-simulator4sim-miner personas, scenarios

(The binaries carry their own glue-path tests: daemon 52 — supervisor loops, notifier hydration, wallet-moment cooldown, auto-backup loop, the connected-worker refcount over V1, TLS and Sv2, plus one #[ignore] per-worker regtest consensus E2E — CLI 7 for argument plumbing, the WarpTune CLI 3, 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/ or in an in-crate tests.rs module (the api crate keeps its handler tests in src/tests.rs).

Typical cases:

TestCrateWhat
e2e.rsstratum-v1TCP listener + ShareValidator + sim-miner roundtrip with a real subscribe/authorize/submit flow
regtest_e2e.rsbitcoin-rpc#[ignore] — against a running bitcoind regtest
update_apply_with_cosign_verify_but_no_env_returns_500apiaxum mock GitHub + mock asset server
metrics_renders_pool_counters_when_pool_metrics_setapi/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

ScenarioDurationStatusWhat it verifies
solo-block-found30s✓ executablesim miner connects, block-found event fires
connection-storm-defense60s✓ executablerate limit kicks in under N parallel auth attempts
enterprise-load-smoke5min✓ executableN parallel miners of mixed personas run cleanly
small-24h-stability24hplan-onlySmall profile stays under 200 MB, no panics
rpc-outage-recovery10minplan-onlypool 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):

PersonaNominal hashrateTLSNote
nerdminer-v250 GH/snoplain only
bitaxe-ultra2 TH/syesclean reconnect
nerd-octaxe4.5 TH/syesNerdNOS quirks
avalon-q90 TH/snoCGMiner user agent
antminer-s23-pro580 TH/syesovert ASIC boost
adversary——malicious inputs

Failure modes

dvb-warppool-sim failures <mode> injects production problems:

  • rpc-down — Bitcoin Core unreachable
  • zmq-disconnect — block notify gone
  • tls-handshake-fail — expired cert
  • miner-hang — connection stalls after N shares
  • network-latency — artificial RTT
  • packet-loss — N% packet loss
  • invalid-template — broken coinbase tx
  • reorg — tip gets rolled back
  • memory-pressure — pool under RAM pressure
  • disk-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:

TestWhat
regtest_blockchain_info_returns_regtest_chainchain == "regtest"
regtest_getblocktemplate_workstemplate carries version + bits + prev hash
regtest_submit_invalid_block_is_rejectedtrivially 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:

CrateBenchHot path
warppool-share-validatorvalidateper accepted+rejected share
warppool-job-builderbuild_jobper new template
warppool-stratum-v1vardiffper 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 run check   # = svelte-kit sync && svelte-check && node scripts/i18n-guard.mjs
# → 376 files / 0 errors / 0 warnings; i18n-guard: locale parity, key existence, no hardcoded German

svelte-check is clean — 0 errors, 0 warnings — and the i18n guard (all 8 locales carry the same key set, every i18n.t() key exists, no hardcoded German in components) is part of the same CI gate.

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_panics drives StratumRequest::parse

    • sanitize_json_escapes through 20,000 deterministic mutated / pathological inputs (random bytes, byte-mutated valid messages, deep nesting, escape storms) and asserts none of them panic.
  • Stable smoke fuzz, second layer (v1.26.0): stratum-v1/src/fuzz_smoke.rs and stratum-v2/src/fuzz_smoke.rs — dependency-free xorshift generators push noise, mutated valid input and random frames through StratumRequest::parse + the hex helpers, every Sv2 decode_payload, decode_frame, and MiningServerSession::process_frame (before and after a valid SetupConnection). Several thousand cases each, no panics allowed.

  • 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 main cargo build/test --workspace never pulls in the nightly-only dependency. Crashes go into fuzz/artifacts/; minimise and add the input as a regression case in fuzz_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 --workspace
  • cargo 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 with packaging/macos/build-macos-release.sh, the .msi is paused
  • repro.yml — reproducible-builds verify (build twice + sha256 diff)
  • docs.yml — mdBook → docs.warppool.org
  • benches.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)