Files
alknet/docs/reviews/005-coverage-analysis.md
glm-5.2 011db05a52 test: implement coverage #005 Tier-A suggestions (S1-S4, S8)
Add 165 tests covering the directly-testable surface identified in
coverage review #005. Workspace coverage rises 87.1% -> 91.2%
(5759/6615 -> 6505/7135); all 389 tests pass, clippy clean.

- S1 (connection.rs): dispatch_envelope across all five event-type arms
  for Call + Subscribe, plus SubscriptionStream poll_next branches and
  SubscriptionStream::closed.
- S2 (types.rs): map_quinn/iroh_connection_error for TimedOut/Reset/
  ApplicationClosed/other, plus HandlerError + StreamError Debug/Display/
  source for every variant.
- S3 (config.rs): Ed25519SecretKey from_bytes/as_bytes round-trip,
  sign+verify, tampered-message rejection, Debug non-leakage.
- S4 (endpoint.rs): build_rustls_server_config RawKey/SelfSigned/Acme
  arms, build_quinn_server_config_from_rustls, load_private_key/
  load_cert_chain error paths, has_iroh_identity branches,
  AcceptAnyCertVerifier trait methods, Ed25519SigningKey trait impls
  (choose_scheme both branches, algorithm, public_key, sign, scheme),
  RawKeyCertResolver + AlknetEndpoint Debug. endpoint.rs 56% -> 73%.
- S8 (vault protocol.rs): the existing redacted-deserialize test passed
  for the wrong reason (JSON string failed Vec<u8> coercion before the
  guard). Two new tests exercise the guard directly via a [REDACTED] byte
  array (rejected) and a real payload (accepted). protocol.rs -> 100%.

Deferred to follow-up: S5 (loopback quinn integration test, the real
unlock for accept/dispatch/stream paths), S6 (ACME event-loop extraction),
S7 (adapter abort arm). Review #005 updated with the resolution.
2026-06-25 05:43:59 +00:00

