返回 DeepSeek-Reasonix
report_test.go
根目录 / internal / cli / report_test.go
1 package cli
2
3 import (
4 "bytes"
5 "context"
6 "errors"
7 "strings"
8 "testing"
9
10 "reasonix/internal/crashreport"
11 "reasonix/internal/i18n"
12 "reasonix/internal/netclient"
13 )
14
15 func isolateCLIReports(t *testing.T) string {
16 t.Helper()
17 i18n.DetectLanguage("en")
18 t.Cleanup(func() { i18n.DetectLanguage("en") })
19 home := t.TempDir()
20 t.Setenv("REASONIX_HOME", home)
21 t.Setenv("REASONIX_SAFE_MODE", "")
22 t.Chdir(t.TempDir())
23 return home
24 }
25
26 func captureCLIReport(t *testing.T, home string) crashreport.Pending {
27 t.Helper()
28 if err := crashreport.CapturePanic(home, "v1.20.0", "private panic value", []byte("goroutine 1 [running]:\nreasonix.run()\n\t/Users/alice/reasonix/main.go:12")); err != nil {
29 t.Fatal(err)
30 }
31 pending, err := crashreport.Load(home, "")
32 if err != nil {
33 t.Fatal(err)
34 }
35 return pending
36 }
37
38 func TestReportCommandPreviewDoesNotSendNonInteractive(t *testing.T) {
39 home := isolateCLIReports(t)
40 pending := captureCLIReport(t, home)
41 previous := sendCLIReport
42 t.Cleanup(func() { sendCLIReport = previous })
43 sendCLIReport = func(context.Context, crashreport.Report, netclient.ProxySpec) error {
44 t.Fatal("non-interactive preview attempted upload")
45 return nil
46 }
47
48 var out, errOut bytes.Buffer
49 if rc := reportCommandWithIO(nil, false, strings.NewReader("y\n"), &out, &errOut); rc != 0 {
50 t.Fatalf("report rc=%d stderr=%q", rc, errOut.String())
51 }
52 if !strings.Contains(out.String(), pending.ID) || !strings.Contains(out.String(), "Preview only") || !strings.Contains(out.String(), "<path>/main.go:12") {
53 t.Fatalf("preview output:\n%s", out.String())
54 }
55 if _, err := crashreport.Load(home, pending.ID); err != nil {
56 t.Fatalf("preview removed report: %v", err)
57 }
58 }
59
60 func TestReportCommandInteractiveConfirmationSendsAndDeletes(t *testing.T) {
61 home := isolateCLIReports(t)
62 pending := captureCLIReport(t, home)
63 previous := sendCLIReport
64 t.Cleanup(func() { sendCLIReport = previous })
65 calls := 0
66 sendCLIReport = func(_ context.Context, report crashreport.Report, _ netclient.ProxySpec) error {
67 calls++
68 if report.Source != "cli.go" {
69 t.Fatalf("report=%+v", report)
70 }
71 return nil
72 }
73
74 var out, errOut bytes.Buffer
75 if rc := reportCommandWithIO(nil, true, strings.NewReader("y\n"), &out, &errOut); rc != 0 {
76 t.Fatalf("report rc=%d stderr=%q", rc, errOut.String())
77 }
78 if calls != 1 || !strings.Contains(out.String(), "[y/N]") || !strings.Contains(out.String(), "Sent CLI crash report") {
79 t.Fatalf("calls=%d output=%q", calls, out.String())
80 }
81 if _, err := crashreport.Load(home, pending.ID); !errors.Is(err, crashreport.ErrNoReports) {
82 t.Fatalf("sent report remains: %v", err)
83 }
84 }
85
86 func TestReportCommandFailedSendKeepsReport(t *testing.T) {
87 home := isolateCLIReports(t)
88 pending := captureCLIReport(t, home)
89 previous := sendCLIReport
90 t.Cleanup(func() { sendCLIReport = previous })
91 sendCLIReport = func(context.Context, crashreport.Report, netclient.ProxySpec) error {
92 return errors.New("offline")
93 }
94
95 var out, errOut bytes.Buffer
96 if rc := reportCommandWithIO([]string{"send", pending.ID}, false, strings.NewReader(""), &out, &errOut); rc != 1 {
97 t.Fatalf("send rc=%d", rc)
98 }
99 if !strings.Contains(errOut.String(), "kept") {
100 t.Fatalf("stderr=%q", errOut.String())
101 }
102 if _, err := crashreport.Load(home, pending.ID); err != nil {
103 t.Fatalf("failed send removed report: %v", err)
104 }
105 }
106
107 func TestLegacySafeModeEnvDoesNotBlockReportSendOrDelete(t *testing.T) {
108 home := isolateCLIReports(t)
109 pending := captureCLIReport(t, home)
110 t.Setenv("REASONIX_SAFE_MODE", "1")
111 previous := sendCLIReport
112 t.Cleanup(func() { sendCLIReport = previous })
113 calls := 0
114 sendCLIReport = func(context.Context, crashreport.Report, netclient.ProxySpec) error {
115 calls++
116 return nil
117 }
118
119 var out, errOut bytes.Buffer
120 if rc := reportCommandWithIO([]string{"send", pending.ID}, false, strings.NewReader(""), &out, &errOut); rc != 0 {
121 t.Fatalf("legacy-env send rc=%d stderr=%q", rc, errOut.String())
122 }
123 if calls != 1 {
124 t.Fatalf("report upload calls=%d, want 1", calls)
125 }
126 pending = captureCLIReport(t, home)
127 out.Reset()
128 errOut.Reset()
129 if rc := reportCommandWithIO([]string{"delete", pending.ID}, false, strings.NewReader(""), &out, &errOut); rc != 0 {
130 t.Fatalf("legacy-env delete rc=%d stderr=%q", rc, errOut.String())
131 }
132 }
133
134 func TestReportCommandListShowAndNoReports(t *testing.T) {
135 home := isolateCLIReports(t)
136 pending := captureCLIReport(t, home)
137 var out, errOut bytes.Buffer
138 if rc := reportCommandWithIO([]string{"list"}, false, strings.NewReader(""), &out, &errOut); rc != 0 || !strings.Contains(out.String(), pending.ID) {
139 t.Fatalf("list rc=%d out=%q err=%q", rc, out.String(), errOut.String())
140 }
141 out.Reset()
142 if rc := reportCommandWithIO([]string{"show", pending.ID}, false, strings.NewReader(""), &out, &errOut); rc != 0 || !strings.Contains(out.String(), `"source": "cli.go"`) {
143 t.Fatalf("show rc=%d out=%q err=%q", rc, out.String(), errOut.String())
144 }
145 if err := crashreport.Remove(home, pending.ID); err != nil {
146 t.Fatal(err)
147 }
148 out.Reset()
149 if rc := reportCommandWithIO(nil, false, strings.NewReader(""), &out, &errOut); rc != 0 || !strings.Contains(out.String(), "No pending") {
150 t.Fatalf("empty rc=%d out=%q err=%q", rc, out.String(), errOut.String())
151 }
152 }
153
153 lines GO