返回 DeepSeek-Reasonix
goal_driver_close_test.go
根目录 / internal / control / goal_driver_close_test.go
1 package control
2
3 import (
4 "context"
5 "os"
6 "sync"
7 "testing"
8 "time"
9
10 "reasonix/internal/agent"
11 "reasonix/internal/event"
12 "reasonix/internal/session"
13 "reasonix/internal/tool"
14 )
15
16 func TestControllerCloseCancelsGoalDriverFlushWait(t *testing.T) {
17 flushStarted := make(chan struct{})
18 releaseFlush := make(chan struct{})
19 var once sync.Once
20 store, err := session.CreateWithOptions(t.TempDir()+"/goal-close-flush", "goal-close-flush", session.OpenOptions{
21 Sync: func(*os.File) error {
22 once.Do(func() { close(flushStarted) })
23 <-releaseFlush
24 return nil
25 },
26 })
27 if err != nil {
28 t.Fatal(err)
29 }
30 service, err := session.NewService("desktop", failingFlushPersistence{session: store})
31 if err != nil {
32 t.Fatal(err)
33 }
34 t.Cleanup(func() { _ = service.CloseAll(context.Background()) })
35 runtime, err := service.Create(t.Context(), session.CreateOptions{SessionID: "goal-close-flush"})
36 if err != nil {
37 t.Fatal(err)
38 }
39 replacementBinding, err := service.Bind(runtime)
40 if err != nil {
41 t.Fatal(err)
42 }
43 exec := agent.New(nil, tool.NewRegistry(), agent.NewSession("system"), agent.Options{}, event.Discard)
44 c := newOwnedTestController(t, Options{Executor: exec, Sink: event.Discard, SessionService: service, SessionRuntime: runtime, ExclusiveSession: true})
45 if err := c.SetGoalDurable("close without waiting for a stuck disk"); err != nil {
46 t.Fatal(err)
47 }
48 c.kickGoalDriver()
49 select {
50 case <-flushStarted:
51 case <-time.After(5 * time.Second):
52 t.Fatal("goal driver did not enter Flush")
53 }
54 done := make(chan struct{})
55 go func() {
56 c.ReleaseResources()
57 close(done)
58 }()
59 select {
60 case <-done:
61 case <-time.After(time.Second):
62 close(releaseFlush)
63 t.Fatal("controller close remained blocked on the goal driver Flush")
64 }
65 close(releaseFlush)
66 if _, err := runtime.Session().Flush(t.Context()); err != nil {
67 t.Fatal(err)
68 }
69 if err := replacementBinding.Release(t.Context()); err != nil {
70 t.Fatal(err)
71 }
72 }
73
73 lines GO