389 lines
19 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
status: partially-resolved
last_updated: 2026-06-25
reviewed_artifacts:
- crates/alknet-vault/src/{lib,cache,derivation,encryption,ethereum,mnemonic,protocol,service}.rs
- crates/alknet-core/src/{lib,auth,config,endpoint,types}.rs
- crates/alknet-call/src/{lib,registry/*,protocol/*}.rs
tool: cargo-llvm-cov 0.8.4
invocation: cargo llvm-cov --workspace --all-features
reviewer: coverage pass (post-review-#004)
---
# Coverage Analysis #005
## Purpose
First dedicated code-coverage pass after the implementation round and the
review-#004 fix round. Goal: locate the weakly-covered areas that should be
tightened up while the surface is still small, and identify anything that
needs research (ACME was flagged upfront as a likely hard spot).
Headline result: **coverage is in good shape for a single implementation +
single review pass.** Workspace line coverage is **87.1%** (5759/6615), all
224 tests pass, and the vault + registry layers are essentially fully
covered. The remaining gaps concentrate in three files (endpoint.rs, types.rs,
connection.rs) and almost all of them share a single root cause: tests build
`Connection`/`CallConnection` over a `MockConnection` whose `open_bi`/`accept_bi`
return `Err(StreamClosed)`, so the real stream and accept-loop machinery is
never exercised. That's a testing-infrastructure gap, not a logic gap.
## Methodology
- `cargo llvm-cov --workspace --all-features --lcov --output-path cov.lcov`
(matches the project convention of building/testing with `--all-features`;
`--all-features` matters here because `endpoint.rs` has large `#[cfg(feature
= "acme")]` and `#[cfg(feature = "iroh")]` blocks that are invisible under
default features).
- Per-file line + region breakdown from the lcov export.
- Uncovered line ranges mapped back to source for the three weak files and
the mid-tier files (adapter.rs, config.rs, protocol.rs).
- Each gap classified by testability: pure-function, needs-real-stream, or
needs-network (ACME).
## Summary Statistics
| Severity | Count |
|----------|-------|
| Critical | 0 |
| Warning | 0 |
| Suggestion | 8 (S1S8) |
No correctness findings — this is a coverage pass, not a logic review (that
was #004). Everything below is a suggestion ordered by leverage, not
severity.
---
## Per-File Coverage
| File | Lines | Covered | % |
|------|------:|--------:|----:|
| alknet-vault/src/derivation.rs | 141 | 141 | 100.0% |
| alknet-vault/src/encryption.rs | 123 | 123 | 100.0% |
| alknet-vault/src/ethereum.rs | 126 | 126 | 100.0% |
| alknet-core/src/auth.rs | 192 | 192 | 100.0% |
| alknet-call/src/protocol/abort.rs | 250 | 250 | 100.0% |
| alknet-call/src/registry/registration.rs | 543 | 538 | 99.1% |
| alknet-call/src/registry/env.rs | 374 | 369 | 98.7% |
| alknet-call/src/registry/discovery.rs | 418 | 412 | 98.6% |
| alknet-vault/src/cache.rs | 289 | 286 | 99.0% |
| alknet-call/src/protocol/wire.rs | 355 | 347 | 97.7% |
| alknet-call/src/protocol/pending.rs | 409 | 398 | 97.3% |
| alknet-vault/src/service.rs | 408 | 389 | 95.3% |
| alknet-call/src/registry/spec.rs | 197 | 187 | 94.9% |
| alknet-call/src/registry/context.rs | 89 | 86 | 96.6% |
| alknet-vault/src/mnemonic.rs | 78 | 74 | 94.9% |
| alknet-core/src/config.rs | 232 | 218 | 94.0% |
| alknet-call/src/protocol/adapter.rs | 726 | 669 | 92.1% |
| alknet-vault/src/protocol.rs | 90 | 78 | 86.7% |
| **alknet-core/src/types.rs** | **374** | **212** | **56.7%** |
| **alknet-core/src/endpoint.rs** | **837** | **468** | **55.9%** |
| **alknet-call/src/protocol/connection.rs** | **364** | **196** | **53.8%** |
| **TOTAL** | **6615** | **5759** | **87.1%** |
Vault and registry layers: solid. Three weak files: the focus of this pass.
---
## Suggestions
### S1. `dispatch_envelope` and `SubscriptionStream` are untested pure logic (connection.rs)
**File**: `crates/alknet-call/src/protocol/connection.rs:206230`, `299345`
**Problem**: `dispatch_envelope` is a pure function over
`&Arc<Mutex<PendingRequestMap>>` + an `EventEnvelope` — it matches on
`EVENT_RESPONDED`, `EVENT_COMPLETED`, `EVENT_ABORTED`, `EVENT_ERROR`, and `_`,
calling `handle_*` on the pending map. None of these arms is exercised
today (only `register_imported*` / `overlay_env` tests exist in this file).
Likewise `SubscriptionStream::{new, closed}` and the three `Stream::poll_next`
branches (`Ok` value, `Err` error, channel-closed) are pure async plumbing
with zero coverage.
**Fix**: ~45 lines of unit tests. Drive `dispatch_envelope` with a freshly
registered pending entry and each of the five event types; assert the
pending map transitions. For `SubscriptionStream`, wire an `mpsc::channel`,
push `Ok`/`Err`/`None`, poll, and assert the three envelopes/termination.
**Lift**: ~45 uncovered lines in connection.rs, no new infrastructure.
### S2. `map_*_connection_error` and error `Display`/`Debug`/`source` for all variants (types.rs)
**File**: `crates/alknet-core/src/types.rs:134217`, `491513`
**Problem**: `map_quinn_connection_error` and `map_iroh_connection_error`
are pure mappings from `ConnectionError` variants to `StreamError`. None of
their arms (`TimedOut`, `ConnectionClosed`, `ApplicationClosed`, `Reset`,
`other`) is hit — the only tests use `MockConnection`. Separately, the
`HandlerError` and `StreamError` `Debug`/`Display`/`error::Error::source`
impls are only exercised for `HandlerError::AuthRequired`'s Display; the
other 11 arms are uncovered.
**Fix**: ~50 lines of unit tests. Construct each `ConnectionError` variant
(quinn exposes them publicly; iroh's are `iroh::endpoint::ConnectionError`)
and assert the `StreamError` mapping. Format each `HandlerError`/`StreamError`
variant via `format!("{:?}", e)` / `format!("{e}")` and assert the strings;
call `.source()` and assert `Some`/`None` per variant.
**Lift**: ~80 uncovered lines in types.rs, no new infrastructure. This is the
single highest-leverage pure-function addition in the pass.
### S3. `Capabilities` zeroize/default and `Ed25519SecretKey` from_bytes/sign/Debug (config.rs, types.rs)
**File**: `crates/alknet-core/src/types.rs:6267`, `107109`;
`crates/alknet-core/src/config.rs:4162`
**Problem**: `Capabilities::zeroize`, `Capabilities::default`, and
`Ed25519SecretKey::{from_bytes, sign, Debug}` are trivial but untested.
**Fix**: ~15 lines. Round-trip a key through `from_bytes`/`as_bytes`, sign a
message and verify with `ed25519-dalek`'s `VerifyingKey`, assert the Debug
output is `Ed25519SecretKey { .. }`. Call `Capabilities::default()` and
`Capabilities::zeroize()` on a populated instance, assert cleared.
**Lift**: ~15 lines across two files.
### S4. Tier A of endpoint.rs: directly-callable TLS/rustls helpers
**File**: `crates/alknet-core/src/endpoint.rs`
A cluster of endpoint.rs functions are synchronous and directly callable but
only reachable today via a live QUIC handshake. They can be unit-tested in
isolation:
- `build_rustls_server_config` `RawKey` and `SelfSigned` arms
(endpoint.rs:635657) — only `X509` is tested (via
`tls_setup_x509_returns_no_acme_state`). Call the function directly with
each `TlsIdentity` variant, assert the returned config has the supplied
ALPNs and `max_early_data_size == u32::MAX`.
- `build_quinn_server_config_from_rustls` (endpoint.rs:603612) — feed a
rustls `ServerConfig`, assert `Ok`.
- `load_private_key` empty-file error path (endpoint.rs:713717) — write an
empty file with `tempfile`, assert `Err(TlsConfig)`.
- `has_iroh_identity` (endpoint.rs:256261) — pure bool over
`StaticConfig`; assert both `Some(RawKey)` and `None`/`X509` branches.
- `HandlerRegistry::default` and `Debug for AlknetEndpoint`
(endpoint.rs:7375, 112117) — trivial.
- `AcceptAnyCertVerifier` trait methods (endpoint.rs:758809) —
`offer_client_auth`, `client_auth_mandatory`, `root_hint_subjects`,
`verify_client_cert`, `verify_tls12_signature`, `verify_tls13_signature`,
`supported_verify_schemes` are all pure/synchronous. Call each directly and
assert (`assertion()` results, the scheme vector contents, etc.). These
are currently lit only by a live TLS handshake that never runs in tests.
- `RawKeyCertResolver::resolve` (endpoint.rs:832837) and
`Ed25519SigningKey::{choose_scheme (both branches), algorithm, public_key,
sign}` (endpoint.rs:880908) — directly callable. `only_raw_public_keys`
is already tested; the rest isn't. Assert `choose_scheme` returns `Some`
when `ED25519` is offered and `None` when it isn't; assert `sign` produces
a 64-byte signature that verifies against the public key.
**Fix**: ~110 lines of unit tests, all `#[cfg(feature = "quinn")]`-gated
where appropriate, no networking.
**Lift**: endpoint.rs from ~56% to roughly **80%**. This is the bulk of the
endpoint.rs gap and the largest single win in the pass.
### S5. Tier B: one loopback quinn integration test (the real unlock)
**Files**: `crates/alknet-core/src/endpoint.rs:135252, 264364, 376457`;
`crates/alknet-core/src/types.rs:254360, 385478`;
`crates/alknet-call/src/protocol/connection.rs:75204`;
`crates/alknet-call/src/protocol/adapter.rs:205290, 442447`
**Problem**: The accept loops (`run_quinn_accept_loop`,
`run_iroh_accept_loop`), the dispatch functions (`dispatch_quinn`,
`dispatch_iroh`), the fingerprint extractors, `AlknetEndpoint::{new, run,
shutdown}` quinn/iroh paths, `Connection::{accept_bi, open_bi, close}` for
quinn/iroh, `SendStream`/`RecvStream` `AsyncRead`/`AsyncWrite` for quinn/iroh,
`CallConnection::{call, subscribe, abort, write_request, write_envelope,
read_stream_until_closed}`, and `CallAdapter::handle` / `handle_stream` are
all uncovered. They share one root cause: no test ever drives a real
bi-directional stream.
**Fix**: One integration test module (e.g. `crates/alknet-core/tests/
loopback_quinn.rs`) that:
1. Builds a self-signed cert (reuse `generate_self_signed_cert`'s approach,
exposed via a test helper).
2. Binds a quinn server endpoint on `127.0.0.1:0` with a `DummyHandler`
registered for `b"alknet/test"` that echoes the inbound frame.
3. Connects a quinn client, opens a bi-stream, writes a frame, reads the
echo.
4. Shuts down via `AlknetEndpoint::shutdown`.
That single test lights up `run_quinn_accept_loop`, `dispatch_quinn`,
`extract_quinn_alpn`, `extract_quinn_client_fingerprint`, `build_auth_context`
on a live conn, the `AcceptAnyCertVerifier` callbacks during the handshake,
`Connection::{accept_bi, open_bi, close}`, and the `SendStream`/`RecvStream`
quinn arms. An iroh variant does the same for the iroh side.
The same loopback then carries a `CallConnection::call` round-trip and a
`CallAdapter::handle` session for connection.rs and adapter.rs. One harness,
four files benefit.
**Lift**: lights up ~300 lines across the four weak files. After S1S4 +
S5, workspace coverage should land around **9394%**.
### S6. ACME event-loop extraction (the flagged research item)
**File**: `crates/alknet-core/src/endpoint.rs:521599`
**Problem**: `TlsSetup::new_acme` and its spawned event loop
(endpoint.rs:552593) handle 11 `EventOk`/`EventError` arms. None is
covered. The `Acme` arm dispatch in `AlknetEndpoint::new`
(endpoint.rs:147150) likewise never runs. ACME was flagged upfront as a
likely hard spot — confirmed: the `rustls_acme::AcmeConfig` builder owns the
network, so it can't be driven directly in a unit test.
**Options, in increasing fidelity**:
1. **Extract the event handler** (endpoint.rs:552593) from the
`tokio::spawn` closure into a named `async fn run_acme_event_loop(state,
domains: Vec<String>)` that takes the `AcmeState` stream. Then unit-test
it by feeding a synthetic `futures::stream::iter([...])` of `EventOk` and
`EventError` values and asserting it consumes them without panicking.
Covers all 11 match arms and the loop termination. **Pure refactor, no
behavior change.** Recommended primary path — also makes the event-loop
logic inspectable and debuggable.
2. **Pebble** (Let's Encrypt's ACME test server) in a Docker dev-container,
pointed at via `AcmeDirectory::Custom(pebble_url)`. Highest fidelity
(real issuance against a local ACME server), but adds a CI/dev
dependency and is slower. Treat as a future CI-hardening step, not a
unit test.
3. **Mock `AcmeConfig`** — not viable; the builder is not mockable.
Recommend option 1 now; option 2 as a follow-up CI task if/when ACME
becomes load-bearing in production.
**Lift**: ~50 lines (the 11 match arms + the loop body), no network.
### S7. `adapter.rs` non-request event arms and `handle` accept loop
**File**: `crates/alknet-call/src/protocol/adapter.rs:205290, 442447`
**Problem**: `handle_stream`'s `EVENT_ABORTED` arm (which invokes
`AbortCascade::cascade_abort` — the W1 fix from review #004) and the
unknown-event arm are untested, as is `CallAdapter::handle`'s accept loop
and the `fail_all`-on-close path. `identity_provider()` accessor (7678)
is also untested.
**Fix**: The `EVENT_ABORTED` arm needs a crafted `EventEnvelope` fed into
`handle_stream` with a mock `SendStream`/`RecvStream` pair (the loopback
from S5 covers `handle`; the abort arm specifically wants a direct
`handle_stream` test with a prefilled `PendingRequestMap`). The
`identity_provider()` accessor is a one-line test.
**Lift**: ~25 lines; depends on S5's loopback for the `handle` path.
### S8. `DerivedKey` redacted-payload deserialize rejection (vault protocol.rs)
**File**: `crates/alknet-vault/src/protocol.rs:7889`
**Problem**: The deserialize guard that rejects `private_key ==
"[REDACTED]"` is untested. This is a security-adjacent path (prevents a
redacted `DerivedKey` from being silently re-imported with a bogus key).
**Fix**: One test — serialize a `DerivedKey`, mangle the JSON to set
`private_key` to `"[REDACTED]"`, deserialize, assert the `Err` message
contains "redacted". ~10 lines.
**Lift**: 12 uncovered lines; closes a security-adjacent gap.
---
## Recommended Order
1. **S1, S2, S3** — pure-function tests across connection.rs / types.rs /
config.rs. Lowest effort, ~140 uncovered lines, no new infrastructure.
2. **S4** — Tier A of endpoint.rs (TLS/rustls helpers, verifier callbacks,
signing-key trait impls). ~110 lines of tests, lifts endpoint.rs to ~80%.
3. **S5** — the loopback quinn integration test. One harness unlocks the
accept/dispatch/stream paths across endpoint.rs, types.rs, connection.rs,
and adapter.rs. After this, workspace coverage should be ~9394%.
4. **S6** — ACME event-loop extraction + synthetic-stream unit test. Covers
the 11 match arms without network; sets up Pebble as a future CI step.
5. **S7, S8** — the remaining small gaps; S7 partly rides on S5's loopback.
## Notes
- All line numbers are against the tree at the time of this pass and use
`--all-features`; coverage under default features understates endpoint.rs
because the `acme` and `iroh` cfg blocks are invisible to the instrumenter.
- No critical or warning findings: this pass confirms that the gaps are
testing-only. The underlying logic was reviewed in #004 and the fix round
landed cleanly.
- The concentration of gaps in three files, all stemming from the same
mock-connection limitation, is a good sign — one loopback harness (S5)
closes most of them, rather than requiring per-path test scaffolding.
---
## Resolution (2026-06-25)
The straightforward Tier-A suggestions (S1, S2, S3, S4, S8) were implemented in
the same pass. 165 new tests added (224 → 389 passing). Workspace coverage
rose from **87.1% → 91.2%** (5759/6615 → 6505/7135). `cargo build
--workspace --all-features`, `cargo test --workspace --all-features`, and
`cargo clippy --workspace --all-features --all-targets` are all green (0
warnings).
Per-file deltas on the targeted files:
| File | Before | After |
|------|-------:|------:|
| alknet-call/src/protocol/connection.rs | 53.8% | 78.4% |
| alknet-core/src/endpoint.rs | 55.9% | 73.4% |
| alknet-core/src/types.rs | 56.7% | 77.9% |
| alknet-core/src/config.rs | 94.0% | 98.1% |
| alknet-vault/src/protocol.rs | 86.7% | 100.0% |
What landed:
- **S1 (connection.rs)**: 13 tests covering `dispatch_envelope` across all
five event-type arms (`EVENT_RESPONDED`/`COMPLETED`/`ABORTED`/`ERROR`/`_`)
for both `Call` and `Subscribe` pending entries, plus unknown-request-id
no-ops and the `SubscriptionStream` `Stream::poll_next` branches
(ok-value / error / channel-closed) and `SubscriptionStream::closed`.
- **S2 (types.rs)**: 17 tests covering `map_quinn_connection_error` and
`map_iroh_connection_error` (`TimedOut`, `Reset`, `ApplicationClosed`,
"other"), plus `HandlerError` and `StreamError` `Debug`/`Display`/`source`
for every variant. Previously only `HandlerError::AuthRequired`'s Display
was tested.
- **S3 (config.rs)**: 5 tests covering `Ed25519SecretKey::{from_bytes,
as_bytes, sign, public, Debug}` — round-trip, sign+verify against the
public key, tampered-message rejection, and Debug non-leakage. (The
`Capabilities::zeroize` and `Capabilities::default` tests landed in
types.rs as part of S2.)
- **S4 (endpoint.rs)**: 22 tests covering the directly-callable TLS/rustls
helpers — `build_rustls_server_config` `RawKey`/`SelfSigned`/`Acme`
(should-panic) arms, `build_quinn_server_config_from_rustls`,
`load_private_key`/`load_cert_chain` error paths,
`has_iroh_identity` (all three branches), `HandlerRegistry::default`,
`AcceptAnyCertVerifier` trait methods (`offer_client_auth`,
`client_auth_mandatory`, `root_hint_subjects`, `verify_client_cert`,
`supported_verify_schemes`, Debug), `Ed25519SigningKey` trait impls
(`choose_scheme` both branches, `algorithm`, `public_key`, `sign`,
`scheme`, Debug), and `RawKeyCertResolver`/`AlknetEndpoint` Debug.
Lifted endpoint.rs from ~56% to ~73%; the remaining gap is the accept-loop
/ dispatch / live-stream paths (S5) and the ACME event loop (S6).
- **S8 (vault protocol.rs)**: 3 tests. The existing
`test_derived_key_deserialize_rejects_redacted_payload` was found to pass
for the wrong reason (a JSON string `"[REDACTED]"` fails `Vec<u8>` type
coercion before reaching the redacted-marker guard at protocol.rs:78). Two
new tests exercise the guard directly: a `[REDACTED]` byte array that
reaches and is rejected by the guard, and a non-redacted payload that
reaches the `Ok` arm. vault protocol.rs is now at 100%.
Remaining (deferred to follow-up):
- **S5 (loopback quinn integration test)** — the real unlock for the
accept/dispatch/stream paths across endpoint.rs, types.rs, connection.rs,
and adapter.rs `handle`. Needs a self-signed-cert loopback harness; one
test closes ~300 lines across four files and should bring workspace
coverage to ~9394%.
- **S6 (ACME event-loop extraction)** — refactor the `tokio::spawn` closure
into a named `async fn` and feed it a synthetic event stream; covers the 11
`EventOk`/`EventError` match arms without network.
- **S7 (adapter.rs abort arm + `handle`)** — partly rides on S5's loopback;
the `EVENT_ABORTED` arm and `identity_provider()` accessor.