Skip to content

Test strategy — layering, what runs where, conventions #56

Description

@shinagawa-web

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:

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) CAP_BPF / root best-effort (see #55)
E2e real BPF attached, real traffic, assertion-driven scenarios (#29) root privileged/self-hosted or manual

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.)

Conventions

  • Table-driven for the parser (Expand parser unit tests — status codes, methods, body sizes, malformed input #28, Chunked transfer-encoding — parser support + tests #60): one row per case (status codes, methods, body sizes, chunked, malformed/partial input). Golden inputs as testdata files where payloads are large.
  • 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.

Unit

Integration

E2e

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    gov0.3.0Filtering milestone

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions