conformance

package
v0.1.29 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 27, 2026 License: MIT Imports: 3 Imported by: 0

Documentation

Overview

Package conformance is the executable contract of router.Router.

The interface states the SIGNATURES; this package states the BEHAVIOUR. Two implementations can satisfy the interface — compile without a complaint — and disagree on everything that matters. That is not hypothetical: it is what happened. One registered routes by path alone (so two methods on one path panicked), the other ran its access gate before establishing identity (so every guarded route was a permanent 403). Both compiled. Both were wrong. Nothing caught it, because the only implementation under test was the mock — the one nobody deploys.

A compiler cannot catch "these two implementations behave differently": both satisfy the interface. So it has to become something that goes red. This is that something.

An implementation proves conformance from its own test package:

func TestHTTPDConformance(t *testing.T) {
    conformance.Run(t, conformance.Factory{New: newHTTPD, Verify: verifyStartup})
}

The shape (exported suite, parameterized by a factory, one t.Run per clause) is the one the Go team uses for the same job: golang.org/x/net/nettest.TestConn for net.Conn, and testing/fstest.TestFS for fs.FS. This package importing "testing" in non-test code is deliberate and is the whole point — a _test.go file cannot be imported by another repo.

Index

Constants

View Source
const (
	// UserAuthorized holds Action on Resource: a guarded route must SERVE it.
	UserAuthorized = "conformance-authorized"
	// UserUnauthorized is a valid identity WITHOUT the permission: a guarded route
	// must reject it — authentication is not authorization.
	UserUnauthorized = "conformance-unauthorized"
	// Anonymous is no identity at all.
	Anonymous = ""
)

The identities the suite drives through ServeFunc.

View Source
const Action = model.Update

Action is the permission the suite's guarded routes require.

View Source
const Resource model.Resource = "conformance"

Resource is the resource the suite guards its routes with.

Variables

This section is empty.

Functions

func Authorize

func Authorize(userID string, r model.Resource, a model.Action) bool

Authorize is the authorizer the suite drives its cases with. An implementation MUST wire the one handed to it in Setup — it is how the suite tells an authorized caller from a merely authenticated one.

func Run

func Run(t *testing.T, f Factory)

Run executes every clause of the contract against the implementation.

Types

type Factory

type Factory struct {
	// New returns an EMPTY Router and the ServeFunc that drives it. It is called once per
	// case, so no case can be polluted by another's routes.
	New func(t *testing.T, s Setup) (router.Router, ServeFunc)

	// Verify reports the error the implementation raises AT STARTUP for the routes
	// registered so far — nil meaning "this configuration is legal".
	//
	// Optional. An implementation that cannot fail at startup leaves it nil, and the
	// contradiction case skips with a loud reason instead of passing quietly.
	Verify func(r router.Router) error

	// ServeOp drives ONE request through a route registered via OpRegistry.Op(name, h) — the
	// provider-side counterpart of router.Caller.Call(name, args, cb). It receives the SAME
	// Router New built, so registration (by the clause) and invocation (by this func) share
	// one instance; name is the op name the clause registered.
	//
	// Optional. An implementation that does not yet implement Op leaves this nil, and the
	// Op clauses skip with a loud reason instead of failing to compile.
	ServeOp func(r router.Router, name string, body []byte, userID string) Response
}

Factory builds a fresh Router plus the driver that sends a request through it.

type Response

type Response struct {
	Status int
	Body   []byte
}

Response is what came back from the transport. It is deliberately not an http.Response: the contract is isomorphic, and one of its implementations does not run on a server.

type ServeFunc

type ServeFunc func(method, path string, body []byte, userID string) Response

ServeFunc drives ONE request through the Router under test and reports what came back.

It must go through the implementation's REAL pipeline — identity, access gate, middleware, handler. A ServeFunc that calls the matched handler directly proves the opposite of what it claims to: the gate is precisely what is under test.

userID is the identity the transport reports for this caller ("" = anonymous). The implementation routes it through whatever its authentication seam is, the same one a production caller would go through.

type Setup

type Setup struct {
	// Authorize answers whether an identity holds a permission. The implementation MUST
	// install it as its authorizer. Wiring something else — or nothing — makes the
	// authorization cases meaningless.
	Authorize model.Authorizer
}

Setup is what the suite hands the implementation so it can be built the way the suite needs to drive it.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL