返回 DeepSeek-Reasonix
tab_legacy_adoption_test.go
根目录 / desktop / tab_legacy_adoption_test.go
1 package main
2
3 import (
4 "os"
5 "path/filepath"
6 "runtime"
7 "strings"
8 "testing"
9
10 "reasonix/internal/agent"
11 "reasonix/internal/config"
12 "reasonix/internal/control"
13 "reasonix/internal/event"
14 "reasonix/internal/provider"
15 "reasonix/internal/session"
16 )
17
18 func TestBootLegacyTabReusesMigratedIdentityAfterMetadataRefresh(t *testing.T) {
19 for _, dag := range []bool{false, true} {
20 name := "linear"
21 if dag {
22 name = "selected DAG head"
23 }
24 t.Run(name, func(t *testing.T) { testBootLegacyAdoption(t, dag) })
25 }
26 }
27
28 func testBootLegacyAdoption(t *testing.T, dag bool) {
29 isolateDesktopUserDirs(t)
30 path := filepath.Join(config.SessionDir(), "boot # %20 中文.jsonl")
31 if err := os.MkdirAll(filepath.Dir(path), 0700); err != nil {
32 t.Fatal(err)
33 }
34 messageCount := 1
35 if dag {
36 legacy := agent.NewSession("system")
37 legacy.Add(provider.Message{ID: "user", Role: provider.RoleUser, Content: "preserved history"})
38 if err := legacy.Save(path); err != nil {
39 t.Fatal(err)
40 }
41 messageCount = 2
42 } else if err := os.WriteFile(path, []byte("{\"role\":\"user\",\"content\":\"preserved history\"}\n"), 0600); err != nil {
43 t.Fatal(err)
44 }
45 var expected session.SessionRef
46 for attempt := range 2 {
47 app := NewApp()
48 app.ctx = t.Context()
49 t.Cleanup(app.closeSessionServices)
50 if err := app.migrateLegacyDirectory(t.Context(), desktopMigrationSource{root: filepath.Dir(path), scope: "global", exact: map[string]bool{path: true}}); err != nil {
51 t.Fatal(err)
52 }
53 ref, found, err := app.legacyCanonicalRef(t.Context(), path)
54 if err != nil || !found {
55 t.Fatalf("adoption: %v, %v", found, err)
56 }
57 if attempt == 0 {
58 expected = ref
59 } else if ref != expected {
60 t.Fatalf("restart changed identity: %v -> %v", expected, ref)
61 }
62 // Catalog refresh changes an import sidecar without changing history.
63 meta, _, err := agent.LoadBranchMeta(path)
64 if err != nil {
65 t.Fatal(err)
66 }
67 meta.TopicTitle = "refreshed display title"
68 if err := agent.SaveBranchMeta(path, meta); err != nil {
69 t.Fatal(err)
70 }
71 ctrl := control.New(control.Options{
72 Executor: agent.New(nil, nil, agent.NewSession("test"), agent.Options{}, event.Discard),
73 SessionService: app.desktopSessionService(""), ExclusiveSession: true,
74 })
75 t.Cleanup(ctrl.Close)
76 lookup := path
77 if runtime.GOOS == "windows" {
78 lookup = strings.ToLower(filepath.VolumeName(path)) + path[len(filepath.VolumeName(path)):]
79 }
80 got, _, err := app.bindTabCanonicalSession(t.Context(), ctrl, &config.Config{}, "global", "", "", lookup, "", false)
81 if err != nil || got != expected {
82 t.Fatalf("boot must open adopted identity: got=%v want=%v err=%v", got, expected, err)
83 }
84 page, err := app.ReadSessionHistory(got, "", 10)
85 if err != nil || len(page.Messages) != messageCount || page.Messages[messageCount-1].Content != "preserved history" {
86 t.Fatalf("history=%+v err=%v", page, err)
87 }
88 tab := &WorkspaceTab{ID: "adopted", Scope: "global", Ctrl: ctrl, SessionID: got.SessionID, Ready: true}
89 tab.sink = &tabEventSink{tabID: tab.ID, app: app}
90 app.tabs[tab.ID], app.tabOrder, app.activeTabID = tab, []string{tab.ID}, tab.ID
91 installNoopRuntimeEvents(app, tab.sink)
92 if _, err := app.continueLegacySessionForTranscript(tab, ctrl, lookup, 10, true, false); err != nil {
93 t.Fatalf("transcript continuation: %v", err)
94 }
95 list, err := app.desktopSessionService("").Query().List(t.Context(), "", 10)
96 if err != nil || len(list.Sessions) != 1 {
97 t.Fatalf("duplicate import: %+v, %v", list, err)
98 }
99 if attempt == 1 && !dag {
100 original, err := os.ReadFile(path)
101 if err != nil {
102 t.Fatal(err)
103 }
104 if err := os.Remove(path); err != nil {
105 t.Fatal(err)
106 }
107 got, _, err := app.bindTabCanonicalSession(t.Context(), ctrl, &config.Config{}, "global", "", "", lookup, "", false)
108 if err != nil || got != expected {
109 t.Fatalf("missing retained source replaced adoption: %v %v", got, err)
110 }
111 if err := os.WriteFile(path, append(original, []byte("{\"role\":\"user\",\"content\":\"changed\"}\n")...), 0600); err != nil {
112 t.Fatal(err)
113 }
114 if got, _, err := app.bindTabCanonicalSession(t.Context(), ctrl, &config.Config{}, "global", "", "", lookup, "", false); err != nil || got != expected {
115 t.Fatalf("retained source changes must not replace the adopted conversation: %v %v", got, err)
116 }
117 if ref, _ := ctrl.SessionRef(); ref != expected {
118 t.Fatalf("failed bind replaced original: %v", ref)
119 }
120 }
121 ctrl.Close()
122 app.closeSessionServices()
123 }
124 if _, err := os.Stat(path); err != nil {
125 t.Fatalf("source lost: %v", err)
126 }
127 }
128
128 lines GO