test: harden suite — remove dev_skip masking + publisher preflight - #94
Merged
Conversation
TaprootFreak
force-pushed
the
chore/test-quality-overhaul
branch
from
May 25, 2026 06:07
55bafcb to
99350d0
Compare
TaprootFreak
marked this pull request as ready for review
May 25, 2026 06:10
The api_remote suite reported "33 passed" while critical paths were
silently skipped via dev_skip!() on 5xx errors and 120-s retry loops
on scanner-lag 422s. The Coverage Gate and the deploy-dev API E2E
became a green stamp instead of a real signal — an empty publisher
wallet caused every mint to 503, every 5xx was masked as "ok", and CI
stayed green on a broken DEV.
Tier 1 — must-fix:
- Remove all dev_skip!() on is_server_error() (4 sites)
- Remove dev_skip!() on /health/ready, balance-not-observed, and
/api/username/claim 503
- Remove SEND_RETRY_DEADLINE retry loops on "Unable to get
merkle/mmr proofs" 422 — scanner is event-driven post-#87, the
stopgaps are obsolete. Replace with poll_until_balance before the
send op (15-s ceiling).
- Add fresh-state assertion to happy-path roundtrips
- feature_skip!() becomes a hard panic when CI=true env is set
- mint_handler_concurrent_mint_during_proof_returns_503 now
synchronizes via a #[cfg(test)] tokio::sync::Notify instead of a
200-ms sleep
- commit_with_valid_signature_fails_broadcast_returns_503 now
wiremocks Esplora and asserts exactly 503 (no more accept-either)
- fetch_capabilities .expect() instead of .unwrap_or(false) — a
missing capabilities field is a contract regression
- New /health/publisher endpoint exposes the publisher wallet's
UTXO count + total sats
- New deploy-dev preflight step probes /health/publisher before
the API E2E job runs — empty wallet -> job fails with a clear
"top up publisher" message
Tier 2 — same-PR quality:
- Value-bearing assertions replace .is_some()/.is_ok() shape checks
in api_remote, server_tests, state_tests
- Hash-byte-length + non-zero assertions on send response payloads
- Concrete bounds on LNURLp min/maxSendable
- tokio::time::sleep(60s) in test handlers replaced with
std::future::pending::<()>().await
- Ad-hoc tempdir cleanup replaced with tempfile::tempdir()
- Delete proof_id_one_returns_200_or_404 — accept-either status
was tautological
Tier 3 — documentation:
- Comment block on lock-poisoning tests' nextest isolation
requirement
The three TODO comments in account_server.rs (lines 147, 170, 416)
are tracked separately and not addressed here.
The strict `assert_minting_balance_is_bootstrap` helper would tripwire CI on every develop push after a manual reset: the deploy-dev workflow only runs `reset-zkcoins-server` on explicit workflow_dispatch with reset_state=true, not on the default push trigger. After this PR's first run, the minting balance drops to `bootstrap - 2*MINT_AMOUNT` and the strict equality fails forever. Replace with `assert_minting_balance_in_bounds`: upper-bound on BOOTSTRAP_MINTING_BALANCE (catches unauthorized re-seed bugs) plus a non-zero lower bound (catches unexpected wipe). Both happy-path tests now use the same helper. Also drops the redundant second `poll_until_balance` call in `send_commit_roundtrip_moves_balance` (the prior `poll_balance_at_least` already covered it) and documents the deliberately-deferred B5 proof_id pin in server_tests.rs (proof store ID grows across DB lifetime, same constraint as the minting balance bound).
Coverage Gate audit identified publisher_health_handler (router.rs)
as uncovered by unit tests — only api_remote E2E exercises it, and
api_remote is explicitly excluded from the coverage gate via
`-E 'not binary(api_remote)'`.
Add two unit tests in router_tests.rs mirroring the /health/ready
pattern:
- 200 Ok arm with wiremocked Esplora returning two UTXOs
- 503 Err arm via mint_test_state's unreachable Esplora URL
Refactor publisher_health_handler to derive the Taproot address from
PUBLISHER_KEY once at startup (lazy_static PUBLISHER_ADDRESS in lib.rs),
removing the SecretKey::from_str / Address::p2tr from the request path.
Side benefits:
- Handler is now pure I/O (Ok/Err on get_publisher_utxo only)
- One fewer panic-able branch per request
- Coverage Gate reaches 100% with the two new tests
Also:
- Fix stale "server::create_router" comment in runtime_tests.rs
(introduced by the test-quality commit, before PR #93's rename
sweep landed)
- Update BOOTSTRAP_MINTING_BALANCE doc-comment to describe the
bound semantic (not the strict equality that the second commit
of this branch relaxed)
- Defensive `command -v jq` install in deploy-dev.yaml preflight
TaprootFreak
force-pushed
the
chore/test-quality-overhaul
branch
from
May 25, 2026 07:01
9653784 to
687f412
Compare
2 tasks
TaprootFreak
added a commit
that referenced
this pull request
May 25, 2026
Four small follow-ups identified by the pre-CI audit, none blocking but all worth landing: 1. router.rs: switch `&*PUBLISHER_ADDRESS` deref to `.clone()` — eliminates a llvm-cov region-tracking edge case on the new handler's first line (98% safe either way; this is belt-and- braces). Address::clone is cheap. 2. router_tests.rs: wrap both await points of `mint_handler_concurrent_mint_during_proof_returns_503` in `tokio::time::timeout` (30 s + 60 s). Prevents a future regression in `mint_handler` phase 2 from hanging the 120-min CI job budget. 3. api_remote.rs: replace stale `reset-zkcoins-server` comment references with the post-rename `reset-zkcoins-node`. Cosmetic; matches the host-side dispatcher command name updated in DFXServer/server commit f74ec4a. 4. ci.yaml: the polling-pattern lint step (issue #84 guard) targets paths under `server/src/` that no longer exist after PR #93's rename to `node/src/`. The grep returned empty vacuously, which means the lint has been silently dead for 24 h. Update paths. Note: the audit also flagged the stale `server::create_router` comment in runtime_tests.rs, but that fix already landed in 687f412 on chore/test-quality-overhaul. Stacked on top of PR #94 (chore/test-quality-overhaul) per the "no force-push during running CI" project convention.
TaprootFreak
added a commit
that referenced
this pull request
May 25, 2026
Four small follow-ups identified by the pre-CI audit, none blocking but all worth landing: 1. router.rs: switch `&*PUBLISHER_ADDRESS` deref to `.clone()` — eliminates a llvm-cov region-tracking edge case on the new handler's first line (98% safe either way; this is belt-and- braces). Address::clone is cheap. 2. router_tests.rs: wrap both await points of `mint_handler_concurrent_mint_during_proof_returns_503` in `tokio::time::timeout` (30 s + 60 s). Prevents a future regression in `mint_handler` phase 2 from hanging the 120-min CI job budget. 3. api_remote.rs: replace stale `reset-zkcoins-server` comment references with the post-rename `reset-zkcoins-node`. Cosmetic; matches the host-side dispatcher command name updated in DFXServer/server commit f74ec4a. 4. ci.yaml: the polling-pattern lint step (issue #84 guard) targets paths under `server/src/` that no longer exist after PR #93's rename to `node/src/`. The grep returned empty vacuously, which means the lint has been silently dead for 24 h. Update paths. Note: the audit also flagged the stale `server::create_router` comment in runtime_tests.rs, but that fix already landed in 687f412 on chore/test-quality-overhaul. Stacked on top of PR #94 (chore/test-quality-overhaul) per the "no force-push during running CI" project convention.
TaprootFreak
added a commit
that referenced
this pull request
May 25, 2026
Four small follow-ups identified by the pre-CI audit, none blocking but all worth landing: 1. router.rs: switch `&*PUBLISHER_ADDRESS` deref to `.clone()` — eliminates a llvm-cov region-tracking edge case on the new handler's first line (98% safe either way; this is belt-and- braces). Address::clone is cheap. 2. router_tests.rs: wrap both await points of `mint_handler_concurrent_mint_during_proof_returns_503` in `tokio::time::timeout` (30 s + 60 s). Prevents a future regression in `mint_handler` phase 2 from hanging the 120-min CI job budget. 3. api_remote.rs: replace stale `reset-zkcoins-server` comment references with the post-rename `reset-zkcoins-node`. Cosmetic; matches the host-side dispatcher command name updated in DFXServer/server commit f74ec4a. 4. ci.yaml: the polling-pattern lint step (issue #84 guard) targets paths under `server/src/` that no longer exist after PR #93's rename to `node/src/`. The grep returned empty vacuously, which means the lint has been silently dead for 24 h. Update paths. Note: the audit also flagged the stale `server::create_router` comment in runtime_tests.rs, but that fix already landed in 687f412 on chore/test-quality-overhaul. Stacked on top of PR #94 (chore/test-quality-overhaul) per the "no force-push during running CI" project convention.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The api_remote suite was reporting "33 passed" while critical paths were silently skipped. Empty publisher wallet → every mint returned 503 →
dev_skip!()macro masked 5xx as "ok" → CI stayed green on a broken DEV. Coverage Gate and deploy-dev's API E2E became green stamps instead of real signals.User feedback: "das spricht aber nicht für unser testsetup! ich bin enttäuscht von dir!" — fully justified.
What this PR does
Removes silent-skip masking (BLOCKER fixes)
dev_skip!()blocks onis_server_error()inapi_remote.rs— mint, send_commit's mint/send/commit — gone. 5xx now hard-fails.dev_skip!()on/api/username/claim503 — gone.dev_skip!()on/health/readynon-200 — replaced withassert_eq!.dev_skip!()onbalance never observed within 60s— replaced withassert!.Removes scanner-lag retry stopgaps
SEND_RETRY_DEADLINEretry loops onUnable to get merkle proofs/Unable to get mmr inclusion proof422s (PR test(api_remote): retry second mint on scanner-lag 422 #83) are gone. Event-driven scanner from PR feat(scanner): event-driven chain ingestion (replace Esplora polling) #87 makes them obsolete.send_commit_roundtrip_moves_balancenow uses a singlepoll_balance_at_leastwait before send. If the scanner regresses, the wait fails — no silent masking.Operational preflight (NEW)
GET /health/publisherendpoint atserver.rs:1270— readsPUBLISHER_KEY, derives Taproot address, queries Esplora UTXOs, returns{ address, utxo_count, total_sats }. 503 on Esplora error (no fabricated empty response).deploy-dev.yamlpreflight step probes/health/ready+/health/publisherBEFORE the API E2E job. Job fails with clear "publisher wallet too low (utxos=N, sats=M) — top up" onutxos < 1 OR sats < 50000.Hardens determinism
mint_handler_concurrent_mint_during_proof_returns_503: replaced 200ms sleep withtokio::sync::Notifybarrier under#[cfg(test)]. Deterministic, no timing race.commit_with_valid_signature_fails_broadcast_returns_503: wiremocked Esplora (200 on /address/utxo, 400 on /tx). Asserts exactly 503. No more accept-either.Hardens contracts
fetch_capabilitiesuses.expect()for all 4 capability fields. Missing fields are now contract-regression errors, not silent feature-off.feature_skip!()panics whenCI=trueenv is set. Catches accidentally-dropped--all-featuresin workflows.Adds fresh-state sanity
assert_minting_balance_in_boundshelper:0 < balance <= BOOTSTRAP_MINTING_BALANCE. Catches impossible states (unauthorized re-seed, unexpected wipe) without tripwiring CI on every push-after-reset (the deploy-dev push trigger doesn't run reset_state).Value-bearing assertions
account_state_hash,output_coins_root. Proof_id > 0.min_sendable >= 1,max_sendable >= min_sendable.is_ok()beforeunwrap().is_some()shape checks replaced with.expect()+ value checks.Hygiene
tokio::time::sleep(60s)in test handlers replaced withstd::future::pending::<()>().await(no timing risk).tempfile::tempdir()instead of ad-hoc tempdir + manual cleanup.proof_id_one_returns_200_or_404— accept-either status was tautological.#[ignore]heavy-proof test and lock-poisoning tests' nextest isolation requirement.Tier-3 / deferred
TODO:comments inaccount_server.rs:147,170,416— tracked separately, out of scope.proof_id == 1pin — explicitly deferred (proof store ID grows across DB lifetime, same constraint as the minting balance bound).Behavioral impact
After this PR merges, the deploy-dev workflow's
API E2E against DEVjob will fail loudly when:Top-up procedure: send sats to the publisher address reported by
GET /health/publisher.Test plan