返回 DeepSeek-Reasonix
unknown_operation_test.go
根目录 / internal / cli / unknown_operation_test.go
1 package cli
2
3 import (
4 "bytes"
5 "encoding/json"
6 "io"
7 "os"
8 "strings"
9 "testing"
10
11 "reasonix/internal/taskmonitor"
12 )
13
14 func captureErr(t *testing.T, fn func() int) (int, string) {
15 t.Helper()
16 old := os.Stderr
17 r, w, err := os.Pipe()
18 if err != nil {
19 t.Fatal(err)
20 }
21 os.Stderr = w
22 defer func() { os.Stderr = old }()
23
24 ec := fn()
25 if err := w.Close(); err != nil {
26 t.Fatal(err)
27 }
28 data, err := io.ReadAll(r)
29 if err != nil {
30 t.Fatal(err)
31 }
32 if err := r.Close(); err != nil {
33 t.Fatal(err)
34 }
35 return ec, string(data)
36 }
37
38 func TestUnknownOperationNamesTheValidOnes(t *testing.T) {
39 for _, tc := range []struct {
40 name string
41 run func(args []string, out *bytes.Buffer) int
42 want []string
43 }{
44 {
45 name: "hook",
46 run: func(a []string, o *bytes.Buffer) int { return runHookCommand(a, o) },
47 want: []string{"list", "status"},
48 },
49 {
50 name: "session",
51 run: func(a []string, o *bytes.Buffer) int { return runSessionCommand(a, o) },
52 want: []string{"list", "show", "status", "recovery"},
53 },
54 {
55 name: "task",
56 run: func(a []string, o *bytes.Buffer) int { return runTaskCommand(a, o) },
57 want: []string{"list", "show"},
58 },
59 } {
60 t.Run(tc.name, func(t *testing.T) {
61 var out bytes.Buffer
62 if rc := tc.run([]string{"bogus", "--json"}, &out); rc != 2 {
63 t.Fatalf("exit code = %d, want 2", rc)
64 }
65 var payload struct {
66 Error struct {
67 Code string `json:"code"`
68 Message string `json:"message"`
69 } `json:"error"`
70 }
71 if err := json.Unmarshal(out.Bytes(), &payload); err != nil {
72 t.Fatalf("machine output is not JSON: %v (%s)", err, out.String())
73 }
74 if payload.Error.Code != "unknown_command" {
75 t.Fatalf("code = %q, want unknown_command", payload.Error.Code)
76 }
77 if !strings.Contains(payload.Error.Message, "bogus") {
78 t.Fatalf("message does not echo the rejected operation: %s", payload.Error.Message)
79 }
80 for _, op := range tc.want {
81 if !strings.Contains(payload.Error.Message, op) {
82 t.Fatalf("message omits valid operation %q: %s", op, payload.Error.Message)
83 }
84 }
85 })
86 }
87 }
88
89 func TestTaskUnknownSubcommandsNameValidOnes(t *testing.T) {
90 store := taskmonitor.NewInMemoryStore()
91 originalStore := taskStore
92 taskStore = store
93 t.Cleanup(func() { taskStore = originalStore })
94
95 for _, tc := range []struct {
96 name string
97 run func() int
98 wantUsage string
99 }{
100 {
101 name: "task",
102 run: func() int { return taskCommand([]string{"bogus"}) },
103 wantUsage: "usage: reasonix task <list|show|monitor|status|events|stop|cancel|requeue|open-session|tmux> [flags]",
104 },
105 {
106 name: "task monitor",
107 run: func() int { return taskMonitorCommand(store, []string{"bogus"}) },
108 wantUsage: "usage: reasonix task monitor <list|status|events|stop|cancel|requeue|open-session> [flags]",
109 },
110 {
111 name: "task tmux",
112 run: func() int { return taskTmuxCmd(store, []string{"bogus"}) },
113 wantUsage: "usage: reasonix task tmux <attach|status|open|detach>",
114 },
115 } {
116 t.Run(tc.name, func(t *testing.T) {
117 exit, stderr := captureErr(t, tc.run)
118 if exit != 2 {
119 t.Fatalf("exit = %d, want 2", exit)
120 }
121 if !strings.Contains(stderr, "bogus") {
122 t.Fatalf("stderr does not echo the rejected subcommand: %s", stderr)
123 }
124 if !strings.Contains(stderr, tc.wantUsage) {
125 t.Fatalf("stderr does not include usage %q: %s", tc.wantUsage, stderr)
126 }
127 })
128 }
129 }
130
130 lines GO