moves: the serve() wiring test checks the config identity and call order
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 file changed,
+53
−21
cmd/console/movesswitch_test.go+53 −21
diff --git a/cmd/console/movesswitch_test.go b/cmd/console/movesswitch_test.go| index 3240f12fd..5fc3f903d 100644 |
| --- a/cmd/console/movesswitch_test.go |
| +++ b/cmd/console/movesswitch_test.go |
| @@ -33,36 +33,68 @@ func TestApplyMovesSwitchSetsTheConfigField(t *testing.T) { |
| } |
| // serve() is not callable from a test (it opens a database, a bucket and a |
| -// listener), so this parses main.go and checks that it applies the switch to |
| -// the config it hands console.NewServer, reading the real environment. |
| +// listener), so this parses main.go and checks three things: serve() calls |
| +// applyMovesSwitch(&X, os.Getenv); the same X is the config handed to |
| +// console.NewServer; and the switch call comes before the NewServer call. |
| func TestServeAppliesTheMovesSwitchFromTheEnvironment(t *testing.T) { |
| f, err := parser.ParseFile(token.NewFileSet(), "main.go", nil, 0) |
| if err != nil { |
| t.Fatal(err) |
| } |
| - found := false |
| + var serve *ast.FuncDecl |
| for _, d := range f.Decls { |
| - fn, ok := d.(*ast.FuncDecl) |
| - if !ok || fn.Name.Name != "serve" { |
| - continue |
| + if fn, ok := d.(*ast.FuncDecl); ok && fn.Name.Name == "serve" { |
| + serve = fn |
| } |
| - ast.Inspect(fn.Body, func(n ast.Node) bool { |
| - call, ok := n.(*ast.CallExpr) |
| - if !ok { |
| - return true |
| - } |
| - if id, ok := call.Fun.(*ast.Ident); !ok || id.Name != "applyMovesSwitch" || len(call.Args) != 2 { |
| - return true |
| + } |
| + if serve == nil { |
| + t.Fatal("no serve() in main.go") |
| + } |
| + var applies, news []*ast.CallExpr |
| + ast.Inspect(serve.Body, func(n ast.Node) bool { |
| + call, ok := n.(*ast.CallExpr) |
| + if !ok { |
| + return true |
| + } |
| + switch fun := call.Fun.(type) { |
| + case *ast.Ident: |
| + if fun.Name == "applyMovesSwitch" { |
| + applies = append(applies, call) |
| } |
| - if sel, ok := call.Args[1].(*ast.SelectorExpr); ok { |
| - if pkg, ok := sel.X.(*ast.Ident); ok && pkg.Name == "os" && sel.Sel.Name == "Getenv" { |
| - found = true |
| - } |
| + case *ast.SelectorExpr: |
| + if pkg, ok := fun.X.(*ast.Ident); ok && pkg.Name == "console" && fun.Sel.Name == "NewServer" { |
| + news = append(news, call) |
| } |
| - return true |
| - }) |
| + } |
| + return true |
| + }) |
| + if len(applies) != 1 || len(news) != 1 { |
| + t.Fatalf("serve() has %d applyMovesSwitch calls and %d console.NewServer calls, want one of each", len(applies), len(news)) |
| } |
| - if !found { |
| - t.Fatal("serve() does not call applyMovesSwitch(&cfg, os.Getenv); the switch would never reach the console") |
| + apply, ns := applies[0], news[0] |
| + if len(apply.Args) != 2 || len(ns.Args) < 1 { |
| + t.Fatal("applyMovesSwitch wants (&cfg, os.Getenv) and console.NewServer wants (cfg, store)") |
| } |
| + if sel, ok := apply.Args[1].(*ast.SelectorExpr); !ok || !isIdent(sel.X, "os") || sel.Sel.Name != "Getenv" { |
| + t.Fatal("applyMovesSwitch is not given os.Getenv; the switch would not read the real environment") |
| + } |
| + addr, ok := apply.Args[0].(*ast.UnaryExpr) |
| + if !ok || addr.Op != token.AND { |
| + t.Fatal("applyMovesSwitch's first argument is not &X") |
| + } |
| + x, ok := addr.X.(*ast.Ident) |
| + if !ok { |
| + t.Fatal("applyMovesSwitch's first argument is not the address of a plain variable") |
| + } |
| + if !isIdent(ns.Args[0], x.Name) { |
| + t.Fatalf("console.NewServer is not handed %s, the config applyMovesSwitch changed", x.Name) |
| + } |
| + if apply.Pos() >= ns.Pos() { |
| + t.Fatal("applyMovesSwitch runs after console.NewServer; the server would be built with the switch off") |
| + } |
| +} |
| + |
| +func isIdent(e ast.Expr, name string) bool { |
| + id, ok := e.(*ast.Ident) |
| + return ok && id.Name == name |
| } |