返回 DeepSeek-Reasonix
session_archive_foreign_workspace_test.go
根目录 / desktop / session_archive_foreign_workspace_test.go
1 package main
2
3 import (
4 "context"
5 "errors"
6 "os"
7 "path/filepath"
8 "testing"
9 "time"
10
11 "reasonix/internal/agent"
12 "reasonix/internal/provider"
13 "reasonix/internal/sessioncatalog"
14 )
15
16 // A save event indexes a transcript under the global fallback scope when its
17 // sidecar names none, while archive assigns the owner from the project store
18 // the file lives in. The row must leave the sidebar it was listed in anyway.
19 func TestArchivedSourceOwnedByAnotherWorkspaceLeavesSidebar(t *testing.T) {
20 for _, heads := range []int{1, 2} {
21 name := "single-head"
22 if heads == 2 {
23 name = "branched"
24 }
25 t.Run(name, func(t *testing.T) {
26 isolateDesktopUserDirs(t)
27 workspace := filepath.Join(t.TempDir(), "project")
28 if err := os.MkdirAll(workspace, 0o755); err != nil {
29 t.Fatal(err)
30 }
31 if err := saveProjectsFile(desktopProjectFile{Projects: []desktopProject{{Root: workspace}}}); err != nil {
32 t.Fatal(err)
33 }
34 dir := desktopSessionDir(workspace)
35 path := filepath.Join(dir, "20260922-122527.100224400-deepseek-chat.jsonl")
36 legacy := agent.NewSession("system")
37 legacy.Add(provider.Message{ID: "q0", Role: provider.RoleUser, Content: "question"})
38 legacy.Add(provider.Message{ID: "a0", Role: provider.RoleAssistant, Content: "answer"})
39 if err := legacy.Save(path); err != nil {
40 t.Fatal(err)
41 }
42 if heads == 2 {
43 if _, err := legacy.ForkHead(path, legacy.Snapshot()[1].ID, agent.HeadKindFork, "sibling"); err != nil {
44 t.Fatal(err)
45 }
46 legacy.Add(provider.Message{Role: provider.RoleAssistant, Content: "sibling answer"})
47 if err := legacy.Save(path); err != nil {
48 t.Fatal(err)
49 }
50 }
51 if err := agent.SaveBranchMeta(path, agent.BranchMeta{TopicID: "topic_unscoped"}); err != nil {
52 t.Fatal(err)
53 }
54 app := NewApp()
55 app.ctx = t.Context()
56 pinDesktopSessionRoot(t, app)
57 installNoopRuntimeEvents(app)
58 t.Cleanup(app.closeSessionServices)
59 catalog, err := sessioncatalog.Open(context.Background(), sessioncatalog.Options{InMemory: true, DisableRepair: true, MetadataOnly: true})
60 if err != nil {
61 t.Fatal(err)
62 }
63 app.sessionCatalog.Store(catalog)
64 t.Cleanup(func() { app.stopSessionCatalog(time.Second) })
65 globalRows := func() []ProjectNode {
66 t.Helper()
67 // The desktop's persist observer indexes each save under this fallback.
68 if err := catalog.IndexSessionPath(context.Background(), sessioncatalog.DirectoryTarget{Path: dir, Scope: "global"}, path); err != nil {
69 t.Fatal(err)
70 }
71 page, err := app.ListProjectTopics(ProjectTopicPageRequest{Scope: "global", Limit: 50})
72 if err != nil {
73 t.Fatal(err)
74 }
75 app.ReleaseReadSnapshot(page.SnapshotID)
76 return page.Items
77 }
78 rows := globalRows()
79 if len(rows) != heads {
80 t.Fatalf("global rows before archive = %+v, want %d", rows, heads)
81 }
82 for _, row := range rows {
83 result, err := app.ArchiveSessionTarget(SessionSelector{Ref: row.Session, Source: row.Source, SessionPath: row.SessionPath})
84 if err != nil || !result.Committed {
85 t.Fatalf("archive %+v = %+v, %v", row.Source, result, err)
86 }
87 }
88 if left := globalRows(); len(left) != 0 {
89 var operationErr *SessionOperationError
90 err := app.SetSessionPinned(SessionSelector{Source: left[0].Source, SessionPath: left[0].SessionPath}, true)
91 if errors.As(err, &operationErr) {
92 t.Fatalf("archived rows stay listed (%d) and refuse every action with %q", len(left), operationErr.Code)
93 }
94 t.Fatalf("archived rows stay listed: %+v", left)
95 }
96 })
97 }
98 }
99
99 lines GO