From 21f24d24fd6f501b32f15a2bef41c89cc461f623 Mon Sep 17 00:00:00 2001 From: Jeff Halter <868228+jhalter@users.noreply.github.com> Date: Fri, 10 Jul 2026 09:48:18 -0700 Subject: Overhaul regression testing: e2e suite, fuzzing, CI, and bug fixes Add a protocol-level end-to-end suite (in-process fully wired server on an ephemeral port pair, driven by hotline.Client over TCP) covering handshake, login, public and private chat, message board, threaded news, file list/download/upload, account admin, disconnect notification, and shutdown broadcast. The harness retries on a fresh port pair when another process steals a probed port before ListenAndServe binds it, and Server gains WithConnectionRateLimit so tests can disable the per-IP connection throttle. Add native fuzz tests for Transaction, Field, and flattened file object decoding, and fix the bugs the new tests surfaced: - Transaction.Write panicked on out-of-range attacker-controlled size fields, and transactionScanner's uint32 length addition could wrap and yield a truncated token. The information fork size declared in an untrusted fork header is now bounded too. - Client keepalive read c.done unsynchronized while Disconnect replaces it under the mutex. - The shared Agreement's Seek+ReadAll login path raced concurrent logins; the server now prefers an AgreementBytes() snapshot. Fill unit-test gaps (main's config-copy helpers, file resume data, ReloaderFunc, R2 error injection and env validation) and add a CI test workflow (build/vet + race-enabled shuffled suite), fixed lint workflow triggers with golangci-lint v2.6, and Makefile test/cover/ lint/fuzz targets. --- hotline/client_test.go | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'hotline/client_test.go') diff --git a/hotline/client_test.go b/hotline/client_test.go index 23b6155..fb5a3a9 100644 --- a/hotline/client_test.go +++ b/hotline/client_test.go @@ -21,8 +21,8 @@ func newTestClient() *Client { func TestClient_Handshake(t *testing.T) { t.Run("successful handshake", func(t *testing.T) { clientConn, serverConn := net.Pipe() - defer clientConn.Close() - defer serverConn.Close() + defer func() { _ = clientConn.Close() }() + defer func() { _ = serverConn.Close() }() c := newTestClient() c.Connection = clientConn @@ -41,8 +41,8 @@ func TestClient_Handshake(t *testing.T) { t.Run("server returns error response", func(t *testing.T) { clientConn, serverConn := net.Pipe() - defer clientConn.Close() - defer serverConn.Close() + defer func() { _ = clientConn.Close() }() + defer func() { _ = serverConn.Close() }() c := newTestClient() c.Connection = clientConn @@ -61,7 +61,7 @@ func TestClient_Handshake(t *testing.T) { t.Run("connection closed during read", func(t *testing.T) { clientConn, serverConn := net.Pipe() - defer clientConn.Close() + defer func() { _ = clientConn.Close() }() c := newTestClient() c.Connection = clientConn @@ -69,7 +69,7 @@ func TestClient_Handshake(t *testing.T) { go func() { buf := make([]byte, 12) _, _ = io.ReadFull(serverConn, buf) - serverConn.Close() + _ = serverConn.Close() }() err := c.Handshake() @@ -81,8 +81,8 @@ func TestClient_Handshake(t *testing.T) { func TestClient_Send(t *testing.T) { t.Run("sends non-reply transaction and tracks it", func(t *testing.T) { clientConn, serverConn := net.Pipe() - defer clientConn.Close() - defer serverConn.Close() + defer func() { _ = clientConn.Close() }() + defer func() { _ = serverConn.Close() }() c := newTestClient() c.Connection = clientConn @@ -104,8 +104,8 @@ func TestClient_Send(t *testing.T) { t.Run("reply transactions are not tracked", func(t *testing.T) { clientConn, serverConn := net.Pipe() - defer clientConn.Close() - defer serverConn.Close() + defer func() { _ = clientConn.Close() }() + defer func() { _ = serverConn.Close() }() c := newTestClient() c.Connection = clientConn @@ -195,7 +195,7 @@ func TestClient_HandleTransaction(t *testing.T) { func TestClient_Disconnect(t *testing.T) { t.Run("closes connection and done channel", func(t *testing.T) { clientConn, serverConn := net.Pipe() - defer serverConn.Close() + defer func() { _ = serverConn.Close() }() c := newTestClient() c.Connection = clientConn @@ -220,7 +220,7 @@ func TestClient_Disconnect(t *testing.T) { t.Run("handles nil done channel", func(t *testing.T) { clientConn, serverConn := net.Pipe() - defer serverConn.Close() + defer func() { _ = serverConn.Close() }() c := newTestClient() c.Connection = clientConn -- cgit