返回 DeepSeek-Reasonix
new_session_meta_test.go
根目录 / desktop / new_session_meta_test.go
1 package main
2
3 import (
4 "os"
5 "path/filepath"
6 "strings"
7 "testing"
8
9 "reasonix/internal/agent"
10 )
11
12 func listSessionsAfterPinnedOwnerReconcile(t *testing.T, app *App, dir, workspaceRoot string) []SessionMeta {
13 t.Helper()
14 if active := app.activeSessionDir(); !sameDesktopPath(active, dir) {
15 t.Fatalf("active session dir = %q, want pinned dir %q", active, dir)
16 }
17 reconcileSessionCatalogForTest(t, app, dir, "project", workspaceRoot)
18 return app.ListSessions()
19 }
20
21 func TestPinNewEmptySessionBranchMetaStoresBinding(t *testing.T) {
22 isolateDesktopUserDirs(t)
23
24 projectRoot := t.TempDir()
25 tests := []struct {
26 name string
27 scope string
28 workspaceRoot string
29 topicID string
30 topicTitle string
31 wantRoot string
32 }{
33 {
34 name: "project",
35 scope: "project",
36 workspaceRoot: projectRoot,
37 topicID: "topic_project",
38 topicTitle: "Project topic",
39 wantRoot: normalizeProjectRoot(projectRoot),
40 },
41 {
42 name: "global",
43 scope: "global",
44 workspaceRoot: globalWorkspaceRoot(),
45 topicID: "topic_global",
46 topicTitle: "Global topic",
47 },
48 }
49
50 for _, tt := range tests {
51 t.Run(tt.name, func(t *testing.T) {
52 path, err := createEmptySessionFile(t.TempDir(), "test-model")
53 if err != nil {
54 t.Fatalf("createEmptySessionFile: %v", err)
55 }
56 err = pinNewEmptySessionBranchMeta(
57 path,
58 tt.scope,
59 tt.workspaceRoot,
60 tt.topicID,
61 tt.topicTitle,
62 )
63 if err != nil {
64 t.Fatalf("pinNewEmptySessionBranchMeta: %v", err)
65 }
66 meta, ok, err := agent.LoadBranchMeta(path)
67 if err != nil || !ok {
68 t.Fatalf("LoadBranchMeta(%q): ok=%v err=%v", path, ok, err)
69 }
70 if meta.Scope != tt.scope ||
71 meta.WorkspaceRoot != tt.wantRoot ||
72 meta.TopicID != tt.topicID ||
73 meta.TopicTitle != tt.topicTitle {
74 t.Fatalf(
75 "branch meta binding = scope %q root %q topic %q title %q, want %q %q %q %q",
76 meta.Scope,
77 meta.WorkspaceRoot,
78 meta.TopicID,
79 meta.TopicTitle,
80 tt.scope,
81 tt.wantRoot,
82 tt.topicID,
83 tt.topicTitle,
84 )
85 }
86 })
87 }
88 }
89
90 func TestPinnedSessionMetaKeepsProjectBindingOutsideKnownDirectories(t *testing.T) {
91 isolateDesktopUserDirs(t)
92
93 projectRoot := t.TempDir()
94 topicID := "topic_unknown_dir"
95 topicTitle := "Unknown directory"
96 sessionPath, err := createEmptySessionFile(t.TempDir(), "test-model")
97 if err != nil {
98 t.Fatalf("createEmptySessionFile: %v", err)
99 }
100 if err := pinNewEmptySessionBranchMeta(
101 sessionPath,
102 "project",
103 projectRoot,
104 topicID,
105 topicTitle,
106 ); err != nil {
107 t.Fatalf("pinNewEmptySessionBranchMeta: %v", err)
108 }
109
110 app := NewApp()
111 tab := &WorkspaceTab{
112 ID: "tab_unknown_dir",
113 Scope: "project",
114 WorkspaceRoot: projectRoot,
115 TopicID: topicID,
116 TopicTitle: topicTitle,
117 SessionPath: sessionPath,
118 disabledMCP: map[string]ServerView{},
119 }
120 app.tabs[tab.ID] = tab
121 app.tabOrder = []string{tab.ID}
122 app.activeTabID = tab.ID
123
124 resolved, ok := app.reconcileTabWithPinnedSessionMeta(tab)
125 if !ok || !sameDesktopPath(resolved, sessionPath) {
126 t.Fatalf("reconcile binding = %q, %v, want %q, true", resolved, ok, sessionPath)
127 }
128 if tab.Scope != "project" || !sameProjectRoot(tab.WorkspaceRoot, projectRoot) {
129 t.Fatalf("reconciled tab binding = %q/%q, want project/%q", tab.Scope, tab.WorkspaceRoot, projectRoot)
130 }
131 }
132
133 func TestPinSessionBranchMetaReturnsCorruptSidecarError(t *testing.T) {
134 isolateDesktopUserDirs(t)
135
136 sessionPath := filepath.Join(t.TempDir(), "corrupt.jsonl")
137 if err := os.WriteFile(sessionPath, nil, 0o644); err != nil {
138 t.Fatalf("write session: %v", err)
139 }
140 metaPath := agent.BranchMetaPath(sessionPath)
141 const corruptMeta = `{"scope":`
142 if err := os.WriteFile(metaPath, []byte(corruptMeta), 0o644); err != nil {
143 t.Fatalf("write corrupt branch meta: %v", err)
144 }
145
146 if err := pinSessionBranchMeta(sessionPath, "project", t.TempDir(), "topic", "Topic"); err == nil {
147 t.Fatal("pinSessionBranchMeta error = nil, want corrupt sidecar error")
148 }
149 got, err := os.ReadFile(metaPath)
150 if err != nil {
151 t.Fatalf("read corrupt branch meta: %v", err)
152 }
153 if string(got) != corruptMeta {
154 t.Fatalf("corrupt branch meta was overwritten: %q", got)
155 }
156 }
157
158 func TestPinNewEmptySessionBranchMetaCleansUpRejectedBinding(t *testing.T) {
159 isolateDesktopUserDirs(t)
160
161 dir := t.TempDir()
162 path, err := createEmptySessionFile(dir, "test-model")
163 if err != nil {
164 t.Fatalf("createEmptySessionFile: %v", err)
165 }
166 if err := pinNewEmptySessionBranchMeta(
167 path,
168 "project",
169 "",
170 "topic",
171 "Topic",
172 ); err == nil {
173 t.Fatal("pinNewEmptySessionBranchMeta error = nil, want missing project root error")
174 }
175
176 entries, err := os.ReadDir(dir)
177 if err != nil {
178 t.Fatalf("read session dir: %v", err)
179 }
180 for _, entry := range entries {
181 name := entry.Name()
182 if strings.HasSuffix(name, ".jsonl") || strings.HasSuffix(name, ".meta") {
183 t.Fatalf("rejected binding left session artifact %q", name)
184 }
185 }
186 }
187
188 func TestPinSessionBranchMetaPreservesExistingFields(t *testing.T) {
189 isolateDesktopUserDirs(t)
190
191 sessionPath := filepath.Join(t.TempDir(), "existing.jsonl")
192 if err := os.WriteFile(sessionPath, nil, 0o644); err != nil {
193 t.Fatalf("write session: %v", err)
194 }
195 existing := agent.BranchMeta{
196 CustomTitle: "Keep title",
197 Model: "provider/model",
198 Revision: 7,
199 ContentDigest: "digest",
200 WriterID: "writer",
201 SchemaVersion: agent.BranchMetaCountsVersion,
202 Turns: 3,
203 Preview: "Keep preview",
204 RecoveryReason: "keep recovery",
205 }
206 if err := agent.SaveBranchMetaPreserveUpdated(sessionPath, existing); err != nil {
207 t.Fatalf("save existing branch meta: %v", err)
208 }
209
210 projectRoot := t.TempDir()
211 if err := pinSessionBranchMeta(sessionPath, "project", projectRoot, "topic", "Topic"); err != nil {
212 t.Fatalf("pinSessionBranchMeta: %v", err)
213 }
214 got, ok, err := agent.LoadBranchMeta(sessionPath)
215 if err != nil || !ok {
216 t.Fatalf("LoadBranchMeta: ok=%v err=%v", ok, err)
217 }
218 if got.CustomTitle != existing.CustomTitle ||
219 got.Model != existing.Model ||
220 got.Revision != existing.Revision ||
221 got.ContentDigest != existing.ContentDigest ||
222 got.WriterID != existing.WriterID ||
223 got.SchemaVersion != existing.SchemaVersion ||
224 got.Turns != existing.Turns ||
225 got.Preview != existing.Preview ||
226 got.RecoveryReason != existing.RecoveryReason {
227 t.Fatalf("pinning dropped existing fields: got %+v, existing %+v", got, existing)
228 }
229 }
230
230 lines GO