返回 DeepSeek-Reasonix
main_test.go
根目录 / cmd / reasonix / main_test.go
1 package main
2
3 import (
4 "os"
5 "os/exec"
6 "strings"
7 "testing"
8
9 "reasonix/internal/config"
10 "reasonix/internal/crashreport"
11 )
12
13 func TestRunWithCrashCaptureRecordsAndReraises(t *testing.T) {
14 home := t.TempDir()
15 t.Setenv("REASONIX_HOME", home)
16 previous := runCLI
17 t.Cleanup(func() { runCLI = previous })
18 secret := "private prompt from panic"
19 runCLI = func([]string, string) int { panic(secret) }
20
21 func() {
22 defer func() {
23 if recovered := recover(); recovered != secret {
24 t.Fatalf("reraised panic = %#v", recovered)
25 }
26 }()
27 runWithCrashCapture([]string{"run"}, "v1.20.0")
28 }()
29
30 reports, err := crashreport.List(config.ReasonixHomeDir())
31 if err != nil || len(reports) != 1 {
32 t.Fatalf("captured reports=%d err=%v", len(reports), err)
33 }
34 // Token-like function and file basenames may be redacted by the current
35 // privacy filter. Keep asserting that the top frame is the test call site
36 // without requiring its pre-redaction basename.
37 if got := reports[0].Report.TopFrame; !strings.Contains(got, "_test.go:") {
38 t.Fatalf("top frame = %q, want sanitized panic call site in a test file", got)
39 }
40 preview, err := crashreport.Preview(reports[0].Report)
41 if err != nil {
42 t.Fatal(err)
43 }
44 if strings.Contains(string(preview), secret) {
45 t.Fatalf("panic value leaked into report: %s", preview)
46 }
47 }
48
49 func TestRunWithCrashCapturePassesThroughExitCode(t *testing.T) {
50 previous := runCLI
51 t.Cleanup(func() { runCLI = previous })
52 runCLI = func(args []string, version string) int {
53 if len(args) != 1 || args[0] != "version" || version != "v1.20.0" {
54 t.Fatalf("runCLI args=%v version=%q", args, version)
55 }
56 return 17
57 }
58 if got := runWithCrashCapture([]string{"version"}, "v1.20.0"); got != 17 {
59 t.Fatalf("exit code=%d", got)
60 }
61 }
62
63 const fatalChildEnv = "REASONIX_TEST_FATAL_CHILD"
64
65 func TestGoroutinePanicIsReportedOnNextStart(t *testing.T) {
66 if os.Getenv(fatalChildEnv) == "1" {
67 runCLI = func([]string, string) int {
68 go func() { panic("private prompt from background goroutine") }()
69 select {}
70 }
71 runWithCrashCapture([]string{"chat"}, "v1.20.0")
72 return
73 }
74
75 home := t.TempDir()
76 child := exec.Command(os.Args[0], "-test.run=^TestGoroutinePanicIsReportedOnNextStart$")
77 child.Env = append(os.Environ(), fatalChildEnv+"=1", "REASONIX_HOME="+home)
78 output, err := child.CombinedOutput()
79 if err == nil {
80 t.Fatalf("child exited cleanly; output:\n%s", output)
81 }
82 if !strings.Contains(string(output), "panic:") {
83 t.Fatalf("child did not die from the goroutine panic:\n%s", output)
84 }
85
86 t.Setenv("REASONIX_HOME", home)
87 previous := runCLI
88 t.Cleanup(func() { runCLI = previous })
89 runCLI = func([]string, string) int { return 0 }
90 runWithCrashCapture([]string{"version"}, "v1.20.0")
91
92 reports, err := crashreport.List(home)
93 if err != nil || len(reports) != 1 {
94 t.Fatalf("pending reports after restart = %d (err %v), want the goroutine panic", len(reports), err)
95 }
96 report := reports[0].Report
97 if !strings.Contains(report.Stack, "goroutine ") || !strings.Contains(report.Stack, "TestGoroutinePanicIsReportedOnNextStart") {
98 t.Fatalf("stack = %q, want the panicking goroutine", report.Stack)
99 }
100 preview, err := crashreport.Preview(report)
101 if err != nil {
102 t.Fatal(err)
103 }
104 if strings.Contains(string(preview), "private prompt") {
105 t.Fatalf("panic value leaked into report: %s", preview)
106 }
107 }
108
108 lines GO