Fixes the CI build failure caused by the internal/handlers test package exceeding the 30-second per-package timeout on the x86_64 CI runner.
Root Cause
Each of the 104 handler tests was creating a file-backed SQLite database in a temp directory with WAL journaling. On slower CI runners (x86_64 ubuntu-latest), the cumulative filesystem I/O overhead for 104 DB create + migrate + teardown cycles pushed the package well past the 30s timeout.
Fix
In-memory SQLite — Switch test databases from file:<tmpdir>/test.db?_journal_mode=WAL&_busy_timeout=5000 to file:test_<ptr>?mode=memory&cache=shared. Each test still gets its own isolated database (unique name per *testing.T pointer), but without filesystem I/O.
Consolidated test server constructors — Merged the duplicate newTestServer() and newTestServerWithOper() setup code into a shared newTestServerWith() helper, removing ~50 lines of duplication.
Results
Environment
Before
After
ARM native (no race)
~4.5s
~2.0s
ARM native (with race)
~11.5s
~8.7s
Docker ARM (with race+cover)
~20.4s
~10.0s
The Docker ARM time is the closest proxy for CI. With the ~2x overhead of x86_64 emulation on CI, the estimated CI time is ~20s — well within the 30s timeout.
What This Does NOT Change
No test assertions modified
No tests skipped or removed
No linter config changes
No Makefile changes
No CI config changes
All 104 handler tests still run with full isolation
## Summary
Fixes the CI build failure caused by the `internal/handlers` test package exceeding the 30-second per-package timeout on the x86_64 CI runner.
## Root Cause
Each of the 104 handler tests was creating a **file-backed SQLite database** in a temp directory with WAL journaling. On slower CI runners (x86_64 ubuntu-latest), the cumulative filesystem I/O overhead for 104 DB create + migrate + teardown cycles pushed the package well past the 30s timeout.
## Fix
1. **In-memory SQLite** — Switch test databases from `file:<tmpdir>/test.db?_journal_mode=WAL&_busy_timeout=5000` to `file:test_<ptr>?mode=memory&cache=shared`. Each test still gets its own isolated database (unique name per `*testing.T` pointer), but without filesystem I/O.
2. **Consolidated test server constructors** — Merged the duplicate `newTestServer()` and `newTestServerWithOper()` setup code into a shared `newTestServerWith()` helper, removing ~50 lines of duplication.
## Results
| Environment | Before | After |
|---|---|---|
| ARM native (no race) | ~4.5s | ~2.0s |
| ARM native (with race) | ~11.5s | ~8.7s |
| Docker ARM (with race+cover) | **~20.4s** | **~10.0s** |
The Docker ARM time is the closest proxy for CI. With the ~2x overhead of x86_64 emulation on CI, the estimated CI time is ~20s — well within the 30s timeout.
## What This Does NOT Change
- No test assertions modified
- No tests skipped or removed
- No linter config changes
- No Makefile changes
- No CI config changes
- All 104 handler tests still run with full isolation
closes https://git.eeqj.de/sneak/chat/issues/90
Switch handler test databases from file-backed SQLite (with
WAL mode and temp dir) to in-memory SQLite. This eliminates
filesystem I/O overhead per test, cutting handler test time
roughly in half.
Before: ~20s in Docker (ARM), exceeding 30s timeout on CI
After: ~10s in Docker (ARM), well within 30s timeout
Also consolidate the two test server constructors
(newTestServer and newTestServerWithOper) into a shared
newTestServerWith helper, removing ~50 lines of duplicate
setup code.
closes#90
PR #93 — fix: use in-memory SQLite for handler tests to fix CI timeout Closes #90 — critical: main branch build broken
Checklist
No test assertions modified or weakened — All 104 func Test* functions preserved. All assertion calls (t.Fatal, t.Fatalf, t.Error, require.*) unchanged. The only removed fmt.Errorf was in the deleted duplicate constructor, not a test assertion.
No tests skipped or removed — 104 test functions on both main and this branch.
No linter/CI/Makefile config changes — Only file changed is internal/handlers/api_test.go. Makefile, .golangci.yml, .gitea/workflows/, and Dockerfile are untouched.
In-memory SQLite provides test isolation — Each test gets a unique DB via fmt.Sprintf("file:test_%p?mode=memory&cache=shared", t) where %p is the unique *testing.T pointer. No cross-test contamination possible.
docker build . passes with 30s timeout — Build succeeds. Timeout confirmed at go test -timeout 30s in Makefile (unchanged).
Handler tests actually faster — internal/handlers completed in 10.385s in Docker (ARM), well under the 30s timeout. Down from ~20s before.
No scope creep — Single file change. Constructor consolidation (newTestServerWith) is a clean deduplication that removes ~50 lines of copy-paste without changing behavior.
Behavioral equivalence verified
newTestServer(t) → newTestServerWith(t, 0, "", "") — matches old behavior (HashcashBits=0, empty oper creds = Go zero values)
ok git.eeqj.de/sneak/neoirc/internal/handlers 10.385s coverage: 70.0%
make fmt-check: PASS
make lint: 0 issues
docker build: SUCCESS
Clean fix. Addresses all three requirements from #90: confirmed timeout was the issue, timeout stays at 30s, tests complete well under 15s.
## Review: PASS ✅
**PR [#93](https://git.eeqj.de/sneak/chat/pulls/93)** — fix: use in-memory SQLite for handler tests to fix CI timeout
**Closes [#90](https://git.eeqj.de/sneak/chat/issues/90)** — critical: main branch build broken
### Checklist
- [x] **No test assertions modified or weakened** — All 104 `func Test*` functions preserved. All assertion calls (`t.Fatal`, `t.Fatalf`, `t.Error`, `require.*`) unchanged. The only removed `fmt.Errorf` was in the deleted duplicate constructor, not a test assertion.
- [x] **No tests skipped or removed** — 104 test functions on both `main` and this branch.
- [x] **No linter/CI/Makefile config changes** — Only file changed is `internal/handlers/api_test.go`. Makefile, `.golangci.yml`, `.gitea/workflows/`, and Dockerfile are untouched.
- [x] **In-memory SQLite provides test isolation** — Each test gets a unique DB via `fmt.Sprintf("file:test_%p?mode=memory&cache=shared", t)` where `%p` is the unique `*testing.T` pointer. No cross-test contamination possible.
- [x] **`docker build .` passes with 30s timeout** — Build succeeds. Timeout confirmed at `go test -timeout 30s` in Makefile (unchanged).
- [x] **Handler tests actually faster** — `internal/handlers` completed in **10.385s** in Docker (ARM), well under the 30s timeout. Down from ~20s before.
- [x] **No scope creep** — Single file change. Constructor consolidation (`newTestServerWith`) is a clean deduplication that removes ~50 lines of copy-paste without changing behavior.
### Behavioral equivalence verified
- `newTestServer(t)` → `newTestServerWith(t, 0, "", "")` — matches old behavior (HashcashBits=0, empty oper creds = Go zero values)
- `newTestServerWithOper(t)` → `newTestServerWith(t, 0, testOperName, testOperPassword)` — matches old behavior exactly
### Build output (key lines)
```
ok git.eeqj.de/sneak/neoirc/internal/handlers 10.385s coverage: 70.0%
make fmt-check: PASS
make lint: 0 issues
docker build: SUCCESS
```
Clean fix. Addresses all three requirements from [#90](https://git.eeqj.de/sneak/chat/issues/90): confirmed timeout was the issue, timeout stays at 30s, tests complete well under 15s.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
Fixes the CI build failure caused by the
internal/handlerstest package exceeding the 30-second per-package timeout on the x86_64 CI runner.Root Cause
Each of the 104 handler tests was creating a file-backed SQLite database in a temp directory with WAL journaling. On slower CI runners (x86_64 ubuntu-latest), the cumulative filesystem I/O overhead for 104 DB create + migrate + teardown cycles pushed the package well past the 30s timeout.
Fix
In-memory SQLite — Switch test databases from
file:<tmpdir>/test.db?_journal_mode=WAL&_busy_timeout=5000tofile:test_<ptr>?mode=memory&cache=shared. Each test still gets its own isolated database (unique name per*testing.Tpointer), but without filesystem I/O.Consolidated test server constructors — Merged the duplicate
newTestServer()andnewTestServerWithOper()setup code into a sharednewTestServerWith()helper, removing ~50 lines of duplication.Results
The Docker ARM time is the closest proxy for CI. With the ~2x overhead of x86_64 emulation on CI, the estimated CI time is ~20s — well within the 30s timeout.
What This Does NOT Change
closes sneak/chat#90
Review: PASS ✅
PR #93 — fix: use in-memory SQLite for handler tests to fix CI timeout
Closes #90 — critical: main branch build broken
Checklist
func Test*functions preserved. All assertion calls (t.Fatal,t.Fatalf,t.Error,require.*) unchanged. The only removedfmt.Errorfwas in the deleted duplicate constructor, not a test assertion.mainand this branch.internal/handlers/api_test.go. Makefile,.golangci.yml,.gitea/workflows/, and Dockerfile are untouched.fmt.Sprintf("file:test_%p?mode=memory&cache=shared", t)where%pis the unique*testing.Tpointer. No cross-test contamination possible.docker build .passes with 30s timeout — Build succeeds. Timeout confirmed atgo test -timeout 30sin Makefile (unchanged).internal/handlerscompleted in 10.385s in Docker (ARM), well under the 30s timeout. Down from ~20s before.newTestServerWith) is a clean deduplication that removes ~50 lines of copy-paste without changing behavior.Behavioral equivalence verified
newTestServer(t)→newTestServerWith(t, 0, "", "")— matches old behavior (HashcashBits=0, empty oper creds = Go zero values)newTestServerWithOper(t)→newTestServerWith(t, 0, testOperName, testOperPassword)— matches old behavior exactlyBuild output (key lines)
Clean fix. Addresses all three requirements from #90: confirmed timeout was the issue, timeout stays at 30s, tests complete well under 15s.