返回 DeepSeek-Reasonix
workspace_removal_protocol_test.go
根目录 / desktop / workspace_removal_protocol_test.go
1 package main
2
3 import (
4 "context"
5 "errors"
6 "os"
7 "path/filepath"
8 "reflect"
9 "testing"
10
11 "reasonix/internal/agent"
12 "reasonix/internal/control"
13 "reasonix/internal/provider"
14 "reasonix/internal/session"
15 )
16
17 // Removing a project can select a surviving historical JSONL tab. Opening a
18 // canonical session must replace that native runtime, not require the old
19 // runtime to already use the destination session protocol.
20 func TestOpenCanonicalSessionAfterWorkspaceRemovalFromNativeSurface(t *testing.T) {
21 for _, removal := range []string{"none", "current-project", "other-project", "cancelled-replacement"} {
22 t.Run(removal, func(t *testing.T) {
23 isolateDesktopUserDirs(t)
24 configureSwitchableDefaultModels(t)
25 app := NewApp()
26 app.ctx = t.Context()
27 app.readyHook = func() {}
28 installNoopRuntimeEvents(app)
29 t.Cleanup(func() { app.shutdown(context.Background()) })
30
31 root := canonicalRuntimeRoot(t.TempDir())
32 if err := addProject(root, "Historical"); err != nil {
33 t.Fatal(err)
34 }
35 dir := desktopSessionDir(root)
36 if err := os.MkdirAll(dir, 0o700); err != nil {
37 t.Fatal(err)
38 }
39 path := filepath.Join(dir, "historical.jsonl")
40 loaded := agent.NewSession("system")
41 loaded.Add(provider.Message{Role: provider.RoleUser, Content: "preserved historical message"})
42 if err := loaded.Save(path); err != nil {
43 t.Fatal(err)
44 }
45 source := &SessionSourceRef{HostID: localDesktopHostID, Path: path, HeadID: agent.SessionMainHead}
46 meta, err := app.OpenTopicSession("project", root, "historical", nativeSessionSourceRoute(source))
47 if err != nil {
48 t.Fatal(err)
49 }
50 native := waitForTabReady(t, app, meta.ID)
51 ctrl, ok := native.Ctrl.(*control.Controller)
52 if !ok || !ctrl.NativeLegacySession() || ctrl.UsesExclusiveSession() {
53 t.Fatal("fixture did not open a native historical JSONL runtime")
54 }
55 otherRoot := canonicalRuntimeRoot(t.TempDir())
56 service := app.desktopSessionService("")
57 target, err := service.Create(t.Context(), session.CreateOptions{SessionID: "remaining-session", CWD: otherRoot, Origin: session.SessionOriginNew})
58 if err != nil {
59 t.Fatal(err)
60 }
61 ref := target.Ref()
62 if _, err := app.attachDesktopSession(t.Context(), "project", otherRoot, ref); err != nil {
63 t.Fatal(err)
64 }
65 if err := service.Close(t.Context(), ref); err != nil {
66 t.Fatal(err)
67 }
68
69 if removal == "current-project" || removal == "other-project" {
70 removedRoot := canonicalRuntimeRoot(t.TempDir())
71 if err := addProject(removedRoot, filepath.Base(removedRoot)); err != nil {
72 t.Fatal(err)
73 }
74 if _, err := app.ensureDesktopWorkspace(t.Context(), "project", removedRoot); err != nil {
75 t.Fatal(err)
76 }
77 app.mu.Lock()
78 removed := app.createTabEntry("project", removedRoot, "")
79 app.tabs[removed.ID] = removed
80 app.tabOrder = append(app.tabOrder, removed.ID)
81 if removal == "current-project" {
82 app.activeTabID = removed.ID
83 }
84 app.mu.Unlock()
85 if err := app.RemoveWorkspace(removedRoot); err != nil {
86 t.Fatal(err)
87 }
88 }
89
90 if removal == "cancelled-replacement" {
91 history := ctrl.History()
92 built := false
93 app.sessionOpenBuildHook = func(context.Context) {
94 built = true
95 app.cancelSessionNavigation()
96 }
97 _, err := app.OpenSession(ref)
98 if !built || !errors.Is(err, context.Canceled) {
99 t.Fatalf("expected cancellation during destination build, built=%v err=%v", built, err)
100 }
101 if app.controllerForTab(native) != ctrl || !reflect.DeepEqual(ctrl.History(), history) {
102 t.Fatal("failed replacement changed the original controller or history")
103 }
104 select {
105 case <-ctrl.Closed():
106 t.Fatal("failed replacement closed the native source controller")
107 default:
108 }
109 return
110 }
111 if _, err := app.OpenSession(ref); err != nil {
112 t.Fatalf("open remaining project's canonical session from native surface: %v", err)
113 }
114 app.mu.RLock()
115 active := app.activeTabLocked()
116 app.mu.RUnlock()
117 if active == nil || active.SessionID != ref.SessionID || !sameDesktopPath(active.WorkspaceRoot, otherRoot) {
118 t.Fatal("opening did not commit the selected session and workspace")
119 }
120 preserved, err := agent.LoadSession(path)
121 if err != nil {
122 t.Fatalf("navigation lost historical source: %v", err)
123 }
124 found := false
125 for _, message := range preserved.Snapshot() {
126 found = found || message.Content == "preserved historical message"
127 }
128 if !found {
129 t.Fatal("navigation lost the historical conversation")
130 }
131 })
132 }
133 }
134
135 func TestCanonicalOpenStillRejectsUnsupportedRuntime(t *testing.T) {
136 if _, err := canonicalOpenIdentity(&stubSessionAPI{}); err == nil {
137 t.Fatal("an unsupported runtime passed the identity protocol check")
138 }
139 }
140
140 lines GO