You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Test work is spread across separate issues with no single place defining how tinytap is tested. #56 is the umbrella: it defines the layering + conventions and tracks the test issues to completion. When every issue under "Tracked issues" below is closed, the test surface for the code that exists today is complete — there are no test tasks living only inside #56.
The layering
tinytap's architecture (see CLAUDE.md "Boundaries") isolates the testable parts from the irreducibly system-dependent parts. The test layers mirror that:
Layer
Targets
Privilege
Runs in CI
Unit
parser (HTTP state machine), proc (/proc lookup), pairer
none
yes — every PR
Integration
loader + ringbuf decode against a tiny fixture program (#58)
real BPF attached, real traffic, assertion-driven scenarios (#29)
root
privileged/self-hosted or manual
Unit is where most coverage lives — pure Go, fast, deterministic, no eBPF, no syscalls. This is the layer CI — build, vet, and test on every PR #55's main job gates on.
Keeping the privileged layers out of the default run. The unit layer never needs privilege — that's the point of the split. Integration/e2e do (CAP_BPF / root), and the risk is only that a whole-suite go test ./... sweeps them up and fails on an unprivileged runner. Separate them with a build tag (e.g. //go:build privileged) so the default go test ./... compiles and runs unit only, and the privileged layers run under go test -tags=privileged ./... on a privileged/self-hosted runner or in the Lima VM. (A runtime t.Skip when CAP_BPF is absent is an acceptable fallback, but build-tag separation is cleaner — nothing to skip in the default run.)
No privilege in the unit layer — if a test needs root or eBPF, it belongs in integration/e2e, not unit. Keep the pure-Go layer runnable with plain go test.
Deterministic — no sleeps-as-synchronization, no network to the internet. E2e talks to a locally-spawned server only; proc tests read an injectable fixture tree, not the live /proc.
Process-relative vocabulary in test names and comments (outgoing/incoming), per CLAUDE.md terminology.
Tracked issues
Grouped by layer. The mapping is intentionally not 1-to-1: the Unit layer owns several issues, and #27 is a lifecycle feature (not a test layer) that adds behaviour the unit and e2e tests then exercise. Closing all of these = the test surface for today's code is done.
Why
Test work is spread across separate issues with no single place defining how tinytap is tested. #56 is the umbrella: it defines the layering + conventions and tracks the test issues to completion. When every issue under "Tracked issues" below is closed, the test surface for the code that exists today is complete — there are no test tasks living only inside #56.
The layering
tinytap's architecture (see CLAUDE.md "Boundaries") isolates the testable parts from the irreducibly system-dependent parts. The test layers mirror that:
parser(HTTP state machine),proc(/proc lookup), pairerloader+ ringbuf decode against a tiny fixture program (#58)Keeping the privileged layers out of the default run. The unit layer never needs privilege — that's the point of the split. Integration/e2e do (CAP_BPF / root), and the risk is only that a whole-suite
go test ./...sweeps them up and fails on an unprivileged runner. Separate them with a build tag (e.g.//go:build privileged) so the defaultgo test ./...compiles and runs unit only, and the privileged layers run undergo test -tags=privileged ./...on a privileged/self-hosted runner or in the Lima VM. (A runtimet.SkipwhenCAP_BPFis absent is an acceptable fallback, but build-tag separation is cleaner — nothing to skip in the default run.)Conventions
go test./proc.Tracked issues
Grouped by layer. The mapping is intentionally not 1-to-1: the Unit layer owns several issues, and #27 is a lifecycle feature (not a test layer) that adds behaviour the unit and e2e tests then exercise. Closing all of these = the test surface for today's code is done.
Unit
parser.Close)procPID→process-name lookup (extract from main.go + tests)parser.Close/pairer.Close, fd reuse, concurrent connectionsIntegration
loader+ ringbuf decode against a fixture BPF programE2e
Lifecycle feature (cross-cuts unit + e2e, not a test layer)
See also (referenced, not tracked here): #36 (payload cap affects body-size + chunked cases), #37 (server-compat matrix overlaps e2e).
Out of scope
docs/testing.md— an optional write-up of this strategy; nice to have, not gating on closing Test strategy — layering, what runs where, conventions #56