返回 DeepSeek-Reasonix
session_temp_rebuild_test.go
根目录 / internal / cli / session_temp_rebuild_test.go
1 package cli
2
3 import (
4 "context"
5 "os"
6 "path/filepath"
7 "testing"
8
9 "reasonix/internal/boot"
10 "reasonix/internal/control"
11 "reasonix/internal/provider"
12 )
13
14 // TestCLIHotRebuildPathsKeepSessionTemp covers the CLI commands that replace a
15 // Controller without changing the logical session. Every path must keep the
16 // Manager identity, current generation, and files even after the outgoing
17 // Controller releases its owner reference.
18 func TestCLIHotRebuildPathsKeepSessionTemp(t *testing.T) {
19 for _, command := range []string{"model", "effort", "reload"} {
20 t.Run(command, func(t *testing.T) {
21 isolateUserConfig(t)
22
23 oldCtrl := control.New(control.Options{Label: "deepseek-flash"})
24 t.Cleanup(oldCtrl.Close)
25 oldManager := oldCtrl.SessionTemp()
26 lease, err := oldManager.Acquire()
27 if err != nil {
28 t.Fatalf("acquire old session temp: %v", err)
29 }
30 oldDir := lease.Dir()
31 marker := filepath.Join(oldDir, "cli-hot-rebuild.txt")
32 if err := os.WriteFile(marker, []byte(command), 0o600); err != nil {
33 lease.Release()
34 t.Fatal(err)
35 }
36 lease.Release()
37
38 m := newTestChatTUI()
39 m.ctrl = oldCtrl
40 m.modelRef = "deepseek-flash/deepseek-v4-flash"
41 m.runtimeProfile = boot.TokenModeFull
42 m.buildController = func(_ controllerBuildSpec, _ []provider.Message, _ string, outgoing control.SessionAPI) (*control.Controller, error) {
43 return control.New(control.Options{
44 Label: "deepseek-flash",
45 SessionTemp: sessionTempFromCLIController(outgoing),
46 }), nil
47 }
48 m.rebuildRuntime = func(_ context.Context, _ controllerBuildSpec, outgoing *control.Controller) (*boot.BuildResult, error) {
49 // Production delegates this path to boot.Rebuild, whose owning test
50 // pins the same SessionTemp transfer. This seam exercises the CLI
51 // command/swap lifecycle without booting providers or plugins.
52 return &boot.BuildResult{Controller: control.New(control.Options{
53 Label: "deepseek-flash",
54 SessionTemp: outgoing.SessionTemp(),
55 })}, nil
56 }
57
58 switch command {
59 case "model":
60 m.runModelSubcommand("/model deepseek-flash/another-model")
61 if m.pendingModelSwitch == nil {
62 t.Fatal("/model did not schedule a replacement")
63 }
64 msg := m.pendingModelSwitch()
65 next, _ := m.Update(msg)
66 m = next.(chatTUI)
67 case "effort":
68 effortCmd := m.runEffortCommand("/effort max")
69 if effortCmd == nil {
70 t.Fatal("/effort did not schedule a replacement")
71 }
72 next, _ := m.Update(effortCmd())
73 m = next.(chatTUI)
74 case "reload":
75 reloadCmd := m.runReloadCommand()
76 if reloadCmd == nil {
77 t.Fatal("/reload did not schedule a replacement")
78 }
79 next, _ := m.Update(reloadCmd())
80 m = next.(chatTUI)
81 }
82 newCtrl, ok := m.ctrl.(*control.Controller)
83 if !ok || newCtrl == nil || newCtrl == oldCtrl {
84 t.Fatal("CLI rebuild did not install a replacement Controller")
85 }
86 t.Cleanup(newCtrl.Close)
87 if newCtrl.SessionTemp() != oldManager {
88 t.Fatalf("%s rebuild changed SessionTemp Manager: got %p want %p", command, newCtrl.SessionTemp(), oldManager)
89 }
90
91 // Match the TUI's deferred retirement: the replacement must already
92 // own the shared Manager before the outgoing Controller releases it.
93 oldCtrl.ReleaseResources()
94 if oldManager.Sealed() {
95 t.Fatalf("%s rebuild sealed the shared Manager after old release", command)
96 }
97 again, err := newCtrl.SessionTemp().Acquire()
98 if err != nil {
99 t.Fatalf("acquire after %s rebuild: %v", command, err)
100 }
101 if again.Dir() != oldDir {
102 again.Release()
103 t.Fatalf("%s rebuild rotated temp generation: got %q want %q", command, again.Dir(), oldDir)
104 }
105 body, err := os.ReadFile(marker)
106 again.Release()
107 if err != nil || string(body) != command {
108 t.Fatalf("temp file lost across %s rebuild: %q %v", command, body, err)
109 }
110 })
111 }
112 }
113
114 func TestSessionTempFromCLIControllerHelper(t *testing.T) {
115 if sessionTempFromCLIController(nil) != nil {
116 t.Fatal("nil controller should yield nil SessionTemp")
117 }
118 ctrl := control.New(control.Options{})
119 defer ctrl.Close()
120 if got := sessionTempFromCLIController(ctrl); got == nil || got != ctrl.SessionTemp() {
121 t.Fatal("helper did not return the live Controller's SessionTemp")
122 }
123 }
124
124 lines GO