aboutsummaryrefslogtreecommitdiff
path: root/hotline/server_blackbox_test.go
diff options
context:
space:
mode:
authorJeff Halter <868228+jhalter@users.noreply.github.com>2026-07-10 09:48:18 -0700
committerJeff Halter <868228+jhalter@users.noreply.github.com>2026-07-10 09:48:18 -0700
commit21f24d24fd6f501b32f15a2bef41c89cc461f623 (patch)
treeba4952fb82f3ef9c265228633f27536866e23931 /hotline/server_blackbox_test.go
parentae44fb222ec73cae8441f5a5d9a21f8585fe587f (diff)
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.
Diffstat (limited to 'hotline/server_blackbox_test.go')
-rw-r--r--hotline/server_blackbox_test.go30
1 files changed, 20 insertions, 10 deletions
diff --git a/hotline/server_blackbox_test.go b/hotline/server_blackbox_test.go
index 7033703..bfd9e9c 100644
--- a/hotline/server_blackbox_test.go
+++ b/hotline/server_blackbox_test.go
@@ -16,21 +16,31 @@ func NewTestLogger() *slog.Logger {
return slog.New(slog.NewTextHandler(os.Stdout, nil))
}
-// assertTransferBytesEqual takes a string with a hexdump in the same format that `hexdump -C` produces and compares with
-// a hexdump for the bytes in got, after stripping the create/modify timestamps.
-// I don't love this, but as git does not preserve file create/modify timestamps, we either need to fully mock the
-// filesystem interactions or work around in this way.
-// TODO: figure out a better solution
+// flatFileTimestampOffset is the byte offset of the information fork's CreateDate within a
+// flattened file object stream: FlatFileHeader (24) + INFO FlatFileForkHeader (16) + the info
+// fork's fixed fields up to CreateDate (Platform+TypeSignature+CreatorSignature+Flags+
+// PlatformFlags = 20, then RSVD = 32). CreateDate and ModifyDate are 8 bytes each and adjacent.
+const (
+ flatFileTimestampOffset = 24 + 16 + 20 + 32
+ flatFileTimestampLen = 16 // CreateDate (8) + ModifyDate (8)
+)
+
+// assertTransferBytesEqual takes a string with a hexdump in the same format that `hexdump -C`
+// produces and compares with a hexdump for the bytes in got, after zeroing the info fork's
+// create/modify timestamps. Git does not preserve file create/modify times, so those bytes vary
+// between checkouts; the offset is derived structurally from the flattened file object layout
+// rather than hardcoded (see flatFileTimestampOffset).
func assertTransferBytesEqual(t *testing.T, wantHexDump string, got []byte) bool {
if wantHexDump == "" {
return true
}
- clean := slices.Concat(
- got[:92],
- make([]byte, 16),
- got[108:],
- )
+ clean := slices.Clone(got)
+ if len(clean) >= flatFileTimestampOffset+flatFileTimestampLen {
+ for i := flatFileTimestampOffset; i < flatFileTimestampOffset+flatFileTimestampLen; i++ {
+ clean[i] = 0
+ }
+ }
return assert.Equal(t, wantHexDump, hex.Dump(clean))
}