返回 DeepSeek-Reasonix
migrate_native_test.go
根目录 / internal / agent / migrate_native_test.go
1 package agent
2
3 import (
4 "encoding/json"
5 "os"
6 "path/filepath"
7 "testing"
8
9 "reasonix/internal/provider"
10 "reasonix/internal/store"
11 )
12
13 func TestMigrateLegacySessionsRehomesNativeWALAheadOfCheckpoint(t *testing.T) {
14 useSchemaOneLog(t)
15 src := t.TempDir()
16 global := t.TempDir()
17 workspace := t.TempDir()
18 projRoot := t.TempDir()
19 router := func(ws string) string { return filepath.Join(projRoot, filepath.Base(ws), "sessions") }
20 srcLog := filepath.Join(src, "chat-1.events.jsonl")
21 if err := os.WriteFile(srcLog, []byte(legacyEventLog), 0o644); err != nil {
22 t.Fatal(err)
23 }
24 writeLegacyMeta(t, src, "chat-1", workspace, "native flat import")
25
26 flat := filepath.Join(global, "chat-1.jsonl")
27 base := NewSession("system prompt")
28 base.Add(provider.Message{Role: provider.RoleUser, Content: "checkpoint"})
29 if err := base.Save(flat); err != nil {
30 t.Fatalf("seed native flat session: %v", err)
31 }
32 loaded, err := LoadSession(flat)
33 if err != nil {
34 t.Fatalf("load native flat session: %v", err)
35 }
36 loaded.Add(provider.Message{Role: provider.RoleAssistant, Content: "WAL tail survives migration"})
37 // Normal append saves make the WAL authoritative before the compatibility
38 // checkpoint catches up. Migration must copy that WAL-backed view.
39 if err := loaded.SaveSnapshot(flat); err != nil {
40 t.Fatalf("append native flat session: %v", err)
41 }
42 pinned := map[string]any{"schemaVersion": 1, "sessionId": "chat-1", "files": []string{"README.md"}}
43 pinnedRaw, err := json.Marshal(pinned)
44 if err != nil {
45 t.Fatal(err)
46 }
47 if err := os.WriteFile(store.SessionPinnedContext(flat), pinnedRaw, 0o600); err != nil {
48 t.Fatal(err)
49 }
50 info, err := os.Stat(srcLog)
51 if err != nil {
52 t.Fatalf("legacy source log: %v", err)
53 }
54 if err := os.Chtimes(flat, info.ModTime(), info.ModTime()); err != nil {
55 t.Fatalf("align flat import mtime: %v", err)
56 }
57
58 n, err := MigrateLegacySessions(src, global, router)
59 if err != nil || n != 1 {
60 t.Fatalf("migrate native flat session: n=%d err=%v, want 1 nil", n, err)
61 }
62 dest := filepath.Join(projRoot, filepath.Base(workspace), "sessions", "chat-1.jsonl")
63 migrated, err := LoadSession(dest)
64 if err != nil {
65 t.Fatalf("load re-homed native session: %v", err)
66 }
67 if got := migrated.Messages[len(migrated.Messages)-1].Content; got != "WAL tail survives migration" {
68 t.Fatalf("re-homed session lost WAL tail: got %q", got)
69 }
70 if _, err := os.Stat(flat); !os.IsNotExist(err) {
71 t.Errorf("native flat checkpoint should be retired: %v", err)
72 }
73 if _, err := os.Stat(store.SessionEventLog(flat)); !os.IsNotExist(err) {
74 t.Errorf("native flat WAL should be retired: %v", err)
75 }
76 migratedPinnedRaw, err := os.ReadFile(store.SessionPinnedContext(dest))
77 if err != nil {
78 t.Fatalf("read migrated pinned sidecar: %v", err)
79 }
80 var migratedPinned map[string]any
81 if err := json.Unmarshal(migratedPinnedRaw, &migratedPinned); err != nil {
82 t.Fatal(err)
83 }
84 if migratedPinned["sessionId"] != "chat-1" {
85 t.Fatalf("migrated pinned sessionId = %v", migratedPinned["sessionId"])
86 }
87 }
88
89 func TestMigratePinnedContextSidecarRewritesSessionIDAndPreservesFields(t *testing.T) {
90 dir := t.TempDir()
91 oldPath := filepath.Join(dir, "old.jsonl")
92 newPath := filepath.Join(dir, "new.jsonl")
93 raw := []byte(`{"schemaVersion":1,"sessionId":"old","files":["README.md"],"future":{"keep":true}}`)
94 if err := os.WriteFile(store.SessionPinnedContext(oldPath), raw, 0o600); err != nil {
95 t.Fatal(err)
96 }
97 if err := migratePinnedContextSidecar(oldPath, newPath, "new"); err != nil {
98 t.Fatal(err)
99 }
100 migratedRaw, err := os.ReadFile(store.SessionPinnedContext(newPath))
101 if err != nil {
102 t.Fatal(err)
103 }
104 var migrated map[string]any
105 if err := json.Unmarshal(migratedRaw, &migrated); err != nil {
106 t.Fatal(err)
107 }
108 if migrated["sessionId"] != "new" || migrated["future"] == nil {
109 t.Fatalf("migrated sidecar = %v", migrated)
110 }
111 if _, err := os.Stat(store.SessionPinnedContext(oldPath)); !os.IsNotExist(err) {
112 t.Fatalf("old sidecar survived migration: %v", err)
113 }
114 }
115
115 lines GO