From 847f5c6fecbfb82673edaaf3c4b5da90b621b401 Mon Sep 17 00:00:00 2001 From: clawbot <35+clawbot@noreply.example.org> Date: Wed, 11 Nov 2026 01:07:47 +0100 Subject: [PATCH] Let ircserver and db tests pass under heavy host load (closes #265) Tests in internal/ircserver and internal/db failed under heavy host load and passed on a rerun. testTimeout, the failure limit on every read and wait in internal/ircserver, goes from 5 to 30 seconds; passing tests take no longer. TestQuietClientNotAnsweringPingIsClosed gets its own 4-second ping interval and measures its upper bound from the end of registration, a second before a two-interval read deadline would close the connection, leaving room for scheduling delay. endlessQueryTimeout in internal/db goes from 100 ms to 2 seconds, so the cancelled-query tests interrupt a running statement. Judgement call: the closed test no longer checks PING timing; TestQuietClientAnsweringPingStaysConnected still does. Model: opus-5-5 --- internal/db/db_test.go | 6 ++++- internal/ircserver/server_test.go | 45 ++++++++++++++++++++++++------- 2 files changed, 40 insertions(+), 11 deletions(-) diff --git a/internal/db/db_test.go b/internal/db/db_test.go index 6d10283..40df42a 100644 --- a/internal/db/db_test.go +++ b/internal/db/db_test.go @@ -26,7 +26,11 @@ const ( (SELECT 1 UNION ALL SELECT x + 1 FROM n) SELECT COUNT(*) FROM n` - endlessQueryTimeout = 100 * time.Millisecond + // endlessQueryTimeout is how long the endless query runs + // before its context ends. A query whose context ends before + // it starts is never run, and the pool keeps its connection, + // so the timeout leaves a loaded host time to start it. + endlessQueryTimeout = 2 * time.Second ) // TestDefaultDBURLSetsWALAndBusyTimeout opens a file database diff --git a/internal/ircserver/server_test.go b/internal/ircserver/server_test.go index 2ca7d02..5ee18cc 100644 --- a/internal/ircserver/server_test.go +++ b/internal/ircserver/server_test.go @@ -30,7 +30,10 @@ import ( _ "modernc.org/sqlite" ) -const testTimeout = 5 * time.Second +// testTimeout is how long a test waits for something before it +// fails. A passing test never waits this long, so it is generous +// enough for a heavily loaded host. +const testTimeout = 30 * time.Second // testEnv holds the shared test infrastructure. type testEnv struct { @@ -553,27 +556,49 @@ func TestQuietClientAnsweringPingStaysConnected(t *testing.T) { func TestQuietClientNotAnsweringPingIsClosed(t *testing.T) { t.Parallel() - // latest leaves room for a loaded host, but is sooner than - // a read deadline of two ping intervals would close it. + // A read deadline of two ping intervals would close the + // connection a second after latest. The ping interval is + // longer than testPingInterval, so that the time between + // the expected close and latest leaves room for a loaded + // host. const ( - earliest = testPingInterval + testPongDeadline - latest = 2 * testPingInterval + interval = 4 * time.Second + earliest = interval + testPongDeadline + latest = 2*interval - time.Second ) - env := newTestEnvWithShortPings(t) + env := newTestEnvWithPingTimes( + t, + &config.Config{ServerName: testServerName}, + interval, testPongDeadline, + ) client := env.dial(t) lastSent := time.Now() client.register("silent") - client.readServerPing(lastSent) + + // The server starts both waits once it has handled the + // registration, so the time registration took does not + // count against latest. + registered := time.Now() + + client.readUntil(isServerPing) client.readUntilClosed() waited := time.Since(lastSent) - if waited < earliest || waited >= latest { + if waited < earliest { t.Errorf( - "closed %v after the client's last line, want %v to %v", - waited, earliest, latest, + "closed %v after the client's last line, want at least %v", + waited, earliest, + ) + } + + sinceRegistered := time.Since(registered) + if sinceRegistered >= latest { + t.Errorf( + "closed %v after registration, want less than %v", + sinceRegistered, latest, ) } }