返回 DeepSeek-Reasonix
native_session_test.go
根目录 / desktop / native_session_test.go
1 package main
2
3 import (
4 "bytes"
5 "context"
6 "encoding/json"
7 "os"
8 "path/filepath"
9 "testing"
10
11 "reasonix/internal/agent"
12 "reasonix/internal/config"
13 "reasonix/internal/control"
14 "reasonix/internal/event"
15 "reasonix/internal/provider"
16 "reasonix/internal/session"
17 "reasonix/internal/tool"
18 )
19
20 func TestOpenHistoricalDAGPreservesSelectedHeadAcrossTabPersistence(t *testing.T) {
21 isolateDesktopUserDirs(t)
22 dir := config.SessionDir()
23 if err := os.MkdirAll(dir, 0o700); err != nil {
24 t.Fatal(err)
25 }
26 path := filepath.Join(dir, "native-heads.jsonl")
27 loaded := agent.NewSession("system")
28 loaded.Add(provider.Message{Role: provider.RoleUser, Content: "shared"})
29 loaded.Add(provider.Message{Role: provider.RoleAssistant, Content: "original answer"})
30 if err := loaded.Save(path); err != nil {
31 t.Fatal(err)
32 }
33 if _, err := loaded.ForkHead(path, loaded.Snapshot()[1].ID, agent.HeadKindFork, "alternate"); err != nil {
34 t.Fatal(err)
35 }
36 loaded.Add(provider.Message{Role: provider.RoleAssistant, Content: "alternate answer"})
37 if err := loaded.Save(path); err != nil {
38 t.Fatal(err)
39 }
40 before, err := os.ReadFile(agent.SessionEventLogPath(path))
41 if err != nil {
42 t.Fatal(err)
43 }
44 app := NewApp()
45 app.ctx = t.Context()
46 installNoopRuntimeEvents(app)
47 t.Cleanup(app.closeSessionServices)
48 source := &SessionSourceRef{HostID: localDesktopHostID, Path: path, HeadID: agent.SessionMainHead}
49 meta, err := app.OpenTopicSession("global", "", "", nativeSessionSourceRoute(source))
50 if err != nil {
51 t.Fatal(err)
52 }
53 tab := waitForTabReady(t, app, meta.ID)
54 ctrl, ok := tab.Ctrl.(*control.Controller)
55 if !ok || !ctrl.NativeLegacySession() {
56 t.Fatal("DAG was converted")
57 }
58 history := ctrl.History()
59 if history[len(history)-1].Content != "original answer" {
60 t.Fatalf("wrong head: %+v", history)
61 }
62 after, _ := os.ReadFile(agent.SessionEventLogPath(path))
63 if !bytes.Equal(before, after) {
64 t.Fatal("opening changed the DAG")
65 }
66 body, err := json.Marshal(persistedDesktopTabEntry(tab))
67 if err != nil {
68 t.Fatal(err)
69 }
70 var entry desktopTabEntry
71 if err := json.Unmarshal(body, &entry); err != nil {
72 t.Fatal(err)
73 }
74 entry.historicalSource = source
75 restored := &WorkspaceTab{}
76 prepareRestoredTabIdentity(restored, entry)
77 if restored.SessionHeadID != agent.SessionMainHead {
78 t.Fatal("restart lost the selected head")
79 }
80 }
81
82 func TestOpenHistoricalDirectoryUsesNativeStore(t *testing.T) {
83 isolateDesktopUserDirs(t)
84 root := config.SessionStoreDir()
85 const id = "native-open"
86 coldV4MigrationFixture(t, root, id)
87 path := filepath.Join(root, id)
88 app := NewApp()
89 app.ctx = t.Context()
90 installNoopRuntimeEvents(app)
91 t.Cleanup(app.closeSessionServices)
92 source := &SessionSourceRef{HostID: localDesktopHostID, Path: path, SourceKey: desktopSourceKey(path, "")}
93 meta, err := app.OpenTopicSession("global", "", "", nativeSessionSourceRoute(source))
94 if err != nil {
95 t.Fatal(err)
96 }
97 tab := waitForTabReady(t, app, meta.ID)
98 if tab.SessionPath != path || tab.SessionID != "" {
99 t.Fatalf("historical locator changed: %+v", meta)
100 }
101 ctrl, ok := tab.Ctrl.(*control.Controller)
102 if !ok || ctrl.SessionService() == app.desktopSessionService("") {
103 t.Fatal("opened a migrated runtime")
104 }
105 ref, ok := ctrl.SessionRef()
106 if !ok || ref.SessionID != id {
107 t.Fatalf("wrong native identity: %+v", ref)
108 }
109 state, err := app.workspaceRegistry().Load(t.Context())
110 if err != nil {
111 t.Fatal(err)
112 }
113 if len(state.SourceMappings) != 0 || len(state.PendingOperations) != 0 {
114 t.Fatal("opening started conversion")
115 }
116 if _, err := app.desktopSessionService("").Query().Snapshot(t.Context(), ref); err == nil {
117 t.Fatal("opening duplicated the transcript")
118 }
119 if err := app.ensureTabControllerWorkspace(tab); err != nil {
120 t.Fatal(err)
121 }
122 if tab.Ctrl != ctrl {
123 t.Fatal("native directory identity caused a redundant rebuild")
124 }
125 }
126
127 func TestNativeSessionNewRegistersCurrentStoreAndWorkspace(t *testing.T) {
128 for _, directory := range []bool{false, true} {
129 t.Run(map[bool]string{false: "jsonl", true: "stored-directory"}[directory], func(t *testing.T) {
130 isolateDesktopUserDirs(t)
131 app := NewApp()
132 current := app.desktopSessionService("")
133 historical := current
134 path := filepath.Join(t.TempDir(), "old.jsonl")
135 var ref session.SessionRef
136 if directory {
137 root := t.TempDir()
138 coldV4MigrationFixture(t, root, "old-directory")
139 var err error
140 historical, err = app.historicalSessionService(root)
141 if err != nil {
142 t.Fatal(err)
143 }
144 ref = session.SessionRef{HostID: localDesktopHostID, SessionID: "old-directory"}
145 path = filepath.Join(root, ref.SessionID)
146 } else if err := os.WriteFile(path, []byte("{\"role\":\"user\",\"content\":\"old\"}\n"), 0o600); err != nil {
147 t.Fatal(err)
148 }
149 // The app's historical service must release its ownership file before
150 // TempDir removes the source root on Windows.
151 t.Cleanup(app.closeSessionServices)
152 exec := agent.New(nil, tool.NewRegistry(), agent.NewSession(""), agent.Options{}, event.Discard)
153 ctrl := control.New(control.Options{Runner: exec, Executor: exec, Sink: event.Discard,
154 SessionService: historical, SessionCreateService: current, ExclusiveSession: directory, NativeLegacySession: !directory,
155 OnSessionRotation: app.prepareDesktopSessionRotation})
156 t.Cleanup(func() { ctrl.Close(); <-ctrl.Closed() })
157 if directory {
158 if _, err := ctrl.OpenSession(t.Context(), ref); err != nil {
159 t.Fatal(err)
160 }
161 } else {
162 loaded, err := agent.LoadSession(path)
163 if err != nil {
164 t.Fatal(err)
165 }
166 ctrl.Resume(loaded, path)
167 }
168 tab := &WorkspaceTab{ID: "native", Scope: "global", SessionPath: path, Ctrl: ctrl, Ready: true}
169 app.tabs = map[string]*WorkspaceTab{tab.ID: tab}
170 app.activeTabID = tab.ID
171 if err := ctrl.NewSession(); err != nil {
172 t.Fatal(err)
173 }
174 created, ok := ctrl.SessionRef()
175 if !ok || ctrl.SessionService() != current {
176 t.Fatalf("new session retained historical store: %+v", created)
177 }
178 info, err := current.Query().Stat(context.Background(), created)
179 if err != nil || info.Origin != session.SessionOriginNew {
180 t.Fatalf("new metadata: %+v %v", info, err)
181 }
182 contained, err := app.workspaceRegistry().Contains(t.Context(), created.SessionID)
183 if err != nil || !contained {
184 t.Fatalf("new session missing workspace: %v", err)
185 }
186 if _, err := os.Stat(path); err != nil {
187 t.Fatalf("new session removed old source: %v", err)
188 }
189 if got := ctrl.History(); len(provider.ModelMessages(got)) > 1 {
190 t.Fatalf("new session retained old history: %+v", got)
191 }
192 })
193 }
194 }
195
195 lines GO