返回 DeepSeek-Reasonix
session_temp_rebuild_test.go
根目录 / desktop / session_temp_rebuild_test.go
1 package main
2
3 import (
4 "os"
5 "path/filepath"
6 "testing"
7
8 "reasonix/internal/control"
9 )
10
11 // TestDesktopHotRebuildPathsKeepSessionTemp covers the same-session rebuilds
12 // that use boot.Build directly (not boot.Rebuild): model and effort switches.
13 // SetTokenMode is a deprecated no-op and must keep the same controller and
14 // SessionTemp. Each rebuild must pass the old Controller's SessionTemp so
15 // temporary files survive (Issue #7575).
16 func TestDesktopHotRebuildPathsKeepSessionTemp(t *testing.T) {
17 for _, tc := range []struct {
18 name string
19 prepare func(*App, *WorkspaceTab)
20 rebuild func(*App, *WorkspaceTab) error
21 // inPlace means the switch must keep the same *control.Controller.
22 inPlace bool
23 }{
24 {
25 name: "model",
26 prepare: func(*App, *WorkspaceTab) {},
27 rebuild: func(app *App, tab *WorkspaceTab) error {
28 return app.SetModelForTab(tab.ID, "alt/alt-model")
29 },
30 },
31 {
32 name: "effort",
33 prepare: func(*App, *WorkspaceTab) {},
34 rebuild: func(app *App, tab *WorkspaceTab) error {
35 return app.SetEffortForTab(tab.ID, "high")
36 },
37 },
38 {
39 name: "token mode",
40 prepare: func(*App, *WorkspaceTab) {},
41 rebuild: func(app *App, tab *WorkspaceTab) error {
42 return app.SetTokenModeForTab(tab.ID, "delivery")
43 },
44 inPlace: true,
45 },
46 } {
47 t.Run(tc.name, func(t *testing.T) {
48 app, tab, oldAPI, _ := newGoalDeliveryYoloTestApp(t, control.GoalStatusRunning)
49 oldCtrl, ok := oldAPI.(*control.Controller)
50 if !ok || oldCtrl == nil {
51 t.Fatal("old controller is not *control.Controller")
52 }
53 tc.prepare(app, tab)
54
55 // Plant a file in the session-private temporary directory.
56 lease, err := oldCtrl.SessionTemp().Acquire()
57 if err != nil {
58 t.Fatalf("acquire old session temp: %v", err)
59 }
60 oldMgr := oldCtrl.SessionTemp()
61 oldDir := lease.Dir()
62 marker := filepath.Join(oldDir, "desktop-hot-rebuild.txt")
63 if err := os.WriteFile(marker, []byte(tc.name), 0o600); err != nil {
64 t.Fatal(err)
65 }
66 lease.Release()
67
68 if err := tc.rebuild(app, tab); err != nil {
69 t.Fatalf("rebuild: %v", err)
70 }
71
72 newAPI := app.controllerForTab(tab)
73 newCtrl, ok := newAPI.(*control.Controller)
74 if !ok || newCtrl == nil {
75 t.Fatal("controller missing after switch")
76 }
77 if tc.inPlace {
78 if newCtrl != oldCtrl {
79 t.Fatal("SetTokenMode must not replace the controller")
80 }
81 } else if newCtrl == oldCtrl {
82 t.Fatal("rebuild did not install a replacement *control.Controller")
83 }
84 if newCtrl.SessionTemp() != oldMgr {
85 t.Fatalf("%s did not keep SessionTemp Manager (got %p want %p)",
86 tc.name, newCtrl.SessionTemp(), oldMgr)
87 }
88 if oldMgr.Sealed() {
89 t.Fatal("shared manager sealed after hot rebuild (replacement must retain first)")
90 }
91
92 again, err := newCtrl.SessionTemp().Acquire()
93 if err != nil {
94 t.Fatalf("acquire after rebuild: %v", err)
95 }
96 defer again.Release()
97 if again.Dir() != oldDir {
98 t.Fatalf("%s rebuild rotated temporary generation: got %q want %q",
99 tc.name, again.Dir(), oldDir)
100 }
101 body, err := os.ReadFile(marker)
102 if err != nil || string(body) != tc.name {
103 t.Fatalf("temp file lost across %s rebuild: %q %v", tc.name, body, err)
104 }
105 })
106 }
107 }
108
109 func TestSessionTempFromControllerHelper(t *testing.T) {
110 if sessionTempFromController(nil) != nil {
111 t.Fatal("nil controller should yield nil SessionTemp")
112 }
113 ctrl := control.New(control.Options{})
114 defer ctrl.Close()
115 if sessionTempFromController(ctrl) == nil {
116 t.Fatal("want non-nil SessionTemp from live controller")
117 }
118 if sessionTempFromController(ctrl) != ctrl.SessionTemp() {
119 t.Fatal("helper must return the controller's Manager")
120 }
121 }
122
122 lines GO