返回 DeepSeek-Reasonix
save_recovery_projection_test.go
根目录 / internal / agent / save_recovery_projection_test.go
1 package agent
2
3 import (
4 "os"
5 "path/filepath"
6 "testing"
7
8 "reasonix/internal/provider"
9 "reasonix/internal/store"
10 )
11
12 func TestSaveRecoveryBranchInheritsValidProjection(t *testing.T) {
13 dir := t.TempDir()
14 path := filepath.Join(dir, "session.jsonl")
15 parent := NewSession("sys")
16 parent.Add(provider.Message{Role: provider.RoleUser, Content: "first"})
17 parent.Add(provider.Message{Role: provider.RoleAssistant, Content: "one"})
18 parent.Add(provider.Message{Role: provider.RoleUser, Content: "second"})
19 if err := parent.Save(path); err != nil {
20 t.Fatalf("Save parent: %v", err)
21 }
22 parentMsgs, parentVersion := parent.snapshotMessagesVersion()
23 st := CompactionState{
24 SchemaVersion: compactionStateSchemaCurrent,
25 TranscriptVersion: parentVersion,
26 PromptCacheKey: promptCacheKey("ws", BranchID(path), "test/model"),
27 Projection: ContextProjection{
28 ProjectionVersion: 1,
29 CoveredCount: 2,
30 CoveredPrefixHash: coveredPrefixHash(parentMsgs, 2),
31 Messages: []provider.Message{
32 {Role: provider.RoleUser, Content: "summary"},
33 {Role: provider.RoleAssistant, Content: "one"},
34 },
35 },
36 }
37 if err := SaveCompactionState(path, st); err != nil {
38 t.Fatalf("SaveCompactionState: %v", err)
39 }
40 stale := NewSession("sys")
41 stale.Add(provider.Message{Role: provider.RoleUser, Content: "first"})
42 stale.Add(provider.Message{Role: provider.RoleAssistant, Content: "one"})
43 stale.Add(provider.Message{Role: provider.RoleUser, Content: "second"})
44 stale.Add(provider.Message{Role: provider.RoleUser, Content: "local only"})
45 info, err := stale.SaveRecoveryBranch(RecoveryBranchOptions{OriginalPath: path})
46 if err != nil {
47 t.Fatalf("SaveRecoveryBranch: %v", err)
48 }
49 got, ok, err := LoadCompactionState(info.Path)
50 if err != nil || !ok {
51 t.Fatalf("LoadCompactionState recovery ok=%v err=%v, want inherited sidecar", ok, err)
52 }
53 if got.Projection.CoveredCount != 2 || got.Projection.CoveredPrefixHash != st.Projection.CoveredPrefixHash {
54 t.Fatalf("recovery projection = %+v, want parent projection inherited", got.Projection)
55 }
56 recovered, err := LoadSession(info.Path)
57 if err != nil {
58 t.Fatalf("LoadSession recovery: %v", err)
59 }
60 recoveredMsgs, _ := recovered.snapshotMessagesVersion()
61 if n := got.Projection.CoveredCount; n <= 0 || n > len(recoveredMsgs) ||
62 coveredPrefixHash(recoveredMsgs, n) != got.Projection.CoveredPrefixHash {
63 t.Fatal("inherited projection does not match the recovery transcript")
64 }
65 }
66
67 func TestSaveRecoveryBranchSkipsInvalidProjection(t *testing.T) {
68 dir := t.TempDir()
69 path := filepath.Join(dir, "session.jsonl")
70 parent := NewSession("sys")
71 parent.Add(provider.Message{Role: provider.RoleUser, Content: "first"})
72 parent.Add(provider.Message{Role: provider.RoleAssistant, Content: "one"})
73 parent.Add(provider.Message{Role: provider.RoleUser, Content: "second"})
74 if err := parent.Save(path); err != nil {
75 t.Fatalf("Save parent: %v", err)
76 }
77 parentMsgs, parentVersion := parent.snapshotMessagesVersion()
78 st := CompactionState{
79 SchemaVersion: compactionStateSchemaCurrent,
80 TranscriptVersion: parentVersion,
81 PromptCacheKey: promptCacheKey("ws", BranchID(path), "test/model"),
82 Projection: ContextProjection{
83 ProjectionVersion: 1,
84 CoveredCount: 2,
85 CoveredPrefixHash: coveredPrefixHash(parentMsgs, 2),
86 Messages: []provider.Message{
87 {Role: provider.RoleUser, Content: "summary"},
88 {Role: provider.RoleAssistant, Content: "one"},
89 },
90 },
91 }
92 if err := SaveCompactionState(path, st); err != nil {
93 t.Fatalf("SaveCompactionState: %v", err)
94 }
95 rewritten := parent.Snapshot()
96 rewritten[0] = provider.Message{Role: provider.RoleUser, Content: "edited"}
97 parent.Rewrite(rewritten, "test rewrite")
98 if err := parent.Save(path); err != nil {
99 t.Fatalf("Save rewritten parent: %v", err)
100 }
101 stale := NewSession("sys")
102 stale.Add(provider.Message{Role: provider.RoleUser, Content: "edited"})
103 stale.Add(provider.Message{Role: provider.RoleUser, Content: "local only"})
104 info, err := stale.SaveRecoveryBranch(RecoveryBranchOptions{OriginalPath: path})
105 if err != nil {
106 t.Fatalf("SaveRecoveryBranch: %v", err)
107 }
108 if _, ok, err := LoadCompactionState(info.Path); err != nil || ok {
109 t.Fatalf("LoadCompactionState recovery ok=%v err=%v, want no inherited sidecar", ok, err)
110 }
111 }
112
113 func TestSnapshotUpToDateFastPathSkipsWALProbe(t *testing.T) {
114 path := filepath.Join(t.TempDir(), "session.jsonl")
115 s := NewSession("sys")
116 s.Add(provider.Message{Role: provider.RoleUser, Content: "hello"})
117 if err := s.SaveSnapshot(path); err != nil {
118 t.Fatal(err)
119 }
120 logPath := store.SessionEventLog(path)
121 if err := os.Remove(logPath); err != nil {
122 t.Fatal(err)
123 }
124 if err := os.Mkdir(logPath, 0o755); err != nil {
125 t.Fatal(err)
126 }
127 if err := s.SaveSnapshot(path); err != nil {
128 t.Fatalf("unchanged healthy checkpoint probed unusable WAL path: %v", err)
129 }
130 }
131
131 lines GO