1package cli
2
3import (
4 "go/ast"
5 "go/parser"
6 "go/token"
7 "os"
8 "strings"
9 "testing"
10)
11
12type uiAdoptionExpectation struct {
13 family string
14 file string
15 functions []string
16}
17
18var uiAdoptionExpectations = []uiAdoptionExpectation{
19 {
20 family: "root/server",
21 file: "root.go",
22 functions: []string{"newVersionCmd", "runReloadRemote", "runLogsRemote", "streamLogsRemote", "showContainerLogsLocal"},
23 },
24 {
25 family: "backups",
26 file: "backup.go",
27 functions: []string{"newBackupListCmd", "newBackupRunCmd", "newBackupDetectCmd", "newBackupStatusCmd"},
28 },
29 {
30 family: "images",
31 file: "images.go",
32 functions: []string{"runImagesList", "runImagesPrune"},
33 },
34}
35
36func TestLocalParityMatrix(t *testing.T) {
37 checks := []struct {
38 file string
39 legacyText string
40 command string
41 }{
42 {file: "push.go", legacyText: "push requires remote mode", command: "gordon push"},
43 {file: "rollback.go", legacyText: "rollback requires remote mode", command: "gordon rollback"},
44 {file: "backup.go", legacyText: "backup commands require a configured remote target", command: "gordon backups"},
45 {file: "routes.go", legacyText: "status command requires --remote flag or GORDON_REMOTE env var", command: "gordon status"},
46 {file: "restart.go", legacyText: "local restart does not support --with-attachments; use --remote", command: "gordon restart --with-attachments"},
47 }
48
49 for _, check := range checks {
50 content, err := os.ReadFile(check.file)
51 if err != nil {
52 t.Fatalf("failed to read %s: %v", check.file, err)
53 }
54 if strings.Contains(string(content), check.legacyText) {
55 t.Fatalf("local parity gap: %s still blocks local mode (%q found in %s)", check.command, check.legacyText, check.file)
56 }
57 }
58}
59
60func TestUIAdoptionMatrixCoverage(t *testing.T) {
61 for _, expect := range uiAdoptionExpectations {
62 expect := expect
63 t.Run(expect.family, func(t *testing.T) {
64 if len(expect.functions) == 0 {
65 t.Fatalf("ui adoption matrix for %s is empty", expect.family)
66 }
67
68 fset := token.NewFileSet()
69 fileNode, err := parser.ParseFile(fset, expect.file, nil, parser.AllErrors)
70 if err != nil {
71 t.Fatalf("failed to parse %s: %v", expect.file, err)
72 }
73
74 found := make(map[string]bool, len(expect.functions))
75 ast.Inspect(fileNode, func(n ast.Node) bool {
76 fn, ok := n.(*ast.FuncDecl)
77 if !ok {
78 return true
79 }
80 found[fn.Name.Name] = true
81 return true
82 })
83
84 for _, fn := range expect.functions {
85 if !found[fn] {
86 t.Fatalf("ui adoption matrix references missing function %s in %s", fn, expect.file)
87 }
88 }
89 })
90 }
91}