1package core
2
3import (
4 "testing"
5 "time"
6
7 "github.com/bnema/zut/packages/provider"
8)
9
10func TestReadSessionHistoryRetainsPreCompactionSegments(t *testing.T) {
11 root := t.TempDir()
12 cwd := "/workspace/history"
13 session, err := NewSession(root, cwd, "test", "model", "test")
14 if err != nil {
15 t.Fatal(err)
16 }
17 oldPrompt := textMessage(provider.RoleUser, "old prompt")
18 oldAnswer := textMessage(provider.RoleAssistant, "old answer")
19 toolCall := provider.Message{
20 Role: provider.RoleAssistant,
21 Content: []provider.Content{provider.ToolCallBlock{ID: "call-1", Name: "read"}},
22 }
23 toolResult := provider.Message{
24 Role: provider.RoleTool,
25 Content: []provider.Content{provider.ToolResultBlock{
26 CallID: "call-1",
27 Content: []provider.Content{provider.TextBlock{Text: "old file"}},
28 }},
29 }
30 for _, message := range []provider.Message{oldPrompt, oldAnswer, toolCall, toolResult} {
31 if err := session.AppendMessage(message); err != nil {
32 t.Fatal(err)
33 }
34 }
35 if err := session.AppendCompaction([]provider.Message{
36 {
37 Role: provider.RoleUser,
38 Meta: map[string]string{"compaction": "true"},
39 Content: []provider.Content{
40 provider.TextBlock{Text: "summary"},
41 },
42 },
43 }); err != nil {
44 t.Fatal(err)
45 }
46 if err := session.AppendMessage(textMessage(provider.RoleUser, "new prompt")); err != nil {
47 t.Fatal(err)
48 }
49 path := session.Path
50 if err := session.Close(); err != nil {
51 t.Fatal(err)
52 }
53
54 history, err := ReadSessionHistory(path)
55 if err != nil {
56 t.Fatalf("ReadSessionHistory: %v", err)
57 }
58 if len(history.Segments) != 2 {
59 t.Fatalf("history segments = %d, want 2", len(history.Segments))
60 }
61 if history.Segments[0].Compacted {
62 t.Fatal("pre-compaction segment marked compacted")
63 }
64 if !history.Segments[1].Compacted {
65 t.Fatal("replacement segment not marked compacted")
66 }
67 assertMessageTexts(t, history.Segments[0].Messages, []string{"old prompt", "old answer", "", ""})
68 assertMessageTexts(t, history.Segments[1].Messages, []string{"summary", "new prompt"})
69
70 branchPath, err := BranchSessionHiddenFromHistory(path, root, cwd, "test", history.Segments[0], 1)
71 if err != nil {
72 t.Fatalf("BranchSessionHiddenFromHistory: %v", err)
73 }
74 branch, messages, err := OpenSession(branchPath)
75 if err != nil {
76 t.Fatalf("OpenSession branch: %v", err)
77 }
78 if err := branch.Close(); err != nil {
79 t.Fatal(err)
80 }
81 assertMessageTexts(t, messages, []string{"old prompt"})
82 if branch.Meta.Parent != history.Meta.ID {
83 t.Fatalf("branch parent = %q, want %q", branch.Meta.Parent, history.Meta.ID)
84 }
85}
86
87func TestReadSessionHistoryRepairsEachSegmentIndependently(t *testing.T) {
88 root := t.TempDir()
89 session, err := NewSession(root, "/workspace/history-repair", "test", "model", "test")
90 if err != nil {
91 t.Fatal(err)
92 }
93 call := provider.Message{
94 Role: provider.RoleAssistant,
95 Content: []provider.Content{provider.ToolCallBlock{ID: "call-1", Name: "bash"}},
96 Time: time.Unix(1, 0),
97 }
98 if err := session.AppendMessage(call); err != nil {
99 t.Fatal(err)
100 }
101 if err := session.AppendCompaction([]provider.Message{textMessage(provider.RoleUser, "summary")}); err != nil {
102 t.Fatal(err)
103 }
104 path := session.Path
105 if err := session.Close(); err != nil {
106 t.Fatal(err)
107 }
108
109 history, err := ReadSessionHistory(path)
110 if err != nil {
111 t.Fatal(err)
112 }
113 if len(history.Segments) != 2 {
114 t.Fatalf("history segments = %d, want 2", len(history.Segments))
115 }
116 if len(history.Segments[0].Messages) != 2 {
117 t.Fatalf("repaired old segment messages = %d, want assistant plus stub result", len(history.Segments[0].Messages))
118 }
119 if history.Segments[0].Messages[1].Role != provider.RoleTool {
120 t.Fatalf("repaired old segment tail role = %q, want tool", history.Segments[0].Messages[1].Role)
121 }
122}