返回 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 := newOwnedTestController(t, 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.buildController = func(_ controllerBuildSpec, _ []provider.Message, _ string, outgoing control.SessionAPI) (*control.Controller, error) {
42 return newOwnedTestController(t, control.Options{
43 Label: "deepseek-flash",
44 SessionTemp: sessionTempFromCLIController(outgoing),
45 }), nil
46 }
47 m.rebuildRuntime = func(_ context.Context, _ controllerBuildSpec, outgoing *control.Controller) (*boot.BuildResult, error) {
48 // Production delegates this path to boot.Rebuild, whose owning test
49 // pins the same SessionTemp transfer. This seam exercises the CLI
50 // command/swap lifecycle without booting providers or plugins.
51 return &boot.BuildResult{Controller: newOwnedTestController(t, control.Options{
52 Label: "deepseek-flash",
53 SessionTemp: outgoing.SessionTemp(),
54 })}, nil
55 }
56
57 switch command {
58 case "model":
59 m.runModelSubcommand("/model deepseek-flash/another-model")
60 if m.pendingModelSwitch == nil {
61 t.Fatal("/model did not schedule a replacement")
62 }
63 msg := m.pendingModelSwitch()
64 next, _ := m.Update(msg)
65 m = next.(chatTUI)
66 case "effort":
67 effortCmd := m.runEffortCommand("/effort max")
68 if effortCmd == nil {
69 t.Fatal("/effort did not schedule a replacement")
70 }
71 next, _ := m.Update(effortCmd())
72 m = next.(chatTUI)
73 case "reload":
74 reloadCmd := m.runReloadCommand()
75 if reloadCmd == nil {
76 t.Fatal("/reload did not schedule a replacement")
77 }
78 next, _ := m.Update(reloadCmd())
79 m = next.(chatTUI)
80 }
81 newCtrl, ok := m.ctrl.(*control.Controller)
82 if !ok || newCtrl == nil || newCtrl == oldCtrl {
83 t.Fatal("CLI rebuild did not install a replacement Controller")
84 }
85 t.Cleanup(newCtrl.Close)
86 if newCtrl.SessionTemp() != oldManager {
87 t.Fatalf("%s rebuild changed SessionTemp Manager: got %p want %p", command, newCtrl.SessionTemp(), oldManager)
88 }
89
90 // Match the TUI's deferred retirement: the replacement must already
91 // own the shared Manager before the outgoing Controller releases it.
92 oldCtrl.ReleaseResources()
93 if oldManager.Sealed() {
94 t.Fatalf("%s rebuild sealed the shared Manager after old release", command)
95 }
96 again, err := newCtrl.SessionTemp().Acquire()
97 if err != nil {
98 t.Fatalf("acquire after %s rebuild: %v", command, err)
99 }
100 if again.Dir() != oldDir {
101 again.Release()
102 t.Fatalf("%s rebuild rotated temp generation: got %q want %q", command, again.Dir(), oldDir)
103 }
104 body, err := os.ReadFile(marker)
105 again.Release()
106 if err != nil || string(body) != command {
107 t.Fatalf("temp file lost across %s rebuild: %q %v", command, body, err)
108 }
109 })
110 }
111 }
112
113 func TestSessionTempFromCLIControllerHelper(t *testing.T) {
114 if sessionTempFromCLIController(nil) != nil {
115 t.Fatal("nil controller should yield nil SessionTemp")
116 }
117 ctrl := newOwnedTestController(t, control.Options{})
118 defer ctrl.Close()
119 if got := sessionTempFromCLIController(ctrl); got == nil || got != ctrl.SessionTemp() {
120 t.Fatal("helper did not return the live Controller's SessionTemp")
121 }
122 }
123
123 lines GO