返回 DeepSeek-Reasonix
legacy_tab_scope_test.go
根目录 / desktop / legacy_tab_scope_test.go
1 package main
2
3 import (
4 "bytes"
5 "errors"
6 "os"
7 "path/filepath"
8 "testing"
9
10 "reasonix/desktop/internal/workspacestate"
11 "reasonix/internal/agent"
12 "reasonix/internal/provider"
13 )
14
15 func legacyTabScopeFixture(t *testing.T, register bool) (app *App, projectRoot, legacyPath string) {
16 t.Helper()
17 isolateDesktopUserDirs(t)
18 projectRoot = robustTempDir(t)
19 if register {
20 if err := addProject(projectRoot, "Legacy project"); err != nil {
21 t.Fatal(err)
22 }
23 }
24 dir := desktopSessionDir(projectRoot)
25 if err := os.MkdirAll(dir, 0o755); err != nil {
26 t.Fatal(err)
27 }
28 legacyPath = filepath.Join(dir, "legacy.jsonl")
29 legacy := agent.NewSession("system")
30 legacy.Add(provider.Message{ID: "user", Role: provider.RoleUser, Content: "legacy content"})
31 if err := legacy.Save(legacyPath); err != nil {
32 t.Fatal(err)
33 }
34 if err := agent.SaveBranchMetaPreserveUpdated(legacyPath, agent.BranchMeta{Scope: "project", WorkspaceRoot: projectRoot}); err != nil {
35 t.Fatal(err)
36 }
37 entry := desktopTabEntry{ID: "tab", Scope: "global", TopicID: "topic", SessionPath: legacyPath}
38 file := desktopTabsFile{Tabs: []desktopTabEntry{entry}, ActiveTab: entry.ID, TabOrder: []string{entry.ID}}
39 if err := os.MkdirAll(desktopConfigDir(), 0o700); err != nil {
40 t.Fatal(err)
41 }
42 if err := os.WriteFile(filepath.Join(desktopConfigDir(), tabsFileName), mustMarshalJSON(t, file), 0o600); err != nil {
43 t.Fatal(err)
44 }
45 app = NewApp()
46 t.Cleanup(app.closeSessionServices)
47 app.desktopSessions.root = filepath.Join(t.TempDir(), "by-id")
48 app.desktopSessions.workspaceState = workspacestate.NewStore(filepath.Join(t.TempDir(), "workspace-state-v1.json"))
49 return app, projectRoot, legacyPath
50 }
51
52 func TestTabDerivedLegacySourceTakesScopeFromProjectDirectory(t *testing.T) {
53 app, projectRoot, legacyPath := legacyTabScopeFixture(t, true)
54 _, legacy := app.desktopHistoricalRoots()
55 source, ok := legacy[canonicalRuntimeRoot(filepath.Dir(legacyPath))]
56 if !ok {
57 t.Fatalf("project session directory not discovered: %#v", legacy)
58 }
59 if source.scope != "project" || !sameDesktopPath(source.workspaceRoot, projectRoot) {
60 t.Fatalf("source scope = %q root = %q, want project %q", source.scope, source.workspaceRoot, projectRoot)
61 }
62 if err := app.migrateLegacyDirectory(t.Context(), source); errors.Is(err, errSessionWorkspaceConflict) {
63 t.Fatalf("session of a registered project conflicted: %v", err)
64 } else if err != nil {
65 t.Fatal(err)
66 }
67 }
68
69 func TestTabDerivedLegacySourceOfUnregisteredProjectMigratesOnce(t *testing.T) {
70 app, projectRoot, legacyPath := legacyTabScopeFixture(t, false)
71 before, err := os.ReadFile(legacyPath)
72 if err != nil {
73 t.Fatal(err)
74 }
75 _, legacy := app.desktopHistoricalRoots()
76 source := legacy[canonicalRuntimeRoot(filepath.Dir(legacyPath))]
77 if source.scope != "project" || !sameDesktopPath(source.workspaceRoot, projectRoot) {
78 t.Fatalf("source scope = %q root = %q, want project %q", source.scope, source.workspaceRoot, projectRoot)
79 }
80 for range 2 {
81 if err := app.migrateLegacyDirectory(t.Context(), source); err != nil {
82 t.Fatalf("migration of an unregistered project's session: %v", err)
83 }
84 }
85 state, err := app.desktopSessions.workspaceState.Load(t.Context())
86 if err != nil || len(state.SourceMappings) != 1 {
87 t.Fatalf("re-run duplicated or lost the target: %+v, %v", state.SourceMappings, err)
88 }
89 after, err := os.ReadFile(legacyPath)
90 if err != nil || !bytes.Equal(before, after) {
91 t.Fatalf("legacy transcript changed: %v", err)
92 }
93 }
94
95 func TestTabDerivedLegacySourceStillReportsGenuineWorkspaceConflict(t *testing.T) {
96 for _, register := range []bool{true, false} {
97 app, _, legacyPath := legacyTabScopeFixture(t, register)
98 elsewhere := robustTempDir(t)
99 if err := agent.SaveBranchMetaPreserveUpdated(legacyPath, agent.BranchMeta{Scope: "project", WorkspaceRoot: elsewhere}); err != nil {
100 t.Fatal(err)
101 }
102 _, legacy := app.desktopHistoricalRoots()
103 source := legacy[canonicalRuntimeRoot(filepath.Dir(legacyPath))]
104 if err := app.migrateLegacyDirectory(t.Context(), source); !errors.Is(err, errSessionWorkspaceConflict) {
105 t.Fatalf("registered=%v: err = %v, want workspace conflict", register, err)
106 }
107 }
108 }
109
110 func TestHistoricalImportRerunRetriesFailedSourceOnce(t *testing.T) {
111 app, projectRoot, legacyPath := legacyTabScopeFixture(t, true)
112 app.ctx = t.Context()
113 installNoopRuntimeEvents(app)
114 t.Cleanup(app.stopHistoricalImports)
115 elsewhere := robustTempDir(t)
116 if err := agent.SaveBranchMetaPreserveUpdated(legacyPath, agent.BranchMeta{Scope: "project", WorkspaceRoot: elsewhere}); err != nil {
117 t.Fatal(err)
118 }
119 if _, err := app.ListHistoricalSessions(); err != nil {
120 t.Fatal(err)
121 }
122 if _, err := app.StartHistoricalImport(nil); err != nil {
123 t.Fatal(err)
124 }
125 status := awaitHistoricalBatch(t, app)
126 if len(status.Items) != 1 || status.Items[0].Status != "failed" || status.Items[0].ErrorCode != "workspace_conflict" {
127 t.Fatalf("first run did not fail with the conflict: %+v", status)
128 }
129 if err := agent.SaveBranchMetaPreserveUpdated(legacyPath, agent.BranchMeta{Scope: "project", WorkspaceRoot: projectRoot}); err != nil {
130 t.Fatal(err)
131 }
132 for range 2 {
133 if _, err := app.StartHistoricalImport(nil); err != nil {
134 t.Fatal(err)
135 }
136 status = awaitHistoricalBatch(t, app)
137 }
138 if len(status.Items) != 1 || status.Items[0].Status != "imported" {
139 t.Fatalf("failed source was not attempted again: %+v", status)
140 }
141 state, err := app.workspaceRegistry().Load(t.Context())
142 if err != nil || len(state.SourceMappings) != 1 {
143 t.Fatalf("re-import duplicated the target: %+v, %v", state.SourceMappings, err)
144 }
145 }
146
146 lines GO