tests/server_seams.rs: - TlsServerConfig::new per identity variant → rustls_config() + for_tcp_tls (tcp) + for_noq (noq); ALPN + max_early_data_size=u32::MAX asserted per path; Acme-no-feature → AcmeConfig cell tests/client_seams.rs: - TlsClientConfig::new per credentials cell → into_rustls_config + for_noq; enable_early_data=true + single-value ALPN pinned tests/invariant_pins.rs: - nine-scheme exact-list pin (vec equality, order included) - verifier-selection + client-auth presentation matrices via public API (Debug probe for the pub(super) verifier, direct resolver introspection); root-store fallback non-empty tests/acme_lifecycle.rs (#![cfg(feature = acme)]): - spawn-and-return, acme-tls/1 in ALPN, resolver wiring, 0-RTT on the ACME branch; blackhole + staging URLs — zero network I/O Verification: all five feature combos green (default 68+13, noq 72+16, tcp 71+13, acme 68+11, all-features 75+17), clippy -D warnings, fmt --check, doc --no-deps
131 lines
6.3 KiB
Markdown
131 lines
6.3 KiB
Markdown
---
|
||
id: integration-suite
|
||
name: Integration suite — invariant pins + feature-matrix + seam round-trips (tests/)
|
||
status: completed
|
||
depends_on: [port-server, port-client]
|
||
scope: broad
|
||
risk: medium
|
||
impact: phase
|
||
level: implementation
|
||
tags: [tests, integration, invariants, phase-gate]
|
||
---
|
||
|
||
## Description
|
||
|
||
The `tests/` integration suite per ADR-006: the cross-module surfaces
|
||
the in-module seed tests cannot cover. A full-crate crate has no
|
||
workspace consumers to lean on, so this suite is the invariant gate
|
||
before the alknet rewrite consumes the crate.
|
||
|
||
### The suites
|
||
|
||
1. **Server seam round-trips** — for each `TlsIdentity` variant
|
||
(X509 via generated PEM files in a tempdir, RawKey via
|
||
`Ed25519SecretKey::generate`, SelfSigned):
|
||
`TlsServerConfig::new` → `for_noq()` (all-features) /
|
||
`for_tcp_tls()` / `rustls_config()` all succeed; the rustls config
|
||
carries the expected ALPN list and `max_early_data_size`.
|
||
2. **Client seam round-trips** — `TlsClientConfig::new` per
|
||
credentials cell → `for_noq()` / `into_rustls_config()`;
|
||
`enable_early_data` pinned true; ALPN single-value list.
|
||
3. **Verifier-selection matrix** — the full `local_identity` ×
|
||
`remote_identity` construction matrix (the port-client unit
|
||
assertions promoted to integration level, exercising the public API
|
||
only).
|
||
4. **The nine-scheme exact-list pin** — the regression-proof shape
|
||
(exact vec equality, not membership).
|
||
5. **Root-store fallback** — `load_platform_root_cert_store` (or the
|
||
config built from it) yields a non-empty store even when the
|
||
platform store is empty (webpki-roots merge; assert non-emptiness
|
||
and count > 0).
|
||
6. **ACME lifecycle** (`--features acme`): construct an `Acme` identity
|
||
with staging URL + tempdir cache; assert spawn-and-return semantics,
|
||
`acme-tls/1` in the ALPN list, and the resolver wiring (no network
|
||
I/O — do NOT hit Let's Encrypt).
|
||
7. **Feature-matrix gate**: `default = []` compiles + tests pass;
|
||
`--features noq`; `--features tcp`; `--features acme`;
|
||
`--all-features` (the gate is also a CI-verifiable convention, but
|
||
the suite includes the combination that exercises every
|
||
feature-gated accessor).
|
||
|
||
### Out of scope (explicitly)
|
||
|
||
Real handshakes (transport crates' job — the scope boundary), network
|
||
ACME orders, iroh (no iroh dep exists here), SOCKS5, dispatch.
|
||
|
||
## Work
|
||
|
||
1. Write the `tests/` files (one per suite group is fine).
|
||
2. Promote any in-module assertions that exercise cross-module
|
||
behavior to integration level (keep the in-module ones too).
|
||
3. Wire the exact-list and `enable_early_data` pins.
|
||
|
||
## Verification
|
||
|
||
- [x] Full suite green across all five feature combos
|
||
- [x] The nine-scheme test asserts exact list equality
|
||
- [x] `enable_early_data == true` asserted on a client config
|
||
- [x] ACME test performs no network I/O (staging URL is constructed,
|
||
never contacted)
|
||
- [x] `cargo clippy --all-targets -- -D warnings`, `cargo fmt --check`,
|
||
`cargo doc --no-deps` (doc comments render)
|
||
|
||
## Acceptance Criteria
|
||
|
||
- [x] All 7 suites exist and pass
|
||
- [x] The suite runs green with a single `cargo test --all-features`
|
||
- [x] `tests/` layout documented in the task Summary
|
||
|
||
## References
|
||
|
||
- docs/architecture/decisions/006-module-layout-and-tests.md (the test
|
||
plan)
|
||
- docs/architecture/server.md + client.md (the invariants)
|
||
- Prior art: the extracted crate's in-module tests
|
||
(`crates/alknet-tls/src/*.rs` `#[cfg(test)]` blocks)
|
||
|
||
## Notes
|
||
|
||
> Agent fills this during implementation.
|
||
|
||
## Summary
|
||
|
||
> Agent fills this on completion.
|
||
|
||
### The `tests/` layout
|
||
|
||
| File | Suites covered |
|
||
|------|----------------|
|
||
| `tests/server_seams.rs` | Suites 1 + 7(server half): `TlsServerConfig::new` per X509 (rcgen PEM pair in tempdir) / RawKey / SelfSigned → `rustls_config()` + `for_tcp_tls()` (tcp-gated) + `for_noq()` (noq-gated); ALPN + `max_early_data_size = u32::MAX` asserted per path; the Acme-no-feature → `TlsError::AcmeConfig` cell (`cfg(not(feature = "acme"))`) |
|
||
| `tests/client_seams.rs` | Suite 2: `TlsClientConfig::new` per credentials cell (pinned / CA / empty) → `into_rustls_config()` + `for_noq()` (noq-gated); `enable_early_data == true` + single-value ALPN asserted |
|
||
| `tests/invariant_pins.rs` | Suites 3 + 4 + 5: the nine-scheme exact-list pin (exact vec equality, order included); the verifier-selection matrix (Some → `FingerprintPinVerifier`, None → `WebPkiServerVerifier` — the fail-closed structure, asserted via the config's derived Debug) + client-auth presentation matrix (RawKey → `only_raw_public_keys()`, X509 → loaded chain, SelfSigned/None → nothing, Acme → `TlsError::AcmeConfig`), all through the public API; root-store fallback non-empty |
|
||
| `tests/acme_lifecycle.rs` | Suite 6 (`#![cfg(feature = "acme")]`): spawn-and-return semantics, `acme-tls/1` in the ALPN list, resolver wiring, `max_early_data_size` on the ACME branch — blackhole directory URL (`http://127.0.0.1:9`), staging URL constructed (never contacted) |
|
||
|
||
Suite 7 (the feature-matrix gate) is the verification command block below —
|
||
every combo is run, not just asserted.
|
||
|
||
### Notes / decisions
|
||
|
||
- `rustls::ClientConfig`'s `verifier` and `rustls::ServerConfig`'s
|
||
verifier are `pub(super)` at rustls 0.23.44 — the verifier identity is
|
||
asserted through the derived Debug output (the verifier's type name
|
||
appears in it); the client-auth resolver IS public
|
||
(`client_auth_cert_resolver`), so the presentation matrix asserts it
|
||
directly (`has_certs()` / `only_raw_public_keys()`).
|
||
- `alktls::TlsClientConfig.rustls_config` is `pub(crate)` — integration
|
||
level goes through `into_rustls_config()`.
|
||
- The Acme-no-feature test is `cfg(not(feature = "acme"))` (it asserts
|
||
the error only exists when the feature is absent; with the feature the
|
||
same identity legitimately constructs — that path is covered by
|
||
`acme_lifecycle`).
|
||
- Real handshakes, network ACME, iroh, SOCKS5, dispatch: out of scope
|
||
per the task (the scope boundary holds).
|
||
|
||
### Verification
|
||
|
||
- All five feature combos green: default (68 lib + 13 int), `noq` (72 +
|
||
16), `tcp` (71 + 13), `acme` (68 + 11), `--all-features` (75 + 17).
|
||
- `cargo clippy --all-targets --all-features -- -D warnings` ✓,
|
||
`cargo fmt --check` ✓, `cargo doc --no-deps` ✓.
|
||
- The ACME tests perform no network I/O (staging URL constructed, never
|
||
contacted; the lifecycle test uses `127.0.0.1:9` — a blackhole). |