返回 DeepSeek-Reasonix
doctor_test.go
根目录 / internal / cli / doctor_test.go
1 package cli
2
3 import (
4 "encoding/json"
5 "io"
6 "os"
7 "path/filepath"
8 "strings"
9 "testing"
10 )
11
12 func TestDoctorCommandPrintsJSON(t *testing.T) {
13 out := captureStdout(t, func() {
14 if rc := doctorCommand([]string{"--json"}, "test-version"); rc != 0 {
15 t.Fatalf("doctorCommand rc = %d, want 0", rc)
16 }
17 })
18
19 var decoded map[string]any
20 if err := json.Unmarshal([]byte(out), &decoded); err != nil {
21 t.Fatalf("doctor --json output is not JSON: %v\n%s", err, out)
22 }
23 if decoded["version"] != "test-version" {
24 t.Fatalf("version = %v, want test-version", decoded["version"])
25 }
26 }
27
28 func TestRunDispatchesDoctor(t *testing.T) {
29 out := captureStdout(t, func() {
30 if rc := Run([]string{"doctor"}, "dispatch-version"); rc != 0 {
31 t.Fatalf("Run doctor rc = %d, want 0", rc)
32 }
33 })
34 if !strings.Contains(out, "reasonix dispatch-version doctor") {
35 t.Fatalf("doctor output missing header:\n%s", out)
36 }
37 }
38
39 func TestDoctorSessionCommandWritesBundle(t *testing.T) {
40 home := t.TempDir()
41 t.Setenv("REASONIX_HOME", home)
42 sessionDir := filepath.Join(home, "sessions")
43 if err := os.MkdirAll(sessionDir, 0o755); err != nil {
44 t.Fatal(err)
45 }
46 if err := os.WriteFile(filepath.Join(sessionDir, "abc.jsonl"), []byte(`{"role":"user","content":"hi"}`+"\n"), 0o644); err != nil {
47 t.Fatal(err)
48 }
49 outPath := filepath.Join(t.TempDir(), "abc-diag.zip")
50 out := captureStdout(t, func() {
51 if rc := doctorCommand([]string{"session", "abc", "--zip", "--out", outPath}, "test-version"); rc != 0 {
52 t.Fatalf("doctor session rc = %d, want 0", rc)
53 }
54 })
55 if strings.TrimSpace(out) != outPath {
56 t.Fatalf("doctor session output = %q, want %q", strings.TrimSpace(out), outPath)
57 }
58 if info, err := os.Stat(outPath); err != nil || info.Size() == 0 {
59 t.Fatalf("bundle stat = %v, %v", info, err)
60 }
61 }
62
63 func TestDoctorQualityCommandPrintsPublicSafeJSON(t *testing.T) {
64 home := t.TempDir()
65 t.Setenv("REASONIX_HOME", home)
66 sessionDir := filepath.Join(home, "sessions")
67 if err := os.MkdirAll(sessionDir, 0o755); err != nil {
68 t.Fatal(err)
69 }
70 path := filepath.Join(sessionDir, "abc.jsonl")
71 const secret = "quality-command-secret"
72 if err := os.WriteFile(path, []byte(`{"role":"user","content":"`+secret+`"}`+"\n"), 0o644); err != nil {
73 t.Fatal(err)
74 }
75 out := captureStdout(t, func() {
76 if rc := doctorCommand([]string{"quality", "abc", "--json"}, "test-version"); rc != 0 {
77 t.Fatalf("doctor quality rc = %d, want 0", rc)
78 }
79 })
80 var decoded map[string]any
81 if err := json.Unmarshal([]byte(out), &decoded); err != nil {
82 t.Fatalf("doctor quality output is not JSON: %v\n%s", err, out)
83 }
84 if decoded["version"] != "test-version" {
85 t.Fatalf("version = %v", decoded["version"])
86 }
87 if strings.Contains(out, secret) || strings.Contains(out, home) || strings.Contains(out, "abc.jsonl") {
88 t.Fatalf("doctor quality leaked session data:\n%s", out)
89 }
90 }
91
92 func captureStdout(t *testing.T, fn func()) string {
93 t.Helper()
94 old := os.Stdout
95 r, w, err := os.Pipe()
96 if err != nil {
97 t.Fatal(err)
98 }
99 os.Stdout = w
100 defer func() { os.Stdout = old }()
101
102 fn()
103 if err := w.Close(); err != nil {
104 t.Fatal(err)
105 }
106 data, err := io.ReadAll(r)
107 if err != nil {
108 t.Fatal(err)
109 }
110 return string(data)
111 }
112
113 func TestDoctorSessionCommandOutEqualsForm(t *testing.T) {
114 home := t.TempDir()
115 t.Setenv("REASONIX_HOME", home)
116 sessionDir := filepath.Join(home, "sessions")
117 if err := os.MkdirAll(sessionDir, 0o755); err != nil {
118 t.Fatal(err)
119 }
120 if err := os.WriteFile(filepath.Join(sessionDir, "abc.jsonl"), []byte(`{"role":"user","content":"hi"}`+"\n"), 0o644); err != nil {
121 t.Fatal(err)
122 }
123 outPath := filepath.Join(t.TempDir(), "abc-diag.zip")
124 out := captureStdout(t, func() {
125 if rc := doctorCommand([]string{"session", "abc", "--out=" + outPath}, "test-version"); rc != 0 {
126 t.Fatalf("doctor session rc = %d, want 0", rc)
127 }
128 })
129 if strings.TrimSpace(out) != outPath {
130 t.Fatalf("doctor session output = %q, want %q", strings.TrimSpace(out), outPath)
131 }
132 if rc := doctorCommand([]string{"session", "abc", "--out="}, "test-version"); rc != 2 {
133 t.Fatalf("empty --out= rc = %d, want usage error 2", rc)
134 }
135 }
136
137 func TestDoctorRedactSessionsCommand(t *testing.T) {
138 dir := t.TempDir()
139 const secret = "sensitive-value-that-must-not-leak"
140 path := filepath.Join(dir, "abc.jsonl")
141 if err := os.WriteFile(path, []byte(`{"role":"tool","content":"DEEPSEEK_API_KEY=`+secret+`"}`+"\n"), 0o644); err != nil {
142 t.Fatal(err)
143 }
144
145 out := captureStdout(t, func() {
146 if rc := doctorCommand([]string{"redact-sessions", "--dir", dir}, "test-version"); rc != 0 {
147 t.Fatalf("doctor redact-sessions rc = %d, want 0", rc)
148 }
149 })
150 if !strings.Contains(out, "session secret cleanup redacted 1/1 files") {
151 t.Fatalf("unexpected output:\n%s", out)
152 }
153 data, err := os.ReadFile(path)
154 if err != nil {
155 t.Fatal(err)
156 }
157 if strings.Contains(string(data), secret) {
158 t.Fatalf("doctor redact-sessions leaked secret:\n%s", data)
159 }
160 }
161
161 lines GO