Short and single-letter names in application code become full words (tests keep theirs). Wrapped calls, signatures and composite literals put one item per line, and one-line function bodies are expanded. Exported names and groups of constants get doc comments, and comments refer to the renamed identifiers.
107 lines
2.8 KiB
Go
107 lines
2.8 KiB
Go
package websocket
|
|
|
|
import (
|
|
"bufio"
|
|
"context"
|
|
"errors"
|
|
"net"
|
|
"testing"
|
|
"time"
|
|
|
|
"git.avsdev.uk/Go/job-search/internal/config"
|
|
)
|
|
|
|
// pipeClient runs a plain TCP client of h on an in-memory pipe and reads its
|
|
// welcome. It returns the peer's end, which the test stops reading from to
|
|
// make the client's writes block, and a channel closed when ServeTCP returns.
|
|
func pipeClient(t *testing.T, h *Hub) (net.Conn, chan struct{}) {
|
|
t.Helper()
|
|
server, peer := net.Pipe()
|
|
t.Cleanup(func() {
|
|
server.Close()
|
|
peer.Close()
|
|
})
|
|
done := make(chan struct{})
|
|
go func() {
|
|
h.ServeTCP(context.Background(), server)
|
|
close(done)
|
|
}()
|
|
peer.SetReadDeadline(time.Now().Add(time.Second))
|
|
if _, err := bufio.NewReader(peer).ReadString('\n'); err != nil {
|
|
t.Fatalf("welcome: %v", err)
|
|
}
|
|
waitCount(t, h, 1)
|
|
return peer, done
|
|
}
|
|
|
|
// If ctx expires before the clients have closed, Shutdown closes the rest at
|
|
// once and returns ctx's error.
|
|
func TestShutdownTimeout(t *testing.T) {
|
|
// No write timeout: the client's goodbye blocks until it is cut short.
|
|
h := NewHub(config.JSCServerWebSocket{}, WithLogger(discardLog))
|
|
_, done := pipeClient(t, h)
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Millisecond)
|
|
defer cancel()
|
|
if err := h.Shutdown(ctx); !errors.Is(err, context.DeadlineExceeded) {
|
|
t.Fatalf("Shutdown = %v; want context.DeadlineExceeded", err)
|
|
}
|
|
select {
|
|
case <-done:
|
|
case <-time.After(time.Second):
|
|
t.Fatal("client not closed after the shutdown timeout")
|
|
}
|
|
}
|
|
|
|
// Shutdown with no clients returns at once, even with an expired ctx.
|
|
func TestShutdownNoClients(t *testing.T) {
|
|
h := NewHub(config.JSCServerWebSocket{}, WithLogger(discardLog))
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
cancel()
|
|
for range 20 {
|
|
if err := h.Shutdown(ctx); err != nil {
|
|
t.Fatalf("Shutdown = %v; want nil", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// A ping that can't be sent within the pong timeout drops the client.
|
|
func TestPingFailureDropsClient(t *testing.T) {
|
|
h := NewHub(
|
|
config.JSCServerWebSocket{
|
|
PingInterval: 10 * time.Millisecond,
|
|
PongTimeout: 20 * time.Millisecond,
|
|
},
|
|
WithLogger(discardLog),
|
|
)
|
|
_, done := pipeClient(t, h)
|
|
|
|
select {
|
|
case <-done:
|
|
case <-time.After(time.Second):
|
|
t.Fatal("client not dropped after a failed ping")
|
|
}
|
|
waitCount(t, h, 0)
|
|
}
|
|
|
|
func TestMaxMessageSize(t *testing.T) {
|
|
tests := []struct {
|
|
conf config.JSCServerWebSocket
|
|
want int
|
|
}{
|
|
{config.JSCServerWebSocket{}, defaultMaxMessageSize},
|
|
{config.JSCServerWebSocket{
|
|
MaxMessageSize: -1,
|
|
}, defaultMaxMessageSize},
|
|
{config.JSCServerWebSocket{
|
|
MaxMessageSize: 100,
|
|
}, 100},
|
|
}
|
|
for _, tt := range tests {
|
|
h := NewHub(tt.conf, WithLogger(discardLog))
|
|
if got := h.maxMessageSize(); got != tt.want {
|
|
t.Errorf("maxMessageSize(%d) = %d; want %d", tt.conf.MaxMessageSize, got, tt.want)
|
|
}
|
|
}
|
|
}
|