返回 DeepSeek-Reasonix
resume_prune_test.go
根目录 / internal / control / resume_prune_test.go
1 package control
2
3 import (
4 "path/filepath"
5 "strings"
6 "testing"
7 "time"
8
9 "reasonix/internal/agent"
10 "reasonix/internal/event"
11 "reasonix/internal/provider"
12 )
13
14 func coldResumeFixture(t *testing.T, threshold time.Duration) (*agent.Session, string) {
15 t.Helper()
16
17 dir := t.TempDir()
18 loaded := &agent.Session{Messages: []provider.Message{
19 {Role: provider.RoleSystem, Content: "sys"},
20 {Role: provider.RoleUser, Content: "task"},
21 {Role: provider.RoleAssistant, ToolCalls: []provider.ToolCall{{ID: "1", Name: "grep", Arguments: "{}"}}},
22 {Role: provider.RoleTool, ToolCallID: "1", Name: "grep", Content: strings.Repeat("y", 5000)},
23 {Role: provider.RoleAssistant, Content: "step done"},
24 {Role: provider.RoleUser, Content: "next"},
25 {Role: provider.RoleAssistant, Content: "ok"},
26 }}
27 path := agent.NewSessionPath(dir, "test")
28 if err := loaded.Save(path); err != nil {
29 t.Fatalf("save: %v", err)
30 }
31 if _, err := agent.EnsureBranchMeta(path); err != nil {
32 t.Fatalf("meta: %v", err)
33 }
34
35 exec := agent.New(nil, nil, agent.NewSession("sys"), agent.Options{ContextWindow: 1000, RecentKeep: 2, ArchiveDir: dir}, event.Discard)
36 c := New(Options{Executor: exec, SessionDir: dir, Label: "test"})
37 if threshold == 0 {
38 c.testCacheColdAfter = -1 // force cold
39 } else {
40 c.testCacheColdAfter = threshold
41 }
42 c.Resume(loaded, path)
43 return loaded, path
44 }
45
46 func TestColdResumePrunesAndPersists(t *testing.T) {
47 loaded, path := coldResumeFixture(t, 0)
48
49 msgs := loaded.Snapshot()
50 if !strings.HasPrefix(msgs[3].Content, "[elided tool result") {
51 t.Fatalf("stale tool result not pruned on cold resume: %.60q", msgs[3].Content)
52 }
53 re, err := agent.LoadSession(path)
54 if err != nil {
55 t.Fatalf("reload: %v", err)
56 }
57 if !strings.HasPrefix(re.Messages[3].Content, "[elided tool result") {
58 t.Error("pruned transcript was not persisted")
59 }
60 if re.Messages[3].ToolCallID != "1" {
61 t.Error("tool pairing lost in persisted transcript")
62 }
63 }
64
65 func TestColdResumeAfterClonedHistoryStaysInPlace(t *testing.T) {
66
67 dir := t.TempDir()
68 saved := agent.NewSession("old sys")
69 saved.Add(provider.Message{Role: provider.RoleUser, Content: "task"})
70 saved.Add(provider.Message{Role: provider.RoleAssistant, ToolCalls: []provider.ToolCall{{ID: "1", Name: "grep", Arguments: "{}"}}})
71 saved.Add(provider.Message{Role: provider.RoleTool, ToolCallID: "1", Name: "grep", Content: strings.Repeat("y", 5000)})
72 saved.Add(provider.Message{Role: provider.RoleAssistant, Content: "step done"})
73 saved.Add(provider.Message{Role: provider.RoleUser, Content: "next"})
74 saved.Add(provider.Message{Role: provider.RoleAssistant, Content: "ok"})
75 path := agent.NewSessionPath(dir, "test")
76 if err := saved.Save(path); err != nil {
77 t.Fatalf("save: %v", err)
78 }
79 if _, err := agent.EnsureBranchMeta(path); err != nil {
80 t.Fatalf("meta: %v", err)
81 }
82
83 loaded, err := agent.LoadSession(path)
84 if err != nil {
85 t.Fatalf("load: %v", err)
86 }
87 msgs := loaded.Snapshot()
88 msgs[0].Content = "new sys"
89 resumed := loaded.CloneWithMessages(msgs)
90
91 exec := agent.New(nil, nil, agent.NewSession("new sys"), agent.Options{ContextWindow: 1000, RecentKeep: 2, ArchiveDir: dir}, event.Discard)
92 c := New(Options{Executor: exec, SessionDir: dir, Label: "test"})
93 c.testCacheColdAfter = -1 // force cold
94 c.Resume(resumed, path)
95
96 if got := c.SessionPath(); got != path {
97 t.Fatalf("SessionPath after cold resume = %q, want %q", got, path)
98 }
99 re, err := agent.LoadSession(path)
100 if err != nil {
101 t.Fatalf("reload: %v", err)
102 }
103 if got := re.Messages[0].Content; got != "new sys" {
104 t.Fatalf("system prompt after cold resume = %q, want new sys", got)
105 }
106 if !strings.HasPrefix(re.Messages[3].Content, "[elided tool result") {
107 t.Fatalf("stale tool result not pruned on cloned cold resume: %.60q", re.Messages[3].Content)
108 }
109 if matches, err := filepath.Glob(filepath.Join(dir, "*-recovery-*.jsonl")); err != nil || len(matches) != 0 {
110 t.Fatalf("recovery branches after cloned cold resume = %v err=%v, want none", matches, err)
111 }
112 }
113
114 func TestWarmResumeLeavesHistoryAlone(t *testing.T) {
115 loaded, path := coldResumeFixture(t, 24*time.Hour)
116
117 if got := loaded.Snapshot()[3].Content; !strings.HasPrefix(got, "yyy") {
118 t.Fatalf("warm resume rewrote history: %.60q", got)
119 }
120 re, err := agent.LoadSession(path)
121 if err != nil {
122 t.Fatalf("reload: %v", err)
123 }
124 if !strings.HasPrefix(re.Messages[3].Content, "yyy") {
125 t.Error("warm resume rewrote the saved transcript")
126 }
127 }
128
128 lines GO