返回 DeepSeek-Reasonix
serve_portfile_test.go
根目录 / internal / serve / serve_portfile_test.go
1 package serve
2
3 import (
4 "context"
5 "net"
6 "net/http"
7 "testing"
8 "time"
9
10 "reasonix/internal/config"
11 "reasonix/internal/control"
12 )
13
14 func newListenerTestServer(t *testing.T) *Server {
15 t.Helper()
16 t.Setenv("REASONIX_HOME", t.TempDir())
17 bc := NewBroadcaster()
18 ctrl := control.New(control.Options{
19 Sink: bc,
20 Label: "listener-test",
21 SessionDir: t.TempDir(),
22 })
23 t.Cleanup(func() { ctrl.Close() })
24 return New(ctrl, bc, config.ServeConfig{})
25 }
26
27 func waitForHTTP(t *testing.T, addr string) {
28 t.Helper()
29 client := &http.Client{Timeout: 2 * time.Second}
30 var lastErr error
31 for i := 0; i < 100; i++ {
32 resp, err := client.Get("http://" + addr + "/assets/logo-wordmark.svg")
33 if err == nil {
34 resp.Body.Close()
35 if resp.StatusCode != http.StatusOK {
36 t.Fatalf("GET logo = %d, want 200", resp.StatusCode)
37 }
38 return
39 }
40 lastErr = err
41 time.Sleep(20 * time.Millisecond)
42 }
43 t.Fatalf("server never came up on %s: %v", addr, lastErr)
44 }
45
46 // RunGracefulListener must serve on the caller-supplied listener so callers
47 // that need the real bound address (--addr 127.0.0.1:0 with --port-file) can
48 // listen first, record ln.Addr(), then hand the listener over.
49 func TestRunGracefulListenerServesOnProvidedListener(t *testing.T) {
50 srv := newListenerTestServer(t)
51
52 ln, err := net.Listen("tcp", "127.0.0.1:0")
53 if err != nil {
54 t.Fatalf("listen: %v", err)
55 }
56 ctx, cancel := context.WithCancel(context.Background())
57 done := make(chan error, 1)
58 go func() { done <- srv.RunGracefulListener(ctx, ln) }()
59
60 waitForHTTP(t, ln.Addr().String())
61
62 cancel()
63 select {
64 case err := <-done:
65 if err != nil {
66 t.Fatalf("RunGracefulListener returned a shutdown error: %v", err)
67 }
68 case <-time.After(15 * time.Second):
69 t.Fatal("RunGracefulListener did not return after ctx cancel")
70 }
71 }
72
73 // RunGraceful must keep its historical contract (bind from the addr string
74 // itself) now that it delegates to RunGracefulListener.
75 func TestRunGracefulStillListensFromAddr(t *testing.T) {
76 srv := newListenerTestServer(t)
77
78 probe, err := net.Listen("tcp", "127.0.0.1:0")
79 if err != nil {
80 t.Fatalf("probe listen: %v", err)
81 }
82 addr := probe.Addr().String()
83 probe.Close()
84
85 ctx, cancel := context.WithCancel(context.Background())
86 done := make(chan error, 1)
87 go func() { done <- srv.RunGraceful(ctx, addr) }()
88
89 waitForHTTP(t, addr)
90
91 cancel()
92 select {
93 case err := <-done:
94 if err != nil {
95 t.Fatalf("RunGraceful returned a shutdown error: %v", err)
96 }
97 case <-time.After(15 * time.Second):
98 t.Fatal("RunGraceful did not return after ctx cancel")
99 }
100 }
101
101 lines GO