返回 DeepSeek-Reasonix
runtimeset_test.go
根目录 / internal / extension / runtimeset_test.go
1 package extension
2
3 import (
4 "errors"
5 "testing"
6 )
7
8 // countCloser records Close invocations and the order they happened in.
9 type countCloser struct {
10 closes *int
11 name string
12 order *[]string
13 err error
14 closedN int
15 }
16
17 func (c *countCloser) Close() error {
18 *c.closes++
19 c.closedN++
20 *c.order = append(*c.order, c.name)
21 return c.err
22 }
23
24 // TestRuntimeSetCloseOnce: Close is idempotent — most closers (processes,
25 // sockets) are not safe to close twice, and cleanup paths overlap.
26 func TestRuntimeSetCloseOnce(t *testing.T) {
27 closes := 0
28 var order []string
29 set := NewRuntimeSet(1)
30 set.Add(&countCloser{closes: &closes, name: "first", order: &order})
31 set.Add(&countCloser{closes: &closes, name: "second", order: &order})
32
33 if err := set.Close(); err != nil {
34 t.Fatalf("first Close: %v", err)
35 }
36 if closes != 2 {
37 t.Fatalf("closes = %d, want 2", closes)
38 }
39 // Reverse registration order: later resources may depend on earlier ones.
40 if len(order) != 2 || order[0] != "second" || order[1] != "first" {
41 t.Fatalf("close order = %v, want [second first]", order)
42 }
43 if err := set.Close(); err != nil {
44 t.Fatalf("second Close: %v", err)
45 }
46 if closes != 2 {
47 t.Fatalf("closes after double Close = %d, want still 2", closes)
48 }
49 if !set.Closed() {
50 t.Fatal("Closed() = false after Close")
51 }
52 if set.Len() != 0 {
53 t.Fatalf("Len() = %d after Close, want 0", set.Len())
54 }
55 }
56
57 // TestRuntimeSetCloseIfGeneration: a stale cleanup path must never close
58 // resources that now belong to a newer runtime.
59 func TestRuntimeSetCloseIfGeneration(t *testing.T) {
60 closes := 0
61 var order []string
62 set := NewRuntimeSet(5)
63 set.Add(&countCloser{closes: &closes, name: "c", order: &order})
64
65 if set.CloseIfGeneration(4) {
66 t.Fatal("CloseIfGeneration(4) closed a generation-5 set")
67 }
68 if closes != 0 {
69 t.Fatal("wrong generation closed resources")
70 }
71 if !set.CloseIfGeneration(5) {
72 t.Fatal("CloseIfGeneration(5) did not close the generation-5 set")
73 }
74 if closes != 1 {
75 t.Fatalf("closes = %d, want 1", closes)
76 }
77 // After closing, a repeat guard call is a no-op report, not a re-close.
78 set.CloseIfGeneration(5)
79 if closes != 1 {
80 t.Fatalf("closes after repeat = %d, want still 1", closes)
81 }
82 }
83
84 // TestRuntimeSetAddAfterClose: registering a resource on a dead set closes it
85 // immediately — leaking it would strand sidecar processes.
86 func TestRuntimeSetAddAfterClose(t *testing.T) {
87 closes := 0
88 var order []string
89 set := NewRuntimeSet(1)
90 if err := set.Close(); err != nil {
91 t.Fatalf("Close: %v", err)
92 }
93 set.Add(&countCloser{closes: &closes, name: "late", order: &order})
94 if closes != 1 {
95 t.Fatalf("closes = %d, want the late closer closed immediately", closes)
96 }
97 }
98
99 // TestRuntimeSetCloseErrors: close failures are joined, not dropped, and the
100 // remaining closers still run.
101 func TestRuntimeSetCloseErrors(t *testing.T) {
102 closes := 0
103 var order []string
104 boom := errors.New("boom")
105 set := NewRuntimeSet(1)
106 set.Add(&countCloser{closes: &closes, name: "ok", order: &order})
107 set.Add(&countCloser{closes: &closes, name: "bad", order: &order, err: boom})
108 err := set.Close()
109 if !errors.Is(err, boom) {
110 t.Fatalf("Close error = %v, want boom", err)
111 }
112 if closes != 2 {
113 t.Fatalf("closes = %d, want both closers attempted", closes)
114 }
115 }
116
116 lines GO