返回 DeepSeek-Reasonix
history_slice_migration_test.go
根目录 / desktop / history_slice_migration_test.go
1 package main
2
3 import (
4 "context"
5 "fmt"
6 "os"
7 "path/filepath"
8 "strings"
9 "testing"
10
11 "reasonix/internal/agent"
12 "reasonix/internal/provider"
13 "reasonix/internal/store"
14 )
15
16 // TestHistoryIndexMigrationLoop covers the startup worker directly (the
17 // goroutine itself only arms from the startup hook): it builds missing
18 // indexes, leaves valid ones untouched, and skips legacy event-format files.
19 func TestHistoryIndexMigrationLoop(t *testing.T) {
20 app := historySliceTestApp(t)
21 tab := newColdHistoryTab(t, app)
22 dir := tabSessionDir(tab)
23
24 // A session whose index is missing, and one whose index is valid.
25 var msgs []provider.Message
26 for i := range 4 {
27 msgs = append(msgs, historySliceUser(i, "q"), historySliceAssistant(i, "a"))
28 }
29 _, missingPath := saveHistorySliceSession(t, dir, "migrate-missing.jsonl", msgs)
30 _, validPath := saveHistorySliceSession(t, dir, "migrate-valid.jsonl", msgs)
31 tab.SessionPath = validPath
32
33 missingIndex := store.SessionDisplayIndex(missingPath)
34 if err := os.Remove(missingIndex); err != nil {
35 t.Fatal(err)
36 }
37 validIndex := store.SessionDisplayIndex(validPath)
38 validBefore, err := os.Stat(validIndex)
39 if err != nil {
40 t.Fatal(err)
41 }
42
43 // A legacy event-format session must be skipped, not mis-indexed.
44 legacyPath := filepath.Join(dir, "migrate-legacy.jsonl")
45 legacy := "{\"kind\":\"user.message\",\"text\":\"hi\"}\n{\"kind\":\"model.final\",\"content\":\"hello\"}\n"
46 if err := os.WriteFile(legacyPath, []byte(legacy), 0o600); err != nil {
47 t.Fatal(err)
48 }
49
50 app.historyIndexMigrationLoop(context.Background())
51
52 if _, err := agent.LoadSessionDisplayIndex(missingIndex); err != nil {
53 t.Fatalf("migration should have built the missing index: %v", err)
54 }
55 validAfter, err := os.Stat(validIndex)
56 if err != nil {
57 t.Fatal(err)
58 }
59 if !validAfter.ModTime().Equal(validBefore.ModTime()) {
60 t.Fatal("migration rewrote a valid index")
61 }
62 if _, err := os.Stat(store.SessionDisplayIndex(legacyPath)); !os.IsNotExist(err) {
63 t.Fatal("migration must not index legacy event-format sessions")
64 }
65
66 // Idempotent: a second pass changes nothing.
67 if err := os.Remove(missingIndex); err != nil {
68 t.Fatal(err)
69 }
70 app.historyIndexMigrationLoop(context.Background())
71 if _, err := agent.LoadSessionDisplayIndex(missingIndex); err != nil {
72 t.Fatalf("second migration pass should rebuild: %v", err)
73 }
74 }
75
76 // TestHistorySliceColdLegacyEventFormat pages an ancient event-record
77 // session through the streaming legacy fallback.
78 func TestHistorySliceColdLegacyEventFormat(t *testing.T) {
79 app := historySliceTestApp(t)
80 tab := newColdHistoryTab(t, app)
81 dir := tabSessionDir(tab)
82 if err := os.MkdirAll(dir, 0o755); err != nil {
83 t.Fatal(err)
84 }
85 path := filepath.Join(dir, "legacy.jsonl")
86 var body strings.Builder
87 for i := range 10 {
88 fmt.Fprintf(&body, "{\"kind\":\"user.message\",\"text\":\"question %d\"}\n", i)
89 fmt.Fprintf(&body, "{\"kind\":\"model.final\",\"content\":\"answer %d\"}\n", i)
90 }
91 if err := os.WriteFile(path, []byte(body.String()), 0o600); err != nil {
92 t.Fatal(err)
93 }
94 tab.SessionPath = path
95
96 pages := collectHistorySlicePages(t, app, "cold", HistorySliceRequest{Turns: 4, Entries: 6})
97 rows := concatHistoryPages(pages)
98 if len(rows) != 20 {
99 t.Fatalf("legacy rows = %d, want 20", len(rows))
100 }
101 if rows[0].Role != "user" || rows[0].Content != "question 0" {
102 t.Fatalf("first legacy row = %+v", rows[0])
103 }
104 if rows[19].Content != "answer 9" {
105 t.Fatalf("last legacy row = %+v", rows[19])
106 }
107 if pages[0].TotalTurns != 10 {
108 t.Fatalf("TotalTurns = %d, want 10", pages[0].TotalTurns)
109 }
110 // No display index may be written for the legacy format.
111 if _, err := os.Stat(store.SessionDisplayIndex(path)); !os.IsNotExist(err) {
112 t.Fatal("legacy event-format sessions must not get a display index")
113 }
114 }
115
115 lines GO