package archguard import ( "os" "path/filepath" "strings" "testing" "golang.org/x/tools/go/analysis/checker" "golang.org/x/tools/go/packages" ) // packageFloor is the smallest number of packages a healthy load returns. Without it, a run that loads // NOTHING — a bad pattern, a rename, a broken module — is indistinguishable from a clean tree and the // guard silently becomes decoration. `go vet` is loud about a package that fails to build, but it is // perfectly quiet about analyzing zero of them, so the floor is asserted rather than assumed. The module // holds ~20 packages (~40 with test variants); the floor catches ZERO, it does not track the count. const packageFloor = 15 // TestInvariantsHoldInTheShippingTree runs the analyzers over the REAL module from an ordinary `go test`. // // The invariants moved out of _test.go files into go/analysis, which made them depend on somebody typing // `make vet` — `go test -vet=` refuses a custom analyzer, so the ordinary battery stopped carrying them. // This puts them back without a second implementation: the same Analyzers() the vet tool wires up, driven // here through the checker API instead of unitchecker. func TestInvariantsHoldInTheShippingTree(t *testing.T) { // Every analyzer, from the same slice cmd/tmvet uses: dropping one from Analyzers() must fail HERE, // which is the property the old walk's file floor used to provide for its own coverage. if got := len(Analyzers()); got != 3 { t.Fatalf("Analyzers() returns %d analyzers, want 3 (stageseam, requestseam, egressseam) — an "+ "invariant that is not in this slice is enforced by nothing", got) } cfg := &packages.Config{Mode: packages.LoadAllSyntax, Tests: true, Dir: "../.."} pkgs, err := packages.Load(cfg, "textmachine/backend/...") if err != nil { t.Fatalf("load the module: %v", err) } if len(pkgs) < packageFloor { t.Fatalf("loaded only %d packages (floor %d) — the pattern matches nothing or the module is "+ "broken, which makes this guard decoration rather than a guard", len(pkgs), packageFloor) } var loadErrs []string packages.Visit(pkgs, nil, func(p *packages.Package) { for _, e := range p.Errors { loadErrs = append(loadErrs, e.Error()) } }) if len(loadErrs) > 0 { // A package that does not type-check is analyzed as nothing at all — silently, unless said here. t.Fatalf("packages failed to load, so they were NOT analyzed:\n %s", strings.Join(loadErrs, "\n ")) } graph, err := checker.Analyze(Analyzers(), pkgs, nil) if err != nil { t.Fatalf("run the analyzers: %v", err) } var found []string for act := range graph.All() { if act.Err != nil { found = append(found, act.Analyzer.Name+" errored on "+act.Package.PkgPath+": "+act.Err.Error()) } for _, d := range act.Diagnostics { found = append(found, act.Package.Fset.Position(d.Pos).String()+": "+d.Message) } } if len(found) > 0 { t.Fatalf("architectural invariants violated in the shipping tree:\n %s", strings.Join(found, "\n ")) } assertGuardVocabularyIsLive(t, pkgs) } // assertGuardVocabularyIsLive fails when the guards name something that no longer exists. // // Both halves of this analyzer set are keyed on STRINGS — the exempt lists name files, and the egress // shape names two types. A rename on either side does not break a build and does not fail a test: the // exemption simply stops matching (a legitimate file starts being reported, which is loud) or, far // worse, the shape half stops matching anything at all and the guard goes quietly decorative. That // second direction is exactly the failure mode this whole package was built to end, so it is asserted // rather than trusted. func assertGuardVocabularyIsLive(t *testing.T, pkgs []*packages.Package) { t.Helper() const modulePrefix = "textmachine/backend/" for _, list := range [][]string{stageSeam.exempt, requestSeam.exempt, egressExempt} { for _, f := range list { rel, ok := strings.CutPrefix(f, modulePrefix) if !ok { t.Errorf("exemption %q is not inside this module — it can never match", f) continue } if _, err := os.Stat(filepath.Join("../..", rel)); err != nil { t.Errorf("exemption %q names a file that does not exist (%v) — a rename left it behind, "+ "and an exemption nobody can see is how a seam re-opens", f, err) } } } var llmPkgFound bool for _, p := range pkgs { if p.PkgPath != llmPkg || p.Types == nil { continue } llmPkgFound = true for _, name := range []string{llmRequestType, llmResponseType} { if p.Types.Scope().Lookup(name) == nil { t.Errorf("the egress shape is keyed on %s.%s, which no longer exists — renaming it "+ "silences the name-independent half of the guard without failing anything else", llmPkg, name) } } } if !llmPkgFound { t.Errorf("package %s was not loaded, so the egress vocabulary could not be checked", llmPkg) } }