Files
zip/runtimedoc_test.go
zeekayandHanzo Dev 5f71d2496b The router is three nouns, and every one of them is concrete
App, Group, Operation. A group is a prefix and what holds under it, an operation
is one typed route declared there, and the verb methods are generic methods —
which Go 1.27 allows on a concrete type and still refuses on an interface. So In
and Out come from the handler and the caller writes no type arguments:

	users := app.Group("/v1/users")
	users.Post("/", api.Create)

	mint := users.Group("/mint").With(middleware.Mint(gate))
	mint.Post("/deposit", api.Deposit)

A Group is a CLOSED HANDLE: two unexported fields, and no method that hands back
the app, the parent or the fiber underneath. A subsystem given the group at its
own prefix cannot reach above it, so confinement is a property of the type
instead of a rule something has to police — which is what a host needs in order
to hand out a bounded surface and know what comes back.

That is bought by giving up the router interface, deliberately. Router and
OpTarget are gone, and with them the ability to implement a route table outside
this package: a decorator is no longer a router, a gate is middleware on a group,
and a bound is a path. The five package-level verb functions go the same way —
a registration is a method on the thing it registers into.

Raw carries what is not a typed operation: a stream, an upgrade, a probe, a
foreign subtree. It names its method, and it is the longer spelling because
opting out of the typed model is the exception and should read like one.



Co-authored-by: Hanzo Dev <[email protected]>
2026-09-17 01:39:17 -07:00

133 lines
4.1 KiB
Go

package zip
import (
"context"
"strings"
"sync"
"testing"
)
// The runtime document makes claims; these check them against the code.
// "Before any host exists, Registry(app) recomputes per call and is not
// goroutine-safe — mutable phase, caller's problem."
func TestRuntimeDoc_RegistryRecomputesBeforeAHostExists(t *testing.T) {
a := billingApp()
first := a.Registry()
if _, live := a.Generation(); live {
t.Fatal("inspecting a program installed a generation")
}
// Recomputed, not memoised: a different backing array each call.
if &first[0] == &a.Registry()[0] {
t.Error("Registry() memoised before a host exists; the doc says it recomputes")
}
// And once a host exists it is the generation's, computed once.
h, err := host2(a)
if err != nil {
t.Fatalf("host: %v", err)
}
if &h.Registry()[0] != &h.Registry()[0] {
t.Error("Registry() is not stable on a live generation")
}
}
// "Each generation retains its walk result; reducers serving the live
// generation reuse it rather than rewalking."
func TestRuntimeDoc_GenerationRetainsItsWalkResult(t *testing.T) {
a := billingApp()
h, err := host2(a)
if err != nil {
t.Fatalf("host: %v", err)
}
g := a.live.Load()
if g == nil || len(g.occ) == 0 {
t.Fatal("the live generation retains no walk result")
}
// plan() hands back that same slice rather than walking again.
if &a.plan()[0] != &g.occ[0] {
t.Error("plan() rewalked instead of reusing the generation's result")
}
_ = h
}
// "Requests pin their generation on arrival; no locks on the hot path."
// Concurrent readers of the live generation while it is being replaced.
func TestRuntimeDoc_NoLocksOnTheHotPath(t *testing.T) {
a := quiet("host")
a.Raw("GET", "/x", func(c *Ctx) error { return c.String(200, "x") })
h, err := host2(a)
if err != nil {
t.Fatalf("host: %v", err)
}
var wg sync.WaitGroup
for i := 0; i < 32; i++ {
wg.Add(1)
go func() { defer wg.Done(); _ = a.Registry(); _ = a.live.Load().serve }()
}
wg.Add(1)
go func() {
defer wg.Done()
p := quiet("p")
p.Raw("GET", "/p", func(c *Ctx) error { return nil })
_ = h.Include(p)
}()
wg.Wait()
}
// "Registry and Declaration panic with the joined validation error when the
// program does not compose — validation runs before any rendering, so no tool
// can emit an empty document and exit 0."
func TestRuntimeDoc_ProjectionsPanicRatherThanEmit(t *testing.T) {
a := quiet("broken")
a.Raw("GET", "/dup", func(c *Ctx) error { return nil })
a.Raw("GET", "/dup", func(c *Ctx) error { return nil })
for name, fn := range map[string]func(){
"Registry": func() { _ = a.Registry() },
"Declaration": func() { _ = a.Declaration() },
} {
func() {
defer func() {
r := recover()
if r == nil {
t.Errorf("%s() answered for a program that does not compose", name)
return
}
if msg, _ := r.(string); !strings.Contains(msg, "/dup") {
t.Errorf("%s() panic does not carry the validation error: %v", name, r)
}
}()
fn()
}()
}
}
// Derived validation is its own stage: two occurrences can be structurally fine
// and still derive one operation id, which does not exist until derivation runs.
func TestRuntimeDoc_DerivedValidationCatchesIDCollisions(t *testing.T) {
// Two DIFFERENT definitions, different paths (no address conflict), both
// declaring the same operation id, composed at the same prefix.
one := quiet("one")
one.Get("/a", func(ctx1 context.Context, in *invoiceIn) (*invoiceOut, error) { return &invoiceOut{}, nil }, WithOperationID("shared"))
two := quiet("two")
two.Get("/b", func(ctx1 context.Context, in *invoiceIn) (*invoiceOut, error) { return &invoiceOut{}, nil }, WithOperationID("shared"))
host := quiet("host")
host.Use(one, two)
occ, err := walk(host)
if err != nil {
t.Fatalf("walk: %v", err)
}
if err := structural(occ); err != nil {
t.Fatalf("structural validation should pass — the addresses do not collide: %v", err)
}
err = derived(occ)
if err == nil {
t.Fatal("two occurrences derived one operation id and derived validation passed")
}
if !strings.Contains(err.Error(), "shared") {
t.Errorf("refusal does not name the id: %v", err)
}
}