返回 DeepSeek-Reasonix
session_v5_migration_recovery_test.go
根目录 / desktop / session_v5_migration_recovery_test.go
1 package main
2
3 import (
4 "os"
5 "path/filepath"
6 "testing"
7
8 "reasonix/internal/agent"
9 "reasonix/internal/config"
10 "reasonix/internal/provider"
11 "reasonix/internal/session"
12 )
13
14 func TestDesktopV5UpgradeExcludesAutomaticRecoveryCopies(t *testing.T) {
15 isolateDesktopUserDirs(t)
16 dir := config.SessionDir()
17 for _, fixture := range []struct {
18 name string
19 meta agent.BranchMeta
20 want bool
21 }{
22 {"ordinary", agent.BranchMeta{}, true},
23 {"user-fork", agent.BranchMeta{ParentID: "ordinary"}, true},
24 {"my-recovery-notes", agent.BranchMeta{}, true},
25 {"marked-copy", agent.BranchMeta{Recovered: true, ParentID: "ordinary"}, false},
26 {"version-copy", agent.BranchMeta{VersionKind: agent.VersionRecovery}, false},
27 {"digest-copy", agent.BranchMeta{RecoveryDigest: "historical-marker"}, false},
28 {"ordinary-recovery-0123456789abcdef", agent.BranchMeta{}, false},
29 {"ordinary-recovery-0123456789abcdef-recovery-fedcba9876543210", agent.BranchMeta{}, false},
30 } {
31 path := filepath.Join(dir, fixture.name+".jsonl")
32 legacy := agent.NewSession("system")
33 legacy.Add(provider.Message{ID: fixture.name, Role: provider.RoleUser, Content: fixture.name})
34 if err := legacy.Save(path); err != nil {
35 t.Fatal(err)
36 }
37 writeMigrationJSON(t, agent.BranchMetaPath(path), fixture.meta)
38 if fixture.name == "ordinary-recovery-0123456789abcdef" {
39 if err := os.Remove(agent.BranchMetaPath(path)); err != nil {
40 t.Fatal(err)
41 }
42 }
43 if got := desktopMigrationAutomaticRecovery(path); got == fixture.want {
44 t.Fatalf("unexpected recovery classification: %s = %v", fixture.name, got)
45 }
46 }
47 app := NewApp()
48 t.Cleanup(app.closeSessionServices)
49 if err := app.migrateDesktopSessionsV5(t.Context()); err != nil {
50 t.Fatal(err)
51 }
52 for _, history := range v5MigrationHistories(t, app) {
53 last := history[len(history)-1]
54 if last != "ordinary" && last != "user-fork" && last != "my-recovery-notes" {
55 t.Fatalf("recovery artifact appeared as a conversation: %v", history)
56 }
57 }
58 assertLineageRestart(t, app, 3)
59 }
60
61 func TestDesktopV5UpgradeExcludesConvertedRecoveryCopies(t *testing.T) {
62 for _, removed := range []bool{false, true} {
63 name := "original-present"
64 if removed {
65 name = "original-removed"
66 }
67 t.Run(name, func(t *testing.T) {
68 isolateDesktopUserDirs(t)
69 path, _, _ := migrationSingleDAGFixture(t)
70 copyPath := filepath.Join(filepath.Dir(path), "original-v2-recovery-0123456789abcdef.jsonl")
71 copySession := agent.NewSession("system")
72 copySession.Add(provider.Message{ID: "copy-only", Role: provider.RoleUser, Content: "automatic conflict data"})
73 if err := copySession.Save(copyPath); err != nil {
74 t.Fatal(err)
75 }
76 v3root := filepath.Join(filepath.Dir(config.SessionStoreDir()), "sessions-v3")
77 id := convertedV3Fixture(t, copyPath, v3root, "")
78 v4, err := session.NewService("fixture", session.NewFilesystemPersistence(config.SessionStoreDir()))
79 if err != nil {
80 t.Fatal(err)
81 }
82 if _, _, err := v4.ContinuePrototype(t.Context(), filepath.Join(v3root, id)); err != nil {
83 t.Fatal(err)
84 }
85 if err := v4.Shutdown(t.Context()); err != nil {
86 t.Fatal(err)
87 }
88 if removed {
89 for _, file := range legacyMigrationSourceFiles(copyPath) {
90 if err := os.Remove(file); err != nil && !os.IsNotExist(err) {
91 t.Fatal(err)
92 }
93 }
94 // Only the descendant's archived manifest still knows the origin.
95 if err := os.RemoveAll(filepath.Join(v3root, id)); err != nil {
96 t.Fatal(err)
97 }
98 }
99 app := NewApp()
100 t.Cleanup(app.closeSessionServices)
101 if err := app.migrateDesktopSessionsV5(t.Context()); err != nil {
102 t.Fatal(err)
103 }
104 assertLineageRestart(t, app, 1)
105 })
106 }
107 }
108
109 func TestDesktopV5UpgradeExcludesRecoveryStoreWithoutTranscript(t *testing.T) {
110 isolateDesktopUserDirs(t)
111 root := config.SessionStoreDir()
112 for _, id := range []string{"ordinary", "only-meta-copy", "ordinary-recovery-0123456789abcdef"} {
113 service, err := session.NewService("fixture", session.NewFilesystemPersistence(root))
114 if err != nil {
115 t.Fatal(err)
116 }
117 runtime, err := service.Create(t.Context(), session.CreateOptions{SessionID: id})
118 if err != nil {
119 t.Fatal(err)
120 }
121 appendMigrationTestMessage(t, service, runtime.Ref(), id)
122 if err := service.Shutdown(t.Context()); err != nil {
123 t.Fatal(err)
124 }
125 }
126 writeMigrationJSON(t, agent.BranchMetaPath(filepath.Join(config.SessionDir(), "only-meta-copy.jsonl")), agent.BranchMeta{Recovered: true})
127 app := NewApp()
128 t.Cleanup(app.closeSessionServices)
129 if err := app.migrateDesktopSessionsV5(t.Context()); err != nil {
130 t.Fatal(err)
131 }
132 assertLineageRestart(t, app, 1)
133 }
134
135 func TestDesktopV5UpgradeExcludesArchivedRecoveryMetadata(t *testing.T) {
136 isolateDesktopUserDirs(t)
137 migrationSingleDAGFixture(t)
138 path := filepath.Join(config.SessionDir(), "metadata-marked-copy.jsonl")
139 legacy := agent.NewSession("system")
140 legacy.Add(provider.Message{ID: "automatic", Role: provider.RoleUser, Content: "conflict copy"})
141 if err := legacy.Save(path); err != nil {
142 t.Fatal(err)
143 }
144 writeMigrationJSON(t, agent.BranchMetaPath(path), agent.BranchMeta{Recovered: true})
145 root := filepath.Join(filepath.Dir(config.SessionStoreDir()), "sessions-v3")
146 id := convertedV3Fixture(t, path, root, "")
147 for _, file := range legacyMigrationSourceFiles(path) {
148 if err := os.Remove(file); err != nil && !os.IsNotExist(err) {
149 t.Fatal(err)
150 }
151 }
152 archived := filepath.Join(root, id, "legacy", filepath.Base(path))
153 before := migrationSourceSnapshot(t, append(canonicalMigrationSourceFiles(root, id), legacyMigrationSourceFiles(archived)...))
154 app := NewApp()
155 t.Cleanup(app.closeSessionServices)
156 if err := app.migrateDesktopSessionsV5(t.Context()); err != nil {
157 t.Fatal(err)
158 }
159 assertLineageRestart(t, app, 1)
160 assertMigrationSourceSnapshot(t, before)
161 }
162
162 lines GO