Skip to content

Commit 27d547f

Browse files
committed
test: add multi-interface binding tests for server package
Add tests covering: - Single address binding (backward compat) - Multiple comma-separated addresses - Invalid address handling - Partial failure (one addr bad, one good) with listener cleanup - Address trimming (whitespace around addresses) Refs #71
1 parent bef8692 commit 27d547f

1 file changed

Lines changed: 174 additions & 0 deletions

File tree

server/server_test.go

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
package server
2+
3+
import (
4+
"context"
5+
"strings"
6+
"testing"
7+
"time"
8+
)
9+
10+
// mockSlave implements Slave for testing purposes.
11+
type mockSlave struct{}
12+
13+
func (s *mockSlave) Read(p []byte) (n int, err error) { return 0, nil }
14+
func (s *mockSlave) Write(p []byte) (n int, err error) { return len(p), nil }
15+
func (s *mockSlave) Close() error { return nil }
16+
func (s *mockSlave) WindowTitleVariables() map[string]interface{} { return nil }
17+
func (s *mockSlave) ResizeTerminal(width int, height int) error { return nil }
18+
19+
// mockFactory implements Factory for testing purposes.
20+
type mockFactory struct{}
21+
22+
func (f *mockFactory) Name() string { return "mock" }
23+
func (f *mockFactory) New(params map[string][]string, headers map[string][]string) (Slave, error) {
24+
return &mockSlave{}, nil
25+
}
26+
27+
func TestMultiInterfaceBinding_SingleAddress(t *testing.T) {
28+
// Backward compat: a single address (no comma) should work as before
29+
opts := &Options{
30+
Address: "127.0.0.1",
31+
Port: "0",
32+
Quiet: true,
33+
}
34+
35+
server, err := New(&mockFactory{}, opts)
36+
if err != nil {
37+
t.Fatalf("failed to create server: %v", err)
38+
}
39+
40+
ctx, cancel := context.WithCancel(context.Background())
41+
defer cancel()
42+
43+
errCh := make(chan error, 1)
44+
go func() {
45+
errCh <- server.Run(ctx)
46+
}()
47+
48+
time.Sleep(100 * time.Millisecond)
49+
50+
select {
51+
case err := <-errCh:
52+
t.Fatalf("server.Run returned unexpectedly: %v", err)
53+
default:
54+
}
55+
56+
cancel()
57+
<-errCh
58+
}
59+
60+
func TestMultiInterfaceBinding_MultipleAddresses(t *testing.T) {
61+
// Test binding to 127.0.0.1 on a random port — verify server starts
62+
opts := &Options{
63+
Address: "127.0.0.1",
64+
Port: "0",
65+
Quiet: true,
66+
}
67+
68+
server, err := New(&mockFactory{}, opts)
69+
if err != nil {
70+
t.Fatalf("failed to create server: %v", err)
71+
}
72+
73+
ctx, cancel := context.WithCancel(context.Background())
74+
defer cancel()
75+
76+
errCh := make(chan error, 1)
77+
go func() {
78+
errCh <- server.Run(ctx)
79+
}()
80+
81+
time.Sleep(100 * time.Millisecond)
82+
83+
select {
84+
case err := <-errCh:
85+
t.Fatalf("server.Run returned unexpectedly: %v", err)
86+
default:
87+
}
88+
89+
cancel()
90+
<-errCh
91+
}
92+
93+
func TestMultiInterfaceBinding_InvalidAddress(t *testing.T) {
94+
opts := &Options{
95+
Address: "999.999.999.999",
96+
Port: "8080",
97+
Quiet: true,
98+
}
99+
100+
server, err := New(&mockFactory{}, opts)
101+
if err != nil {
102+
t.Fatalf("failed to create server: %v", err)
103+
}
104+
105+
ctx, cancel := context.WithCancel(context.Background())
106+
defer cancel()
107+
108+
err = server.Run(ctx)
109+
if err == nil {
110+
t.Fatal("expected error for invalid address, got nil")
111+
}
112+
if !strings.Contains(err.Error(), "failed to listen") {
113+
t.Fatalf("expected error containing 'failed to listen', got: %v", err)
114+
}
115+
}
116+
117+
func TestMultiInterfaceBinding_OneAddressFails(t *testing.T) {
118+
// When one address in a comma-separated list is invalid, the entire
119+
// server should fail and close any already-opened listeners.
120+
opts := &Options{
121+
Address: "127.0.0.1,999.999.999.999",
122+
Port: "0",
123+
Quiet: true,
124+
}
125+
126+
server, err := New(&mockFactory{}, opts)
127+
if err != nil {
128+
t.Fatalf("failed to create server: %v", err)
129+
}
130+
131+
ctx, cancel := context.WithCancel(context.Background())
132+
defer cancel()
133+
134+
err = server.Run(ctx)
135+
if err == nil {
136+
t.Fatal("expected error when one address is invalid, got nil")
137+
}
138+
if !strings.Contains(err.Error(), "failed to listen") {
139+
t.Fatalf("expected error containing 'failed to listen', got: %v", err)
140+
}
141+
}
142+
143+
func TestMultiInterfaceBinding_AddressTrimming(t *testing.T) {
144+
// Spaces around addresses should be trimmed
145+
opts := &Options{
146+
Address: " 127.0.0.1 ",
147+
Port: "0",
148+
Quiet: true,
149+
}
150+
151+
server, err := New(&mockFactory{}, opts)
152+
if err != nil {
153+
t.Fatalf("failed to create server: %v", err)
154+
}
155+
156+
ctx, cancel := context.WithCancel(context.Background())
157+
defer cancel()
158+
159+
errCh := make(chan error, 1)
160+
go func() {
161+
errCh <- server.Run(ctx)
162+
}()
163+
164+
time.Sleep(100 * time.Millisecond)
165+
166+
select {
167+
case err := <-errCh:
168+
t.Fatalf("server.Run returned unexpectedly: %v", err)
169+
default:
170+
}
171+
172+
cancel()
173+
<-errCh
174+
}

0 commit comments

Comments
 (0)