返回 DeepSeek-Reasonix
maintenance_restart_test.go
根目录 / internal / session / maintenance_restart_test.go
1 package session
2
3 import (
4 "context"
5 "errors"
6 "os"
7 "os/exec"
8 "path/filepath"
9 "testing"
10 "time"
11 )
12
13 func TestPurgeFilesystemCrashRestart(t *testing.T) {
14 for _, phase := range []string{"before-tombstone", "after-tombstone", "before-rename", "after-rename", "after-content-removal", "after-cleanup"} {
15 t.Run(phase, func(t *testing.T) {
16 root := t.TempDir()
17 for _, dir := range []string{"victim", ".query-cache/victim"} {
18 if err := os.MkdirAll(filepath.Join(root, dir), 0700); err != nil {
19 t.Fatal(err)
20 }
21 if err := os.WriteFile(filepath.Join(root, dir, "body"), []byte("retained history"), 0600); err != nil {
22 t.Fatal(err)
23 }
24 }
25 run := func(point string, wantCrash bool) {
26 ctx, cancel := context.WithTimeout(t.Context(), 30*time.Second)
27 defer cancel()
28 cmd := exec.CommandContext(ctx, os.Args[0], "-test.run=^TestPurgeFilesystemCrashHelper$")
29 cmd.Env = append(os.Environ(), "REASONIX_PURGE_TEST_ROOT="+root, "REASONIX_PURGE_TEST_POINT="+point)
30 output, err := cmd.CombinedOutput()
31 var exit *exec.ExitError
32 if wantCrash {
33 if !errors.As(err, &exit) || exit.ExitCode() != 23 {
34 t.Fatalf("crash not reached: %v %s", err, output)
35 }
36 } else if err != nil {
37 t.Fatalf("restart: %v %s", err, output)
38 }
39 }
40 run(phase, true)
41 _, markerErr := os.Stat(filepath.Join(root, "tombstone"))
42 if phase == "before-tombstone" {
43 if !os.IsNotExist(markerErr) {
44 t.Fatalf("premature tombstone: %v", markerErr)
45 }
46 if body, err := os.ReadFile(filepath.Join(root, "victim", "body")); err != nil || string(body) != "retained history" {
47 t.Fatalf("lost precommit body: %v", err)
48 }
49 } else if markerErr != nil {
50 t.Fatalf("lost tombstone: %v", markerErr)
51 }
52 run("", false)
53 run("", false)
54 for _, path := range []string{"victim", ".purging/victim", ".purging/victim.receipt", ".query-cache/victim"} {
55 if _, err := os.Lstat(filepath.Join(root, path)); !os.IsNotExist(err) {
56 t.Fatalf("cleanup left %s: %v", path, err)
57 }
58 }
59 })
60 }
61 }
62
63 func TestPurgeFilesystemCrashHelper(t *testing.T) {
64 root := os.Getenv("REASONIX_PURGE_TEST_ROOT")
65 if root == "" {
66 return
67 }
68 point := os.Getenv("REASONIX_PURGE_TEST_POINT")
69 err := NewFilesystemPersistence(root).purgeWithTombstone(t.Context(), "victim", func() error {
70 return os.WriteFile(filepath.Join(root, "tombstone"), []byte("deleted"), 0600)
71 }, func(phase string) {
72 if phase == point {
73 os.Exit(23)
74 }
75 })
76 if err != nil {
77 t.Fatal(err)
78 }
79 }
80
80 lines GO