- Rewrote em dash prose across the whole tree outside docs/reviews/ (the historical reviews stay verbatim, matching the alktunnels and alktty precedent): src doc comments and inline comments (587), docs/architecture (319), docs/research + sdd_process (484), tests (361), tasks (899), fuzz (28), README/CHANGELOG/Cargo.toml (77), AGENTS.md (85), and the .opencode/agents prompts (109) - Fenced content follows the precedent: comments, ASCII-diagram label separators, and template placeholders rewritten; wire data, JSON/BAST documents, byte diagrams, and transcripts survive verbatim; assert, expect, panic, error-display, and format message literals rewritten as prose; no code, fixture, or test data change - src/rfc1928/LICENSE: the two dashes in alksocks' own provenance header rewritten; the MIT legal text untouched - AGENTS.md adds Project Convention 18: no em dashes in prose; varied restructure, no spaced-hyphen substitutes, fenced prose rule, en dash numeric ranges exempt, docs/reviews/ exempt from the greps - tasks/*.md frontmatter: the name values rewritten by the sweep are now quoted (a raw colon inside an unquoted YAML scalar parsed as null); taskgraph validate reports 37 tasks, no issues Verification: cargo test; cargo clippy --all-targets -D warnings; cargo fmt --check; cargo doc --no-deps; fuzz corpus replay (cargo test --manifest-path fuzz/shared/Cargo.toml); wasm check and clippy for wasm32-unknown-unknown with -D warnings; em dash grep over the repo returns matches only in docs/reviews/ and the bast.md BAST JSON data fence
216 lines
11 KiB
Markdown
216 lines
11 KiB
Markdown
---
|
|
id: implementation/core-test-harness
|
|
name: "Core: integration test harness (duplex transport + identities + in-process seams)"
|
|
status: completed
|
|
depends_on: [implementation/core-seam-types, implementation/core-params-errors]
|
|
scope: moderate
|
|
risk: medium
|
|
impact: phase
|
|
level: implementation
|
|
---
|
|
|
|
## Description
|
|
|
|
Build `tests/harness.rs`, the producer↔consumer wiring over a
|
|
`tokio::io::duplex` carrying the channels 8-byte chunk format, so
|
|
every subsequent core task tests end-to-end without the `local`
|
|
feature. This is the alkcall-0.8.0-first-consumption task: the POCs
|
|
ran 0.7.1; the harness proves the 0.8.0 establishment surface
|
|
(`register_openable_with_establisher`, `Establishment`,
|
|
`ChannelClient::open_channel`) works for this crate's shape before
|
|
the producer task builds on it. Template:
|
|
`/workspace/@alkdev/alktunnels/tests/harness.rs` (the sibling's
|
|
harness, same topology, adapted to this crate's seams).
|
|
|
|
Topology (the alktunnels harness shape):
|
|
|
|
- **producer** (connect side of the channels connection; runs
|
|
`ChannelClient::from_connection_with_serving`): serves the socks5
|
|
open op + generic channel ops on its serving registry. Its manager
|
|
allocates the channel IDs (`ChannelSide::Connect`, since the serving
|
|
side allocates and the initiator adopts).
|
|
- **consumer** (accept side): the `ChannelsAdapter` + capturing
|
|
install hook. The hook captures the consumer's channel-0
|
|
`CallConnection` and `ChannelManager`.
|
|
- **The POC #1 wiring requirement is load-bearing here** (producer.md
|
|
§Harness wiring requirement): the accept-side install hook must
|
|
call `channel0_conn.set_identity(resolved)` before building the
|
|
`CallConnection`; without it, scope-gated opens fail closed with
|
|
`FORBIDDEN "authentication required"` (correct behavior, implicit
|
|
wiring). The harness makes the requirement explicit and tested.
|
|
|
|
Deliverables:
|
|
|
|
1. **Identities**: `consumer_identity()` (`socks5:open` only),
|
|
`bind_identity()` (`socks5:bind` only, the command-gate probe),
|
|
`both_scopes_identity()` (both grants, explicitly and never implied,
|
|
ADR-006), plus a `TestIdProvider` resolving them (the alktunnels
|
|
harness shape).
|
|
2. **In-process seam closures** (no sockets; the protocol layer is
|
|
substrate-agnostic, so the harness dials in-process):
|
|
- `echo_dial()`: a `DialFn` returning duplex-halves wired to an
|
|
in-process echo server (the POC #1 shape); plus
|
|
`refusing_dial()` (a failing dial for the refusal-mapping
|
|
tests).
|
|
- `echo_egress()`: an `EgressFactory` whose egress echoes
|
|
datagrams back (the POC #2 shape, readiness-contract-clean:
|
|
parks on a channel `recv()`, `Pending` when nothing is ready).
|
|
- `echo_accept()`: a `BindAcceptFactory` whose accept resolves
|
|
immediately with in-process halves (the POC #5 shape); plus a
|
|
failing variant.
|
|
3. **The producer registration helper**: a `spawn_producer(...)`
|
|
function wiring `register_socks5_openable` (or its task-6
|
|
successor) with the harness seams, the single place the
|
|
producer-side wiring lives, so the per-command tasks only write
|
|
tests.
|
|
4. **A raw RFC client helper**: a hand-rolled minimal RFC 1928
|
|
client over any `AsyncRead + AsyncWrite` (greeting → method
|
|
selection → command with target → reply read) following the differential
|
|
suite's established pattern, so producer tasks test without
|
|
depending on the consumer session types (which land later).
|
|
5. **One smoke test** proving the full loop: harness up → producer
|
|
registered → consumer opens a channel → raw RFC CONNECT
|
|
conversation → echo round-trip. (This test needs a minimal
|
|
CONNECT producer path; if the producer task hasn't landed, stub
|
|
the handler with the POC #1 `serve_socks5_over` shape inline in
|
|
the harness, and the producer task then replaces it. Alternatively
|
|
sequence this task to land immediately before the producer task
|
|
and let the smoke test be the producer task's first test; the
|
|
implementer's judgment; the acceptance bar is the harness
|
|
compiling with the identities + seams + helpers tested.)
|
|
|
|
## Acceptance Criteria
|
|
|
|
- [x] Harness topology works over alkcall 0.8.0 (the establishment
|
|
surface consumed successfully; the first-consumption risk
|
|
retired)
|
|
- [x] Identity wiring explicit: `set_identity` in the install hook;
|
|
a scope-less identity gets `FORBIDDEN` (fails closed), tested
|
|
- [x] All three identities + all in-process seam closures unit-tested
|
|
- [x] The raw RFC client helper round-trips a greeting + command +
|
|
reply against a hand-rolled server over a duplex
|
|
- [x] No `local` feature, no sockets; everything in-process
|
|
- [x] `cargo test` green; clippy/fmt clean; both wasm checks clean
|
|
(the harness is dev-only; verify it stays out of the wasm
|
|
build)
|
|
|
|
## References
|
|
|
|
- Template: `/workspace/@alkdev/alktunnels/tests/harness.rs` (the
|
|
topology + identity shapes)
|
|
- POC harnesses: `/workspace/alksocks-connect-poc/src/harness.rs`,
|
|
`/workspace/alksocks-udp-poc/src/harness.rs`,
|
|
`/workspace/alksocks-bind-poc/src/harness.rs`
|
|
- docs/architecture/producer.md §Harness wiring requirement (the
|
|
`set_identity` requirement)
|
|
- docs/architecture/decisions/006-access-control.md (the scope
|
|
identities)
|
|
|
|
## Notes
|
|
|
|
> Filled by the implementation agent (2026-09-23).
|
|
|
|
- `tests/harness.rs`: the producer↔consumer wiring over one
|
|
`tokio::io::duplex` carrying the channels 8-byte chunk format, in
|
|
the task's topology: **producer = connect side**
|
|
(`ChannelClient::from_connection_with_serving`; its manager
|
|
allocates the odd channel IDs), **consumer = accept side** (the
|
|
`ChannelsAdapter` + capturing install hook; the hook captures the
|
|
consumer's channel-0 `CallConnection` + `ChannelManager`).
|
|
- The load-bearing POC #1 wiring requirement is explicit and tested:
|
|
the hook calls `channel0_conn.set_identity(resolved)` before
|
|
building the `CallConnection`; the transport identity is set on
|
|
the dialing (producer) connection before
|
|
`from_connection_with_serving` (CF-005 (b) primary path). The
|
|
scope-less identity test asserts the `FORBIDDEN` code and that no
|
|
channel survives on either side (fail-closed).
|
|
- Identities: `consumer_identity()` (`socks5:open` only),
|
|
`bind_identity()` (`socks5:bind` only), `both_scopes_identity()`
|
|
(both, explicit, per ADR-006), `TestIdProvider` +
|
|
`TEST_AUTH_TOKEN` (the token-resolver path, unit-tested both
|
|
directions).
|
|
- In-process seams (no sockets, no `local`): `echo_dial()`
|
|
(echo-per-dial, exercised through `pump_bidi`, the producer data
|
|
(echo-per-dial, exercised through `pump_bidi`, the producer data
|
|
plane's shape), `refusing_dial(err)` (variant-preserving per-call
|
|
reconstruction, as the seam errors are not `Clone`),
|
|
`echo_egress()` (a `ChanEgress` whose `next_reply` parks on
|
|
`mpsc::UnboundedReceiver::recv()`: `Pending` when idle, with the
|
|
readiness contract verified with a 50ms timeout probe),
|
|
`refusing_egress(err)`, `echo_accept()` (reply#1 advertises a
|
|
static in-process BND.ADDR, `ECHO_BIND_PORT` 2121; the accept
|
|
future resolves immediately with echo-pipe halves),
|
|
`refusing_accept_factory(err)` (pre-reply#1) and
|
|
`refusing_accept(err)` (post-reply#1).
|
|
- `register_socks5_openable_minimal` + `socks5_session_establisher`:
|
|
the producer-side wiring helper over alkcall 0.8.0's
|
|
`ChannelCore::register_openable_with_establisher` (spec from
|
|
`socks5_open_spec()`, `Establishment::default()` for a session gate
|
|
only; the dial is in-stream). The establisher witnesses the
|
|
per-call opener identity id (CF-006) into an `IdentityWitness`,
|
|
asserted end-to-end in the smoke test.
|
|
- `RegisterMode` (`MinimalConnect(dial)` / `NoSocks5`) controls what
|
|
`wire_with` registers post-hoc (W2) on the producer's serving
|
|
registry; generic channel ops always register.
|
|
- The smoke test's producer path is the minimal CONNECT stand-in
|
|
(`make_minimal_connect_handler` → `serve_connect_minimal`: the
|
|
forked typestate's `accept_no_auth` → `read_command` → CONNECT-only
|
|
dispatch → the injected `DialFn` → `reply_success` → `pump_bidi`
|
|
inline, R-02). The producer task replaces this; the registration
|
|
shape (spec/establisher/handler/auth/timeout) stays.
|
|
- `RawRfcClient<S>`: hand-rolled greeting (`05 01 00` → `05 00`) +
|
|
request encoder (all three commands, all address forms via
|
|
`TargetAddr::to_be_bytes()`) + reply reader (v4/v6/domain BND.ADDR
|
|
forms; returns `(code, addr_bytes)`). Round-trip-tested against a
|
|
hand-rolled byte-level server over a duplex, success + refusal
|
|
reply codes; the harness is producer-independent (no consumer
|
|
session types).
|
|
- Wire note: the smoke test calls the open op via
|
|
`consumer_call.call(OP_SOCKS5_OPEN, Socks5Params::as_json())` and
|
|
adopts on `consumer_manager` (the alktunnels `wire_with` +
|
|
`open_reverse_channel` shape) rather than
|
|
`ChannelClient::open_channel`: in this topology the open op is
|
|
served by the producer's registry and reached from the accept
|
|
side's channel-0 call surface; `open_channel` remains the
|
|
consumer-session surface for the consumer task's session types.
|
|
- The harness is `tests/harness.rs` only (dev-only), verified out
|
|
of the wasm build by the targeted checks below.
|
|
|
|
## Summary
|
|
|
|
> Filled on completion (2026-09-23).
|
|
|
|
Built `tests/harness.rs`, the integration test harness: the
|
|
producer↔consumer topology over one `tokio::io::duplex` (producer =
|
|
connect side/serving; consumer = accept side/adapter + capturing
|
|
hook), the alkcall 0.8.0 establishment surface consumed for the
|
|
first time by this crate (`register_openable_with_establisher`,
|
|
`Establishment::default`, the consumer-side call + adopt dance), and
|
|
the first-consumption risk retired with 14 green tests.
|
|
|
|
- The three identities + `TestIdProvider` (all unit-tested).
|
|
- All in-process seam closures: `echo_dial`/`refusing_dial` (variant-
|
|
preserving), `echo_egress` (readiness contract verified: a 50ms
|
|
timeout probe proves `next_reply` parks, never a hot `Ok(None)`) /
|
|
`refusing_egress`, `echo_accept` (immediate resolve + echo) /
|
|
`refusing_accept_factory` / `refusing_accept` (two-stage refusal
|
|
shapes, mapping-table-verified through `to_reply_error()`).
|
|
- The producer registration helper (`register_socks5_openable_minimal`
|
|
+ `socks5_session_establisher` + `RegisterMode`), the single place
|
|
the producer-side wiring lives; the CF-006 identity witness rides
|
|
it and is asserted.
|
|
- `RawRfcClient`: the raw RFC 1928 client helper over any
|
|
`AsyncRead + AsyncWrite`, round-tripped against a hand-rolled
|
|
byte-level server (success + refusal replies).
|
|
- The smoke test: harness up → producer registered → consumer open +
|
|
adopt → raw RFC CONNECT → echo round-trip through `pump_bidi`, with
|
|
the establisher witness asserted mid-flight. Plus the fail-closed
|
|
probe (scope-less identity → `FORBIDDEN`, no channel anywhere) and
|
|
the odd-ID allocation/adopt sanity probe.
|
|
|
|
Verification: `cargo test` (127 green: 76 lib + 14 harness + 37
|
|
rfc1928-differential), `cargo test --all-features` green, `cargo
|
|
clippy --all-targets -- -D warnings` clean, `cargo fmt --check`
|
|
clean, `cargo doc --no-deps` zero warnings, both wasm checks
|
|
(`cargo check` + `cargo clippy` on `wasm32-unknown-unknown`) clean,
|
|
and the harness stays out of the wasm build. |