| 1 | package main |
| 2 | |
| 3 | import ( |
| 4 | "go/ast" |
| 5 | "go/parser" |
| 6 | "go/token" |
| 7 | "strconv" |
| 8 | "testing" |
| 9 | ) |
| 10 | |
| 11 | // Astra plan 6 R1 #9: every member-facing string `carlos apps set` adds is a |
| 12 | // named constant, so the approved copy record can pin it. In apps_swap.go, the |
| 13 | // help text of a flag and the text of an error it prints must be a constant, |
| 14 | // never a bare literal. The only literals allowed there are the help |
| 15 | // strings shared, word for word, with the other verbs' --account, |
| 16 | // --account-id and --console flags: existing copy, not new. |
| 17 | func TestAppsSetCopyIsNamedConstants(t *testing.T) { |
| 18 | shared := map[string]bool{ |
| 19 | "account (sqid or name) the app belongs to (default: your only account, or the configured/chosen one)": true, |
| 20 | "account sqid, matched as a sqid ONLY — supersedes --account": true, |
| 21 | "console to act through (default: the only logged-in one)": true, |
| 22 | } |
| 23 | fset := token.NewFileSet() |
| 24 | f, err := parser.ParseFile(fset, "apps_swap.go", nil, parser.ParseComments) |
| 25 | if err != nil { |
| 26 | t.Fatal(err) |
| 27 | } |
| 28 | // copyArg is which argument of each call is member-facing text. |
| 29 | copyArg := map[string]int{"fs.String": 2, "fs.Int": 2, "appFlag": 1, "consoleOption": 1, |
| 30 | "fmt.Errorf": 0, "errors.New": 0, "log.Fatal": 0, "log.Fatalf": 0} |
| 31 | checked := 0 |
| 32 | ast.Inspect(f, func(n ast.Node) bool { |
| 33 | call, ok := n.(*ast.CallExpr) |
| 34 | if !ok { |
| 35 | return true |
| 36 | } |
| 37 | name := "" |
| 38 | switch fn := call.Fun.(type) { |
| 39 | case *ast.Ident: |
| 40 | name = fn.Name |
| 41 | case *ast.SelectorExpr: |
| 42 | if x, ok := fn.X.(*ast.Ident); ok { |
| 43 | name = x.Name + "." + fn.Sel.Name |
| 44 | } |
| 45 | } |
| 46 | i, ok := copyArg[name] |
| 47 | if !ok || i >= len(call.Args) { |
| 48 | return true |
| 49 | } |
| 50 | checked++ |
| 51 | pos := fset.Position(call.Pos()) |
| 52 | if a, ok := call.Args[i].(*ast.BasicLit); ok { |
| 53 | if s, _ := strconv.Unquote(a.Value); !shared[s] { |
| 54 | t.Errorf("%s: %s(%s): new copy as a bare literal; make it a named constant", pos, name, a.Value) |
| 55 | } |
| 56 | } |
| 57 | return true |
| 58 | }) |
| 59 | if checked < 10 { |
| 60 | t.Fatalf("only %d copy sites checked in apps_swap.go: the scan is broken", checked) |
| 61 | } |
| 62 | } |
| 63 | |