|
1 | 1 | package connector |
2 | 2 |
|
3 | 3 | import ( |
| 4 | + "net" |
| 5 | + "strconv" |
4 | 6 | "testing" |
5 | 7 | "time" |
6 | 8 |
|
7 | 9 | "github.com/stretchr/testify/assert" |
| 10 | + "github.com/stretchr/testify/require" |
8 | 11 | ) |
9 | 12 |
|
10 | 13 | func TestNewService(t *testing.T) { |
@@ -57,20 +60,64 @@ func TestService_APIs(t *testing.T) { |
57 | 60 | func TestService_PauseResumeBackground(t *testing.T) { |
58 | 61 | state := setupTests(t) |
59 | 62 |
|
| 63 | + // Pause/resume only affects the WS listener path; use a pre-assigned port so we can dial it. |
| 64 | + host, port := freeLocalTCPPort(t) |
| 65 | + addr := net.JoinHostPort(host, strconv.Itoa(port)) |
| 66 | + state.service.config.WSEnabled = true |
| 67 | + state.service.config.WSHost = host |
| 68 | + state.service.config.WSPort = port |
| 69 | + |
| 70 | + t.Cleanup(func() { _ = state.service.Stop() }) |
| 71 | + |
60 | 72 | err := state.service.Start() |
61 | | - assert.NoError(t, err) |
| 73 | + require.NoError(t, err) |
| 74 | + require.NotNil(t, state.service.wsServer) |
| 75 | + waitUntilTCPAccepts(t, addr) |
62 | 76 |
|
63 | 77 | err = state.service.PauseBackground() |
64 | | - assert.NoError(t, err) |
65 | | - assert.True(t, state.service.paused) |
| 78 | + require.NoError(t, err) |
| 79 | + require.True(t, state.service.paused) |
| 80 | + waitUntilTCPRefused(t, addr) |
66 | 81 |
|
67 | 82 | err = state.service.ResumeForeground() |
68 | | - assert.NoError(t, err) |
69 | | - assert.False(t, state.service.paused) |
70 | | - |
71 | | - // Give the listener goroutine a small window to bind. |
72 | | - time.Sleep(50 * time.Millisecond) |
| 83 | + require.NoError(t, err) |
| 84 | + require.False(t, state.service.paused) |
| 85 | + waitUntilTCPAccepts(t, addr) |
73 | 86 |
|
74 | 87 | err = state.service.Stop() |
75 | | - assert.NoError(t, err) |
| 88 | + require.NoError(t, err) |
| 89 | +} |
| 90 | + |
| 91 | +// freeLocalTCPPort reserves an ephemeral TCP port on loopback for the test process. |
| 92 | +// There is a small race where another process could bind between Close and Start; acceptable for tests. |
| 93 | +func freeLocalTCPPort(t *testing.T) (host string, port int) { |
| 94 | + t.Helper() |
| 95 | + ln, err := net.Listen("tcp", "127.0.0.1:0") |
| 96 | + require.NoError(t, err) |
| 97 | + tcpAddr := ln.Addr().(*net.TCPAddr) |
| 98 | + require.NoError(t, ln.Close()) |
| 99 | + return tcpAddr.IP.String(), tcpAddr.Port |
| 100 | +} |
| 101 | + |
| 102 | +func waitUntilTCPAccepts(t *testing.T, addr string) { |
| 103 | + t.Helper() |
| 104 | + require.Eventually(t, func() bool { |
| 105 | + c, err := net.DialTimeout("tcp", addr, 50*time.Millisecond) |
| 106 | + if err != nil { |
| 107 | + return false |
| 108 | + } |
| 109 | + _ = c.Close() |
| 110 | + return true |
| 111 | + }, 5*time.Second, 10*time.Millisecond, "expected TCP accept on %s", addr) |
| 112 | +} |
| 113 | + |
| 114 | +func waitUntilTCPRefused(t *testing.T, addr string) { |
| 115 | + t.Helper() |
| 116 | + require.Eventually(t, func() bool { |
| 117 | + c, err := net.DialTimeout("tcp", addr, 50*time.Millisecond) |
| 118 | + if c != nil { |
| 119 | + _ = c.Close() |
| 120 | + } |
| 121 | + return err != nil |
| 122 | + }, 5*time.Second, 10*time.Millisecond, "expected no listener on %s after pause", addr) |
76 | 123 | } |
0 commit comments