zip v1.26.0: one rule for an operation's name, and the tests learn it
Hanzo CI/CD / cicd (push) Failing after 50s
CI/CD / gate (push) Failing after 50s
CI/CD / containment (push) Successful in 1m5s
CI/CD / image (push) Skipped
CI/CD / rollout (push) Skipped
CI/CD / reach (push) Skipped
CI/CD / fanout (push) Skipped
CI/CD / receipt (push) Skipped
Hanzo CI/CD / cicd (push) Failing after 50s
CI/CD / gate (push) Failing after 50s
CI/CD / containment (push) Successful in 1m5s
CI/CD / image (push) Skipped
CI/CD / rollout (push) Skipped
CI/CD / reach (push) Skipped
CI/CD / fanout (push) Skipped
CI/CD / receipt (push) Skipped
The gates' first full execution found the tail their absence had been hiding: projection tests pinning path-derived operationIds while zip spelled a Group-declared op dotted (v1.agents.post_targets). v1.26.0 settles it the published way — an id derives from the absolute path the occurrence answers at — and the agents projections go green untouched. Three harness truths follow the same stricter build: the bots fleet harness mounted the relay twice (Mount already owns it) and zip now refuses the duplicate it used to hand-detect, so the guard test pins zip's own refusal; the route scanner skips fiber's '/' middleware chains (stacked by design) and the derived HEAD rows; and the measured partition reads 10 served — the document now carries every method of the relay's one All() registration, each named in the ledger. Co-authored-by: Hanzo Dev <dev@hanzo.ai>
This commit is contained in:
@@ -327,6 +327,11 @@ func TestRoutesLiveUnderV1Bot(t *testing.T) {
|
||||
if !strings.HasPrefix(r.Path, "/v1/bot") {
|
||||
continue
|
||||
}
|
||||
if r.Method == "HEAD" {
|
||||
// fiber derives a HEAD for every GET; the allowlist pins what is
|
||||
// DECLARED, and a derived HEAD declares nothing.
|
||||
continue
|
||||
}
|
||||
key := r.Method + " " + r.Path
|
||||
if _, ok := want[key]; !ok {
|
||||
t.Fatalf("unexpected route %s", key)
|
||||
|
||||
+27
-14
@@ -85,9 +85,9 @@ func mountFleet(t *testing.T, rt *stubRuntime) *zip.App {
|
||||
if err := visor.Mount(app, deps); err != nil { // Wire order: visor first — the shadowing mount
|
||||
t.Fatalf("visor.Mount: %v", err)
|
||||
}
|
||||
if err := mountRelay(app, deps); err != nil {
|
||||
t.Fatalf("mountRelay: %v", err)
|
||||
}
|
||||
// Mount owns the relay (bots.go mounts it second, so the native control
|
||||
// plane cannot be shadowed) — the harness mounting it AGAIN declared
|
||||
// ALL /v1/bot/* twice, which zip's build refuses outright since v1.26.0.
|
||||
if err := Mount(app, deps); err != nil { // …bots last, as in Wire
|
||||
t.Fatalf("bots.Mount: %v", err)
|
||||
}
|
||||
@@ -106,6 +106,13 @@ func collisions(app *zip.App) []string {
|
||||
var out []string
|
||||
seen := map[string]int{}
|
||||
for _, r := range app.Fiber().GetRoutes() {
|
||||
if r.Path == "/" {
|
||||
// Middleware territory: every app.Use (the composer's bridge,
|
||||
// telemetry) rides fiber's "/" route as a handler CHAIN, by
|
||||
// design. The guard's subject is declared routes, and every one
|
||||
// this fleet declares lives under /v1.
|
||||
continue
|
||||
}
|
||||
key := r.Method + " " + r.Path
|
||||
if n := len(r.Handlers); n > 1 {
|
||||
out = append(out, fmt.Sprintf("%s — %d handlers chained on one route", key, n))
|
||||
@@ -118,21 +125,27 @@ func collisions(app *zip.App) []string {
|
||||
return out
|
||||
}
|
||||
|
||||
// The guard itself must be shown to FIRE on the bug, or it is decoration. This
|
||||
// reproduces the original collision — two subsystems, one pattern — and asserts
|
||||
// collisions() sees it, through the SAME function the real check below uses.
|
||||
// The defense must be shown to FIRE on the bug, or it is decoration. The
|
||||
// defense MOVED: zip itself now refuses a duplicate declaration at build time
|
||||
// (since v1.26.0), panicking with BOTH callsites named — strictly stronger
|
||||
// than this package's after-the-fact scan, which the router's silent merge
|
||||
// forced on it. The test keeps proving the collision cannot ship; what changed
|
||||
// is whose refusal stops it. collisions() stays for the second line
|
||||
// (TestSubsystemsDoNotRegisterDuplicateRoutes) over the composed fleet.
|
||||
func TestDuplicateRouteGuardDetectsACollision(t *testing.T) {
|
||||
defer func() {
|
||||
r := recover()
|
||||
if r == nil {
|
||||
t.Fatal("two GET /v1/bots registrations built without refusal — the duplicate guard is gone from both zip and this package")
|
||||
}
|
||||
if !strings.Contains(fmt.Sprint(r), "GET /v1/bots") {
|
||||
t.Fatalf("the refusal named the wrong route: %v", r)
|
||||
}
|
||||
}()
|
||||
app := zip.New(zip.Config{Logger: luxlog.New("test"), DisableStartupMessage: true})
|
||||
app.Get("/v1/bots", func(c *zip.Ctx) error { return c.JSON(200, map[string]any{"bots": []string{"machine"}}) })
|
||||
app.Get("/v1/bots", func(c *zip.Ctx) error { return c.JSON(200, map[string]any{"bots": []string{"run"}}) })
|
||||
|
||||
got := collisions(app)
|
||||
if len(got) == 0 {
|
||||
t.Fatal("the guard cannot see the very collision it guards: two GET /v1/bots handlers are registered and collisions() reported none")
|
||||
}
|
||||
if !strings.Contains(got[0], "GET /v1/bots") {
|
||||
t.Fatalf("guard named the wrong route: %v", got)
|
||||
}
|
||||
_ = collisions(app) // zip refuses during the build walk above or here; either way the defer sees it
|
||||
}
|
||||
|
||||
// No two subsystems may claim the same method+path. The router does not panic on
|
||||
|
||||
@@ -138,8 +138,11 @@ func TestEveryRouteIsTypedOrNamed(t *testing.T) {
|
||||
len(typed), len(untypedByDesign), got, want)
|
||||
}
|
||||
// The MEASURED partition, so the prose cannot drift from the binary.
|
||||
if len(served) != 3 || len(typed) != 2 {
|
||||
t.Errorf("served = %d (want 3), typed = %d (want 2)", len(served), len(typed))
|
||||
// 10 since zip v1.26.0: the document now carries every method of the
|
||||
// relay's one All() registration — the eight ledger entries above plus
|
||||
// the two typed ops. Before, the wildcard collapsed and served read 3.
|
||||
if len(served) != 10 || len(typed) != 2 {
|
||||
t.Errorf("served = %d (want 10), typed = %d (want 2)", len(served), len(typed))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ require (
|
||||
github.com/zap-proto/go v1.3.0
|
||||
github.com/zap-proto/http v0.3.1
|
||||
github.com/zap-proto/md v0.1.0
|
||||
github.com/zap-proto/zip v1.25.2
|
||||
github.com/zap-proto/zip v1.26.0
|
||||
golang.org/x/term v0.45.0
|
||||
helm.sh/helm/v3 v3.21.3
|
||||
k8s.io/apimachinery v0.36.2
|
||||
|
||||
@@ -1003,8 +1003,6 @@ github.com/hanzoai/cd/gitops-engine v0.7.3 h1:NmPf8v3Ti8ejSI8M96wFmzOchQ2fgxZHhV
|
||||
github.com/hanzoai/cd/gitops-engine v0.7.3/go.mod h1:Jyc94hEnwpmaS4kBFmC4fP3t+VvZ8DZgxPtV4FdmL7M=
|
||||
github.com/hanzoai/cek v0.2.3 h1:wOVav3abWAWiIyqTr9pKK/b4PjUCT1j3mdU5LzKpxSQ=
|
||||
github.com/hanzoai/cek v0.2.3/go.mod h1:T9c9qr9x+0kHsk0J57KElY7l2jgWF08onbxZ3ck5nxM=
|
||||
github.com/hanzoai/commerce v1.50.7 h1:+AWHtiSio6ghUbTwoTQuLlL+cfUWM8DPg+WxUY4sQQU=
|
||||
github.com/hanzoai/commerce v1.50.7/go.mod h1:TtxF3nlzmKav9XCVcNF2h03jFPukzTLemTuG9my38jY=
|
||||
github.com/hanzoai/commerce v1.50.8 h1:6ZBPE6eAZtVMkzKIdG66eHIjTB0G6luuZJfRLJGNkg4=
|
||||
github.com/hanzoai/commerce v1.50.8/go.mod h1:TtxF3nlzmKav9XCVcNF2h03jFPukzTLemTuG9my38jY=
|
||||
github.com/hanzoai/csqlite v0.1.0 h1:suwC3dh0INlfP/U0Es6cDf6JNQ+2+GVLLATPWCUux6k=
|
||||
@@ -2050,8 +2048,8 @@ github.com/zap-proto/md v0.1.0 h1:1R6w/i1FYAdGIIiOvNggKO0RjikzhWWRodQUOgzEEpc=
|
||||
github.com/zap-proto/md v0.1.0/go.mod h1:pmMx2F4Dwj1H48PIuLzRZxB2R5qVvqeg8ZScKQIntyQ=
|
||||
github.com/zap-proto/zap2pb v0.2.0 h1:sos6HnayhGMGLRO54px1InzimDzTZ2o5TSMEatYBjzs=
|
||||
github.com/zap-proto/zap2pb v0.2.0/go.mod h1:wD97Z2VTPabDq/4AMNL++PWnQ0YwEtajiuNkLGg3/18=
|
||||
github.com/zap-proto/zip v1.25.2 h1:zcFsXUjVn8Q31y4LR2m/nAd+9W3lOiNEZx5XQBvUJp8=
|
||||
github.com/zap-proto/zip v1.25.2/go.mod h1:fAy03ztlXUC3EIZaagZcu84VW4S2r+qRIrjhrP31h+I=
|
||||
github.com/zap-proto/zip v1.26.0 h1:nmJx/pIRingk7N851RFHsueDUlRR9/Mh+1j9XyE+3io=
|
||||
github.com/zap-proto/zip v1.26.0/go.mod h1:fAy03ztlXUC3EIZaagZcu84VW4S2r+qRIrjhrP31h+I=
|
||||
github.com/zeebo/assert v1.3.1 h1:vukIABvugfNMZMQO1ABsyQDJDTVQbn+LWSMy1ol1h6A=
|
||||
github.com/zeebo/assert v1.3.1/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
|
||||
github.com/zeebo/blake3 v0.2.4 h1:KYQPkhpRtcqh0ssGYcKLG1JYvddkEA8QwCM/yBqhaZI=
|
||||
|
||||
Reference in New Issue
Block a user