返回 DeepSeek-Reasonix
plan_test.go
根目录 / internal / extension / sidecar / plan_test.go
1 package sidecar
2
3 import (
4 "testing"
5
6 "reasonix/internal/extension"
7 )
8
9 func TestPluginComponentIDRoundTrip(t *testing.T) {
10 id := PluginComponentID("demo")
11 if id != "plugin/demo" {
12 t.Fatalf("id = %q", id)
13 }
14 if got := PluginNameFromComponentID(id); got != "demo" {
15 t.Fatalf("name = %q", got)
16 }
17 if PluginNameFromComponentID("host") != "" {
18 t.Fatal("host should not map to a plugin name")
19 }
20 }
21
22 func TestManagerDetachAdopt(t *testing.T) {
23 m := &Manager{clients: map[string]*Client{}}
24 c1 := &Client{pluginID: "a"}
25 c2 := &Client{pluginID: "b"}
26 m.clients["a"] = c1
27 m.clients["b"] = c2
28
29 detached := m.Detach("a")
30 if detached != c1 || m.Client("a") != nil {
31 t.Fatal("detach failed")
32 }
33 next := &Manager{clients: map[string]*Client{}}
34 if err := next.Adopt("a", detached); err != nil {
35 t.Fatal(err)
36 }
37 if next.Client("a") != c1 {
38 t.Fatal("adopt failed")
39 }
40 // Detach b without Close (incomplete Client must not be closed).
41 if got := m.Detach("b"); got != c2 || m.Client("b") != nil {
42 t.Fatal("detach b failed")
43 }
44 if err := next.Adopt("a", c2); err == nil {
45 t.Fatal("duplicate adopt should fail")
46 }
47 }
48
49 func TestRollbackPlanStartReattachesOnlyAdopted(t *testing.T) {
50 // previous holds old "reloaded" client; new manager has fresh "reloaded" + adopted "keep".
51 prev := &Manager{clients: map[string]*Client{}}
52 // Use real clients only if we can construct them cheaply — use Detach/Adopt unit path.
53 keep := &Client{pluginID: "keep"}
54 oldReloaded := &Client{pluginID: "reloaded"}
55 prev.clients["keep"] = keep
56 prev.clients["reloaded"] = oldReloaded
57
58 next := &Manager{clients: map[string]*Client{}, planAdopted: map[string]*Client{}}
59 // Simulate StartPackagesWithPlan: detach keep, adopt into next; start fresh reloaded.
60 if c := prev.Detach("keep"); c != nil {
61 _ = next.Adopt("keep", c)
62 next.planAdopted["keep"] = c
63 }
64 fresh := &Client{pluginID: "reloaded"}
65 _ = next.Adopt("reloaded", fresh)
66
67 next.RollbackPlanStart(prev)
68 if prev.Client("keep") == nil {
69 t.Fatal("unchanged client must be reattached to previous")
70 }
71 if prev.Client("reloaded") != oldReloaded {
72 t.Fatal("previous must still hold old reloaded client")
73 }
74 // next should be closed/empty of keep; fresh reloaded closed via Close.
75 if next.Client("keep") != nil || next.Client("reloaded") != nil {
76 t.Fatal("next manager must not retain clients after rollback")
77 }
78 }
79
80 func TestDrainPlanSelectsIDs(t *testing.T) {
81 m := &Manager{clients: map[string]*Client{
82 "gone": {pluginID: "gone"},
83 "a": {pluginID: "a"},
84 "keep": {pluginID: "keep"},
85 }}
86 plan := &extension.RuntimePlan{
87 Removed: []extension.ComponentID{"plugin/gone"},
88 Reloaded: []extension.ComponentID{"plugin/a"},
89 }
90 // DrainPlan closes clients; incomplete Clients panic on Close, so only
91 // Detach the matching IDs to verify selection logic.
92 var drained []string
93 for _, id := range append(append([]extension.ComponentID{}, plan.Removed...), plan.Reloaded...) {
94 if name := PluginNameFromComponentID(id); name != "" {
95 if c := m.Detach(name); c != nil {
96 drained = append(drained, name)
97 }
98 }
99 }
100 if len(drained) != 2 {
101 t.Fatalf("drained = %v", drained)
102 }
103 if m.Client("keep") == nil {
104 t.Fatal("keep should remain")
105 }
106 }
107
107 lines GO