返回 DeepSeek-Reasonix
session_open_surface_test.go
根目录 / desktop / session_open_surface_test.go
1 package main
2
3 import (
4 "testing"
5
6 "reasonix/internal/session"
7 )
8
9 // removeOpenFixtureTab leaves the app with no visible surface, the state after
10 // the last session was archived or before the first one was ever opened.
11 func removeOpenFixtureTab(t *testing.T, app *App, tab *WorkspaceTab) {
12 t.Helper()
13 app.mu.Lock()
14 app.markTabRemovedLocked(tab)
15 app.releaseSessionRuntimeLocked(tab)
16 delete(app.tabs, tab.ID)
17 app.removeTabOrderLocked(tab.ID)
18 app.activeTabID = ""
19 app.mu.Unlock()
20 if tab.Ctrl != nil {
21 tab.Ctrl.Close()
22 tab.Ctrl = nil
23 }
24 }
25
26 // Opening a session from the sidebar or from an accepted draft must work when
27 // no surface exists; it used to fail with "workspace is not ready" and relied
28 // on a replacement blank session being present.
29 func TestOpenSessionWithoutSurfaceCreatesOneForTheSession(t *testing.T) {
30 app, tab, target, rootB, workspaceB := canonicalWorkspaceOpenFixture(t)
31 removeOpenFixtureTab(t, app, tab)
32
33 if _, err := app.OpenSession(target.Ref()); err != nil {
34 t.Fatalf("OpenSession without a surface: %v", err)
35 }
36 app.mu.RLock()
37 active := app.tabs[app.activeTabID]
38 count := len(app.tabs)
39 app.mu.RUnlock()
40 if active == nil || count != 1 {
41 t.Fatalf("expected exactly one active surface, got active=%v tabs=%d", active != nil, count)
42 }
43 t.Cleanup(func() {
44 if live := app.controllerForTab(active); live != nil {
45 live.Close()
46 }
47 })
48 if active.SessionID != target.Ref().SessionID || !active.Ready || active.StartupErr != "" || !sameDesktopPath(active.WorkspaceRoot, rootB) || active.SessionWorkspace.ID != workspaceB {
49 t.Fatalf("surface bound wrong session: %+v", persistedDesktopTabEntry(active))
50 }
51 persisted := loadTabsFile()
52 if len(persisted.Tabs) != 1 || persisted.Tabs[0].SessionID != target.Ref().SessionID || persisted.ActiveTab != active.ID {
53 t.Fatalf("surface not persisted as active: %+v", persisted)
54 }
55 }
56
57 // A session that already runs in a visible but inactive tab (the tab a draft
58 // submission created in the background) is activated in place instead of being
59 // rebuilt or rejected.
60 func TestOpenSessionActivatesVisibleTabAlreadyRunningTheSession(t *testing.T) {
61 app, tab, _, _, _ := canonicalWorkspaceOpenFixture(t)
62 ctrl := tab.Ctrl
63 app.mu.Lock()
64 app.activeTabID = ""
65 app.mu.Unlock()
66
67 if _, err := app.OpenSession(session.SessionRef{HostID: localDesktopHostID, SessionID: tab.SessionID}); err != nil {
68 t.Fatalf("OpenSession: %v", err)
69 }
70 app.mu.RLock()
71 activeID, count := app.activeTabID, len(app.tabs)
72 app.mu.RUnlock()
73 if activeID != tab.ID || count != 1 || tab.Ctrl != ctrl {
74 t.Fatalf("owner tab not activated in place: active=%q tabs=%d sameCtrl=%v", activeID, count, tab.Ctrl == ctrl)
75 }
76 }
77
78 // A surface created for an open that then fails must not linger as an empty
79 // tab; that would be the replacement blank session in another form.
80 func TestDiscardUnboundSurfaceRemovesOnlyTheEmptyTab(t *testing.T) {
81 app, tab, target, _, _ := canonicalWorkspaceOpenFixture(t)
82 removeOpenFixtureTab(t, app, tab)
83 workspace, err := app.canonicalSessionWorkspace(t.Context(), target.Ref())
84 if err != nil {
85 t.Fatal(err)
86 }
87 created, ctrl, isNew, err := app.surfaceForCanonicalSession(target.Ref(), workspace)
88 if err != nil || ctrl != nil || !isNew {
89 t.Fatalf("surfaceForCanonicalSession = %+v, %v, new=%v, %v", created, ctrl, isNew, err)
90 }
91 app.mu.RLock()
92 activeID := app.activeTabID
93 app.mu.RUnlock()
94 if activeID != created.ID {
95 t.Fatalf("created surface is not active: %q", activeID)
96 }
97 app.discardUnboundSurface(created)
98 app.mu.RLock()
99 defer app.mu.RUnlock()
100 if len(app.tabs) != 0 || len(app.tabOrder) != 0 || app.activeTabID != "" {
101 t.Fatalf("unbound surface lingered: tabs=%d order=%v active=%q", len(app.tabs), app.tabOrder, app.activeTabID)
102 }
103 }
104
104 lines GO