返回 DeepSeek-Reasonix
inbox_dispatch_shutdown_test.go
根目录 / internal / control / inbox_dispatch_shutdown_test.go
1 package control
2
3 import (
4 "path/filepath"
5 "sync"
6 "testing"
7 "time"
8
9 "reasonix/internal/sessioninbox"
10 )
11
12 func TestControllerShutdownJoinsInboxScanBeforeReturning(t *testing.T) {
13 for _, mode := range []string{"close", "replacement"} {
14 t.Run(mode, func(t *testing.T) {
15 c := newOwnedTestController(t, Options{SessionPath: filepath.Join(t.TempDir(), "session.jsonl")})
16 scanReached := make(chan struct{})
17 releaseScan := make(chan struct{})
18 var releaseOnce sync.Once
19 defer func() {
20 releaseOnce.Do(func() { close(releaseScan) })
21 c.Close()
22 c.autosaveWG.Wait()
23 }()
24 c.SetBeforeInboxDispatch(func(*Controller) (func(), error) { return nil, nil })
25 c.inbox.afterDispatchScan = func(bool) {
26 close(scanReached)
27 <-releaseScan
28 }
29 c.NotifyInboxRuntimeReady()
30 select {
31 case <-scanReached:
32 case <-time.After(inboxDispatchTestTimeout):
33 t.Fatal("dispatcher did not reach the controlled scan boundary")
34 }
35 closed := make(chan struct{})
36 go func() {
37 if mode == "replacement" {
38 c.ReleaseResources()
39 } else {
40 c.Close()
41 }
42 close(closed)
43 }()
44 // The channel fixes the interleaving; this observation window asserts
45 // that shutdown cannot finish until the scan is released.
46 select {
47 case <-closed:
48 t.Fatal("shutdown returned while the inbox scan still owned its sidecar access")
49 case <-time.After(100 * time.Millisecond):
50 }
51 releaseOnce.Do(func() { close(releaseScan) })
52 select {
53 case <-closed:
54 case <-time.After(inboxDispatchTestTimeout):
55 t.Fatal("shutdown did not finish after the scan was released")
56 }
57 c.autosaveWG.Wait()
58 })
59 }
60 }
61
62 func TestInboxDispatchHostAdmissionCanRetireItsController(t *testing.T) {
63 c := newOwnedTestController(t, Options{SessionPath: filepath.Join(t.TempDir(), "session.jsonl")})
64 defer func() {
65 c.Close()
66 c.autosaveWG.Wait()
67 }()
68 if err := c.SetInboxPaused(true); err != nil {
69 t.Fatal(err)
70 }
71 if _, err := c.EnqueueInbox(InboxRequest{Intent: sessioninbox.IntentFollowup, Submit: "queued"}); err != nil {
72 t.Fatal(err)
73 }
74 retired := make(chan struct{})
75 c.SetBeforeInboxDispatch(func(current *Controller) (func(), error) {
76 current.ReleaseResources()
77 close(retired)
78 return nil, ErrInboxRuntimeUnpublished
79 })
80 if err := c.SetInboxPaused(false); err != nil {
81 t.Fatal(err)
82 }
83 select {
84 case <-retired:
85 case <-time.After(inboxDispatchTestTimeout):
86 t.Fatal("host admission could not retire its dispatching controller")
87 }
88 c.autosaveWG.Wait()
89 }
90
90 lines GO