Release: develop -> main - #142
Merged
Merged
Conversation
…141) * perf(probe): add probe_r2 binary for R2 wall-clock + peak-RSS measurement ROADMAP step 9 (still open) budgets the prover hot path on the Mac Studio M3 Ultra: warm prove <= 5s (target <= 1s), cold start <= 30s, peak RSS < 64 GB. There is no automated path that produces those three numbers today — the closest thing is the `#[ignore]`-d `prover_init_roundtrip` test in `script-plonky2/src/lib.rs`, which only proves an empty Init once and never reports RSS. PR #134 tuned the release profile (lto=thin, codegen-units=1) and swapped the global allocator to mimalloc on the `node` binary, so the profile-relative numbers need a re-measurement before step 9 can be ticked off. This patch adds a standalone `node` bin target: - `node/src/bin/probe_r2.rs` — builds `Prover::new()` cold, proves a single `prove_initial`, then runs N warm `prove_account_update` calls (default 5) against the same prev-proof + CommitmentMerkle witness. Reports wall-clock per phase as JSON, plus peak RSS via `getrusage(RUSAGE_SELF).ru_maxrss` (normalised to KB across macOS/Linux), and prints a PASS/FAIL verdict against the three ROADMAP budgets. - `node/Cargo.toml` — adds `libc = "0.2"` (already transitive via tokio/mio/rustls) for the `getrusage` FFI in the probe binary. Standalone tool: not wired into `main.rs`, no Postgres/Esplora/WS code reachable from the run. Intended for local execution on the M3 Ultra reference machine — not on the dfx01 m3-ultra CI runner where a 5+ min sweep would starve concurrent PR jobs. * perf(probe): use mimalloc in probe_r2 to match production allocator * feat(db): migration 0013 — R2 probe results schema Three tables + one view persist every measurement the probe_r2 binary collects: * r2_probe_hosts — natural-key normalised (hostname, os, arch, cpu_brand). Re-running on the same box returns the same row id. * r2_probe_runs — one row per probe execution, with every scalar measurement, the run-time context (git sha, rustc version, allocator, circuit params), and the R2 budgets the run was checked against. Persisting the budgets on the row keeps the summary view's pass/fail stable across future budget tweaks. * r2_probe_warm_calls — one row per warm call, FK ON DELETE CASCADE so retention pruning a run row cleans up its children. The r2_probe_runs_summary view joins host + run and inlines the three budget-pass booleans for trend queries. r2_warm_pass treats NULL (no warm samples) as a fail rather than silently passing. db_tests::connect_and_migrate_creates_all_tables extended for the three new tables; the view lives in information_schema.views and is intentionally not listed there. * feat(node): r2_probe persistence module Public surface: * detect() — best-effort HostInfo (sysctl on macOS, /proc/{cpuinfo,meminfo} on Linux; falls back gracefully). * upsert_host — INSERT ... ON CONFLICT DO UPDATE RETURNING id so the row id is returned on the conflict path too. * insert_run — INSERT ... RETURNING id for the run row. * insert_warm_calls — UNNEST-based batch INSERT; no-op (no SQL round-trip) on an empty slice. * fetch_recent_summary — SELECT ... ORDER BY ran_at DESC LIMIT ... against the convenience view. Uses sqlx::query + Row::get because the summary has 17 columns and FromRow tuple impls top out at 16. Tests are testcontainer-Postgres-17 backed; 12 cases cover the natural-key conflict path (same key → same id, payload-update on conflict, distinct keys, null total_ram_gb), the run-row success and failure shapes, the empty / non-empty warm-call batches, the FK CASCADE, summary DESC ordering with mixed budget verdicts, the limit clamp, and the null-warm-coerced-to-fail edge in the view. * feat(probe): wire probe_r2 to r2_probe persistence layer New CLI surface: * --persist write results to Postgres via r2_probe (requires DATABASE_URL). * --notes TEXT attach a free-form note to the persisted run row. * --tags A,B,C comma-separated tag list. * --warm-budget-ms, --cold-budget-ms, --mem-budget-kb per-run overrides for the R2 budgets that get stored alongside each row (so future budget tweaks do not silently flip the pass/fail of historical rows in the view). Run context auto-detected at runtime: GIT_SHA via 'git rev-parse HEAD' (env override honoured), RUSTC_VERSION via 'rustc --version' (env override honoured), binary version via CARGO_PKG_VERSION. Output now includes p50 / p90 / p99 over the warm-call wall samples and the verify_wall_ms. The console verdict goes off p50 instead of the previous max — a single outlier on a long sweep no longer flips the verdict. When --persist + the run succeeds the binary also prints an ASCII trend table read back from the r2_probe_runs_summary view (last 5 runs newest first) so the operator sees the regression / progression without leaving the terminal. * feat(router): GET /api/admin/r2-probe/history admin endpoint Read-only operator-facing trend view backed by the r2_probe_runs_summary view. Returns the most recent runs newest first as a JSON array of SummaryRow. * Default ?limit=50, hard cap 200. clamp_r2_probe_history_limit is exposed pub(crate) so the clamp shape can be unit-tested without spinning up Postgres. * Unauthenticated like every other route in the closed test environment; the /api/admin/ prefix keeps the endpoint visibly separate from the user-facing surface so it never gets quietly promoted to a public contract. * No write path exists for this resource through HTTP. Router tests cover the clamp helper (default, negative, zero, within window, above cap, exact cap), the DB-error 500 arm via the existing dead_pool, an empty-DB happy path, a multi-row happy path that seeds two runs (one within and one over the warm budget) and asserts DESC ordering plus the per-row pass flags, and the limit=10_000 → 200 clamp arrival at fetch_recent_summary. * docs(roadmap,contributing): point at probe_r2 persistence + admin view * fix(probe): compile-time platform gating in r2_probe to keep coverage gate green `detect_cpu_brand` and `detect_total_ram_gb` selected their platform body with runtime `cfg!(target_os = "...")` branches. On a single-platform runner (the macOS M3-Ultra coverage host) the inactive Linux body is statically dead, but `cargo llvm-cov` still counts those lines as uncovered — the 100% line + function gate in the Coverage Gate job would fail. Split each function into per-platform `_impl` helpers carrying `#[cfg(target_os = "...")]`. The wrapper now just calls `_impl()`. The inactive platform body is not compiled into the binary, so llvm-cov cannot mark it uncovered. Also adds a `not(any(macos, linux))` fallback impl returning `None` so non-Tier-1 targets still compile. `r2_probe.rs` is intentionally in coverage scope (not in the `--ignore-filename-regex` list in `.github/workflows/ci.yaml`), so the right fix is compile-time gating, not widening the ignore list. No behaviour change. Public surface (`detect`, `HostInfo`, ...) is identical. * fix(probe): cold-start verdict in view and trend table covers build + prove The cold-start budget (`BUDGET_COLD_START_MS = 30_000`, ROADMAP §Step 9) is defined against `circuit_build_wall_ms + prove_cold_wall_ms` — the time from a clean process to a verified first proof. The `probe_r2` binary already computes the console verdict that way (`cold_start_ms = circuit_build_wall_ms + prove_cold_wall_ms`), but two downstream surfaces silently compared only the prove leg: * `r2_probe_runs_summary` view: `r2_cold_pass` checked `prove_cold_wall_ms <= r2_cold_budget_ms`. A run with `build = 15s`, `prove_cold = 25s` (sum = 40s, over budget) was flagged PASS because 25s alone fits the 30s cap. * `probe_r2::print_history_table`: the `cold_ms` column showed `prove_cold_wall_ms` (prove-leg only) next to that buggy `r2_cold_pass` flag. Operator reads "cold_ms 25_000 PASS" and misses that the actual cold start is 40_000 and over budget. Fixes: * View: change `r2_cold_pass` to compare `(circuit_build_wall_ms + prove_cold_wall_ms) <= r2_cold_budget_ms`. Migration 0013 is not yet in `develop` / `main`, so the fix lands in-place rather than via a follow-up migration. Inline comment pinned to the ROADMAP constant so a future drift is visible in the schema. * Binary trend table: rename `cold_ms` → `coldstart_ms`, widen the column from 9 to 12 chars, and print `circuit_build_wall_ms + prove_cold_wall_ms`. The number now matches the pass marker next to it. `SummaryRow` already carries `circuit_build_wall_ms` and the view already exposes it, so no schema / struct churn beyond the view's `r2_cold_pass` formula. * New persistence test `fetch_recent_summary_cold_budget_covers_build_plus_prove` pins exactly the edge case the old formula missed: Run A (build 5s + prove 20s = 25s, PASS) plus Run B (build 15s + prove 25s = 40s, FAIL — even though the 25s prove leg alone would have passed). Existing `fetch_recent_summary_returns_desc_with_budget_pass` and `fetch_recent_summary_null_warm_marks_warm_fail` are unchanged and remain green under both formulas, since their `over` row's `prove_cold_wall_ms = 60_000` is itself already over the 30_000 budget. * test(db): include r2_probe_runs_summary view in schema assertion Migration 0013 adds the `r2_probe_runs_summary` VIEW. The `connect_and_migrate_creates_all_tables` introspection query reads `information_schema.tables` without a `table_type` filter, and Postgres lists views in BOTH `information_schema.views` AND `information_schema.tables` (the latter with `table_type = 'VIEW'`). The view therefore surfaces in the assertion and must be part of the expected list — added at the correct alphabetic position between `r2_probe_runs` and `r2_probe_warm_calls`, and the inline comment now spells out the actual Postgres listing semantics instead of the prior wrong claim that views only live in `information_schema.views`. * fix(probe): silence llvm-cov on non-deterministic platform-detect paths The 100% line + function coverage gate on `perf/r2-probe` (CI run for 7a21c79) flagged six locations in `node/src/r2_probe.rs` as uncovered: * L80 — the `else { None }` arm in the hostname-subprocess closure inside `detect()`. * L116 / L120 — the `if !out.status.success() { return None; }` and `if s.is_empty() { None }` arms in `detect_cpu_brand_impl` (macOS). * L164 — the analogous `return None;` in `detect_total_ram_gb_impl` (macOS). * The Linux `_impl` variants — listed by llvm-cov as missing functions even though they are `#[cfg(target_os = "linux")]`-gated and not compiled on the macOS M3-Ultra coverage host. This is a known llvm-cov quirk on `#[cfg]`-multiselect function definitions. All six are non-deterministic error paths: `sysctl` and `hostname` do not fail on a healthy host, so the gate cannot reach those lines from a CI test. Driving them would require a process-substitution test shim around `std::process::Command` that the rest of the probe code does not have. Two fixes: 1. `detect()` — rewrite the hostname closure as a single `.filter().and_then().map()` chain. The `else { None }` branch disappears from the LLVM IR, so llvm-cov no longer sees an uncovered arm. The success-path behaviour is identical. 2. `_impl` helpers — annotate all six variants of `detect_cpu_brand_impl` and `detect_total_ram_gb_impl` with `#[cfg_attr(coverage_nightly, coverage(off))]`. The thin wrappers (`detect_cpu_brand`, `detect_total_ram_gb`) and `detect_cpu_cores` stay in coverage and are exercised by `detect_returns_a_host_struct` in `r2_probe_tests.rs`. This is the same pattern already used 14× across the workspace (e.g. `program-plonky2/src/hash.rs:70`, `script-plonky2/src/lib.rs:270`). Bootstrap for the new attribute on the `node` crate: * `node/src/lib.rs` opts in to the unstable `coverage_attribute` feature behind the `coverage_nightly` cfg, matching `program-plonky2/src/lib.rs` and `script-plonky2/src/lib.rs`. * `node/Cargo.toml` registers `cfg(coverage_nightly)` via the same `unexpected_cfgs` `check-cfg` block the other two crates carry, so `-D warnings` does not trip on the new annotations. No behaviour change. Public surface (`detect`, `HostInfo`, ...) is identical. * ci(coverage): activate coverage_nightly cfg so coverage(off) is honored cargo-llvm-cov does not auto-set `cfg(coverage_nightly)`. The CI run on 643e8a5 surfaced the matching rustc warning under llvm-cov: warning: feature `coverage_attribute` is declared but not used --> script-plonky2/src/lib.rs:27:39 That means every `#[cfg_attr(coverage_nightly, coverage(off))]` in the workspace — the 14 pre-existing ones in program-plonky2 and script-plonky2, plus the new ones on the platform-detection helpers in node/src/r2_probe.rs — has been silently inert under `cargo llvm-cov`. For the node crate this only mattered now because this PR introduced the first such annotations in `node/src/`; the 100% line + function gate started counting the excluded fns as uncovered and the gate broke. Setting RUSTFLAGS only on the `coverage` job: - `lint-and-build` runs Rust 1.81 stable and would reject the nightly-only `feature(coverage_attribute)` if the cfg were active. - `node-tests` doesn't need the cfg — test execution is orthogonal to coverage measurement. - The cfg is registered in node/Cargo.toml via `check-cfg` already (PR 643e8a5), so no spurious "unexpected_cfgs" warnings. * refactor(probe): replace subprocess host-detect with sysinfo crate The earlier `r2_probe::detect()` shelled out to `hostname` / `sysctl` (macOS) and read `/proc/{cpuinfo,meminfo}` (linux). Subprocess- and filesystem-error arms are not deterministically reachable on a healthy CI host, so the 100% line/function coverage gate could only be satisfied by sprinkling `#[cfg_attr(coverage_nightly, coverage(off))]` markers on the per-platform `_impl` helpers (three cfg variants each for cpu brand + total ram). That worked but was a coverage-shaped hack, not a real solution. This rewrite drops the subprocess + `/proc` paths in favour of `sysinfo`, a cross-platform host-introspection crate. Each leg now has exactly one success path with conservative fallbacks (`"unknown"` for hostname / cpu brand, `None` for an unreadable total-ram reading), so every branch the coverage gate sees is reachable from a single `detect()` call. All `coverage(off)` markers in `r2_probe.rs` are removed; the `coverage_attribute` feature on the crate root and the `check-cfg` registration of `cfg(coverage_nightly)` stay in place as general repo infrastructure for other files that need them. `sysinfo` is added with `default-features = false, features = ["system"]` so the `disk` / `network` / `component` / `user` backends are not compiled in. Test coverage: * `detect_returns_a_host_struct` is tightened to assert all six fields: hostname / os / arch / cpu_brand non-empty, cpu_cores >= 1, and total_ram_gb either None or >= 1. Local-run sanity (macOS, M4): hostname populated, cpu_brand "Apple M4", cpu_cores 10, total_ram_gb Some(32) — matches host. Gates: cargo fmt --all --check ok cargo clippy -p node -p shared -- -D warnings ok cargo clippy -p node --all-features -- -D warnings ok cargo clippy -p zkcoins-program-plonky2 -p zkcoins-prover-plonky2 --lib -- -D warnings ok cargo check --workspace --all-features ok cargo build --release -p node --bin probe_r2 ok cargo test --release -p node detect_returns_a_host_struct ok * ci(coverage): emit deterministic missing-lines/funcs report on gate fail cargo llvm-cov nextest --show-missing-lines sometimes elides the per-file "Uncovered Lines:" block in its post-TOTAL output depending on the llvm-cov build (observed empirically across this repo's upgrades: the 643e8a5 run printed the block, 290cf5e did not). When that block is missing, a coverage gate fail forces the operator to reproduce locally (which needs Docker + a postgres testcontainer warm-up) just to see which file/line/function is below the gate. This step runs only on the gate step's failure (`if: failure()`), calls `cargo llvm-cov report` against the on-disk profraw/profdata the gate step already wrote (no re-run, no new test execution), and emits two deterministic views: - the text "--show-missing-lines" block per file - a json digest filtering files below 100% line OR function coverage The `|| true` keeps the failure-handler from hiding the original gate exit code, and the json branch falls back gracefully if jq is ever missing from the runner. * ci(coverage): pass --release to the failure-report llvm-cov call The nextest gate runs `cargo llvm-cov nextest --release ...`, which writes its profraw/profdata into target/llvm-cov-target/release/. `cargo llvm-cov report` without --release defaults to debug/, finds no object files there, and aborts before printing the per-file missing-lines block: error: failed to collect object files: not found object files (searched directories: …/target/llvm-cov-target/debug) Adding --release to both `report` invocations points them at the same target dir the gate just produced. No other change. * refactor(probe): split detect() into detect_impl() for deterministic fallback coverage The two `unwrap_or_else(|| "unknown".to_string())` closures (hostname and cpu_brand) on the prior `detect()` were unreachable on the CI m3-ultra runner: `sysinfo::System::host_name()` always returned `Some(_)` and `cpu_brand()` always returned a non-empty string. llvm-cov counted each closure body as a separate function, so the 100% function-coverage gate flagged r2_probe.rs as 17/15 functions and 162/160 lines. Extracts the host-introspection assembly into a private `detect_impl` that takes the four sysinfo / std readings as plain `Option` / scalar inputs. `detect()` itself stays the live entry point and just wires the real readings in; `detect_impl` is exercised by three new unit tests (`detect_impl_uses_fallbacks_when_inputs_are_none`, `detect_impl_uses_fallback_when_inputs_are_empty_strings`, `detect_impl_uses_inputs_when_provided`) that drive both the `None` and the `Some("")` fallback paths and the happy path with synthetic values. The existing `detect_returns_a_host_struct` test stays unchanged and keeps the black-box contract assertions against the live sysinfo path.
…t commit→reveal directly (#144) * perf(publisher): drop track-tx WS subscribe + REST fallback, broadcast commit→reveal directly The publisher's per-broadcast `track-tx` WS subscription against the upstream Esplora WS, plus its 30 s safety-net + single-shot `GET /tx/{commit}` REST fallback, was empirically dead code on our self-hosted deployment topology: - 16/16 REST fallbacks in the last 72 h DEV `request_log` succeeded (`/api/mint` p50 ≈ 40 s of which ~30 s pure WS-watchdog wait; `/api/send` p50 = 11 s + `/api/commit` p50 = 30.7 s same shape). - Direct WS probe against the self-hosted `mempool/backend:v3.3.1` (`mempool-api-mutinynet:8999/api/v1/ws`) with `{"action":"track-tx","data":"<txid>"}` returns 0 frames in 15 s — the backend version does not implement the action — while `{"action":"want","data":["blocks"]}` answers immediately. In the cluster, node + electrs + bitcoind share the Docker `bitcoin` network. `bitcoind::sendrawtransaction` returns only after the local-mempool accept, so by the time `client.broadcast(commit_tx)` resolves the commit UTXO is visible to the same `bitcoind`'s mempool — which is the same mempool the reveal POST hits. No cross-host propagation window to bridge. Changes: - `publisher.rs`: `broadcast_inscription_txs` and the `_with_persistence` variant now run `client.broadcast(commit_tx) -> client.broadcast(reveal_tx)` back to back, with no inter-tx wait. Updated docstring explains the topology argument. Removed `TRACK_TX_TIMEOUT_SECS` const and `EsploraConfig::track_tx_timeout` field (also dropped at every construction site). - `scanner_ws.rs`: removed `TrackTxStream`, `subscribe_track_tx`, `wait_for_tx_inner_resilient`, `reconnect_track_tx`, `TRACK_TX_FRAME_WATCHDOG`, `TRACK_TX_RECONNECT_BACKOFF_MIN/MAX`, and the `WsError::Timeout` variant. Pruned the `frame_signals_tx_seen` re-export. The block-tip `run_scanner_ws` connect/subscribe/drain loop and its ping-keepalive + liveness watchdog are untouched. - `scanner_ws_parse.rs`: removed `frame_signals_tx_seen` (only consumer was the now-removed track-tx path). - Tests: dropped the `subscribe_track_tx_*` + `track_tx_wait_*` + `frame_signals_tx_seen_*` tests; reworked `broadcast_advances_to_commit_broadcast_after_commit_success` to force the intermediate state by making the SECOND POST /tx return 400 (previous test relied on the WS-timeout path that no longer exists). Removed the in-process `spawn_track_tx_ws` / `mint_broadcast_mock_ws` helpers and every `ws_url: Some(...)` setter — the publisher no longer reads `ws_url` at all (it stays on the config for the block-tip scanner only). Issue #84 "events only" invariant is preserved: a sequential broadcast pair is neither a poll loop nor a timed sleep, so the CI `Forbid polling patterns` grep stays green — this commit only REMOVES sleeps from `publisher.rs`/`scanner_ws.rs`. Expected effect (DEV): `/api/mint` p50 ~40 s -> ~11 s, `/api/send + /api/commit` ~42 s -> ~13 s. * docs: explain direct commit→reveal broadcast + record self-hosted track-tx finding CONTRIBUTING.md "No polling — events only" now describes the publisher's REST-only path (sequencing comes from bitcoind's local-mempool accept inside the shared Docker `bitcoin` network, not from a WS subscription) and notes the historical PR #84 `track-tx` design that the empirical DEV `request_log` evidence invalidated for self-hosted `mempool/backend:v3.3.1`. The scanner-polling-ok exception list and the `Bitcoin Integration` subsection are updated to match. MIGRATION_RESEARCH.md §7.24 codifies the lesson: when porting an event-driven path designed against a public upstream onto a self-hosted reimplementation, smoke-test each WS action against the self-hosted endpoint before assuming parity. README.md coverage table drops the stale `frame_signals_tx_seen` reference (the helper was removed alongside the WS path). * test(publisher): order POST /tx mocks by explicit priority The stacked POST /tx mocks in `broadcast_advances_to_commit_broadcast_after_commit_success` assumed wiremock matched mounted mocks in LIFO order. wiremock 0.6 actually sorts by `priority` and falls back to insertion order (first-mounted wins on ties), so the 400 catch-all mounted first absorbed the commit POST and the test failed before the row reached `commit_broadcast`. Pin the up_to_n_times(1) 200 to priority 1 and the 400 fallback to priority 2 so the layering is explicit and independent of mount order.
…#143) * feat(features): gate username-claim write path behind a Cargo feature Username *resolve* + display is permanent MVP — the `usernames` capability stays hardcoded `true` and the read path (`GET /api/username/resolve/:name`, `db::resolve_username`, `UsernameStore::resolve`/`get_username`/`load_from_pg`) is unconditional so existing claimed names keep resolving. The *claim* write path is now opt-in via the `username-claim` Cargo feature, defaulting off so hosted DEV + PRD images don't offer claim as a UX policy. Self-hosters opt in with `--build-arg FEATURES=username-claim`. `/api/info.capabilities.username_claim` is always present so wallet clients can gate the claim UI on a single capability bit without sniffing build flags — this is the missing third bit alongside `usernames` (resolve) and `lnurl` (different feature, different endpoint). Gated symbols: - router: `claim_username_handler`, `ClaimUsernameRequest`, `/api/username/claim` route registration - db: `claim_username` - username: `UsernameStore::validate`, `precheck`, `commit_after_db`, `claim`; `ClaimUsernameError` enum + its `Display`/`Error`/`From` impls; `digest_to_bytes` import (only used inside `claim`) Test impact: claim-specific tests across `router_tests.rs`, `db_tests.rs`, `username_tests.rs` are now `#[cfg(feature = "username-claim")]`. CI runs `cargo nextest --all-features` so all tests stay covered; local `cargo test` on default features compiles cleanly and skips the write-path cases (the read path keeps its own coverage). `api_remote.rs` reads the new bool from `/api/info` with the same hard-fail contract as the existing capability fields; the `ZKCOINS_FORCE_DISABLE_FEATURES` knob accepts `username_claim` for parity with the other non-MVP flags. * refactor(capabilities): strip MVP-permanent fields from /api/info Capabilities now exposes only opt-in feature bits — `address_list`, `username_claim`, and `lnurl`. The previously hardcoded `faucet` and `usernames` fields are removed: mint (`/api/mint`) and username resolve (`/api/username/resolve/:u`) are permanent MVP endpoints and must not appear in the capability struct (always-true flags are noise, and the struct documents that policy explicitly). router_tests: - drop the `info.capabilities.faucet` / `.usernames` asserts - drop the corresponding entries from the stable-serialization key list - collapse two accidental duplicate `#[cfg(feature = "username-claim")]` attributes flagged in review api_remote: - drop `faucet` / `usernames` fields from the `Capabilities` literal in `fetch_capabilities` and the matching `body[...]` reads - drop the no-op `faucet` / `usernames` arms from the `ZKCOINS_FORCE_DISABLE_FEATURES` match; the `other =>` default catches them now with the standard "unknown flag" warning - drop `faucet` / `usernames` from the shape-only `/api/info` capability key probe - rewrite the `fetch_capabilities` header comment: only `/api/username/resolve/:u` is permanent; `/api/username/claim` is feature-gated like `address-list` / `lnurl` - guard the six tests that POST `/api/username/claim` with `feature_skip!("username_claim", ...)`: - `claim_username_pk_mismatch_returns_401` - `claim_username_bad_signature_returns_401` - `claim_username_stale_timestamp_returns_401` - `balance_response_carries_username_after_claim` - `claim_response_carries_address` - `username_claim_resolve_lnurlp_roundtrip` (now gated on both `username_claim` and `lnurl`) * test(username): cover load_from_pg row-insert without `claim` feature The coverage gate runs on default features, where every test that uses `UsernameStore::claim` is cfg-gated out. That left `load_from_pg`'s `usernames.insert(...)` line uncovered, because the only callers of `claim` were also the only paths that planted rows into the table during the test run. Plant a row via direct SQL, then `load_from_pg` + `resolve` / `get_username` to exercise the row-conversion + insert path without going through the claim write surface. Read path is permanent MVP and must stay covered independently of the `username-claim` Cargo feature.
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.
Automatic Release PR
Commits: 1 new commit(s)