Documentation
¶
Overview ¶
Package conformance is the Harbor Protocol conformance suite — the single binding pass/fail definition of "the Protocol surface works at version 0.1.0" (RFC §5 + master-plan Phase 62 detail block; D-080).
The suite exhaustively exercises every Protocol method (internal/protocol/methods), every Protocol error code (internal/protocol/errors), every documented event-filter shape, the Phase 59 versioning + capability handshake, and the Phase 61 auth pipeline. It runs the same scenario bodies against TWO transports:
- In-process — direct `protocol.ControlSurface.Dispatch` calls. No HTTP; the transport-agnostic surface Phase 54 shipped.
- Over-the-wire — the Phase 60 mux mounted on an `httptest.Server`, including the Phase 61 auth middleware. JWT bearers, JSON bodies, HTTP status codes.
A conformance pass means the Protocol surface is consistent across the two consumer profiles a Console (the canonical client) would reach the runtime through: in-process embedding (e.g. `harbor dev`'s own SPA mount) and remote (a third-party Console over the wire).
Consumer pattern ¶
The suite is itself a reusable artifact (D-025): one shared `Stack` serves N concurrent invocations. Consumers wire a `Factory` that builds a fresh `Stack` per top-level subtest (each subtest gets its own in-mem state, its own event bus, its own task registry, its own JWT keypair) and pass it to `RunSuite(t, factory)`:
func TestProtocol_Conformance(t *testing.T) {
conformance.RunSuite(t, conformance.NewDefaultFactory())
}
The default factory wires real drivers everywhere on the seam — real `tasks.TaskRegistry` (inprocess), real `events.EventBus` (inmem), real `state.StateStore` (inmem), real `protocol.ControlSurface`, real `protocol/auth.Validator` over a real ES256 keypair, real Phase 60 `transports.NewMux` under `httptest.Server`. A future Protocol transport (WebSocket, stdio) consumes the suite via the same Factory seam — no second conformance implementation.
Exhaustiveness ¶
The suite asserts the matrix's exhaustiveness at boot:
- Every entry in `methods.Methods()` has a happy-path AND a malformed-request scenario.
- Every entry in `errors` `canonicalCodes` (via the eight constants enumerated in errorCodeMatrix below) has at least one failure scenario surfacing it.
- Every entry in `types.Capabilities()` is observed in the version handshake test.
A new Protocol method, error code, or capability constant lands in the same PR as its conformance entry; the exhaustiveness check fails loudly otherwise — the failure mode the suite exists to prevent (silent surface drift) is mechanically guarded.
Concurrent reuse (D-025) ¶
`RunSuite` is safe to call concurrently against a single factory: the factory builds a fresh `Stack` per subtest, and every scenario closes over its own request, response, identity, and (for the wire transport) per-request HTTP client. The concurrent-reuse scenario runs N=100 mixed-method invocations against ONE shared `Stack` under `-race` to pin the contract.
Index ¶
Constants ¶
This section is empty.
Variables ¶
var FixedNow = time.Date(2026, 5, 15, 12, 0, 0, 0, time.UTC)
FixedNow is the deterministic clock the suite's JWT validator + token minter share so exp/nbf behaviour is reproducible across runs. Exported so external consumers (e.g. `test/integration/wave10_test.go`'s own Stack builder) can pin to the same instant — keeping `fixedNow` in one canonical home instead of two `time.Date(2026, 5, 15, 12, 0, 0, 0, time.UTC)` literals (PR #91, Wave 10 audit NIT-3). The concrete value is irrelevant — what matters is that every JWT signer + every validator sees the same instant.
Functions ¶
func RunSuite ¶
RunSuite runs every scenario as a subtest. Consumers wire a Factory that builds a fresh Stack per top-level subtest; the suite itself owns the matrix definition.
The suite asserts the matrix's exhaustiveness at the top of the run:
- Every entry in methods.Methods() appears as a happy-path scenario name.
- Every entry in errorCodeMatrix is in lockstep with the canonical errors package's IsValidCode.
- Every entry in types.Capabilities() is observed by the version handshake test.
A new method / code / capability without a corresponding scenario fails the suite at boot — the silent-surface-drift failure mode the suite exists to prevent is mechanically guarded.
Types ¶
type Factory ¶
Factory builds a fresh Stack per subtest. Implementations MUST wire real drivers everywhere on the seam — a mock at the boundary defeats the conformance suite's purpose (CLAUDE.md §17.3).
func NewDefaultFactory ¶
NewDefaultFactory returns the canonical factory used by Harbor's own conformance test. It wires:
- real `tasks.TaskRegistry` (inprocess driver),
- real `events.EventBus` (inmem driver),
- real `state.StateStore` (inmem driver),
- real `protocol.ControlSurface`,
- real `protocol/auth.Validator` over a real ES256 keypair (loaded from `internal/protocol/auth/testdata/`),
- real Phase 60 `transports.NewMux` with the Phase 61 validator threaded via `WithValidator`.
A keypath argument lets a consumer relocate the testdata directory (useful when the suite is invoked from `test/integration/`); an empty string falls back to the package-relative default `../auth/testdata`.
type Stack ¶
type Stack struct {
// Surface is the in-process Protocol task-control surface (Phase
// 54). The in-process transport scenarios reach the runtime
// through `Surface.Dispatch`.
Surface *protocol.ControlSurface
// Bus is the real events.EventBus the SSE wire transport
// subscribes to and the runtime publishes lifecycle events on.
Bus events.EventBus
// Steering is the steering.Registry behind Surface — the
// nine-control methods enqueue events on inboxes opened here.
// Exposed so scenarios can pre-open an inbox before submitting a
// control (mirroring the Wave 9 pattern).
Steering *steering.Registry
// Tasks is the real tasks.TaskRegistry behind Surface — the
// `start` method spawns on this; identity-propagation scenarios
// read spawned tasks back via Get to verify the triple landed.
Tasks tasks.TaskRegistry
// Mux is the Phase 60 wire mux mounted on a Phase 61 validator.
// The over-the-wire scenarios round-trip through `httptest.Server`
// against this handler.
Mux http.Handler
// SignToken mints a valid bearer token for the given identity +
// scopes against the validator's KeySet. Wire-transport scenarios
// call SignToken to obtain the Authorization: Bearer header.
SignToken func(t *testing.T, id identity.Identity, scopes []auth.Scope) string
// SignHS256Token mints an HS256-signed token using the suite's
// ES256 public key bytes as the HMAC secret. Exercises the
// classical algorithm-confusion attack the parser-level allowlist
// closes (CLAUDE.md §7 rule 1).
SignHS256Token func(t *testing.T, id identity.Identity) string
// SignAlgNoneToken mints an `alg: none` token. Exercises RFC 7519
// §6.1's escape hatch the validator MUST reject (CLAUDE.md §7).
SignAlgNoneToken func(t *testing.T, id identity.Identity) string
// SignExpiredToken mints an otherwise-valid token whose `exp` is
// in the past relative to fixedNow.
SignExpiredToken func(t *testing.T, id identity.Identity) string
// Cleanup tears down every driver the factory opened. Called by
// the suite after every top-level subtest.
Cleanup func()
}
Stack is the per-subtest seam — the real-driver runtime surface + the wired Protocol transports a conformance scenario exercises. Each top-level subtest gets a fresh Stack (via Factory) so per-test state (the task registry, the event bus, the steering registry) does not bleed across subtests. The Stack itself is a D-025 compiled artifact — its fields are set once at construction and never mutated; the concurrent-reuse scenario runs N≥100 invocations against one shared Stack.