Files
Crussell/.sisyphus/coverage-report.md
T
popertots 3029fd5179
CI / Nginx config check (push) Successful in 13s
CI / Env docs check (push) Successful in 15s
CI / Docker compose check (push) Successful in 15s
CI / Frontend major deps (push) Failing after 24s
CI / Frontend deps check (push) Successful in 30s
CI / Secrets scan (push) Successful in 38s
CI / Go build (push) Successful in 39s
CI / Frontend build (push) Successful in 1m3s
CI / Knip (push) Successful in 45s
CI / Go vet (prod) (push) Failing after 1m42s
CI / Frontend a11y check (push) Successful in 2m34s
CI / Go vet (dev) (push) Successful in 2m29s
CI / Staticcheck (prod) (push) Failing after 2m38s
CI / go mod tidy (push) Successful in 1m3s
CI / Staticcheck (dev) (push) Successful in 2m55s
CI / Frontend QC (audit) (push) Successful in 51s
CI / golangci-lint (push) Successful in 3m22s
CI / Go vulnerabilities (push) Successful in 1m26s
CI / Frontend QC (typecheck) (push) Successful in 2m18s
CI / Security scan (prod) (push) Successful in 4m18s
CI / Security scan (dev) (push) Successful in 4m40s
CI / Tests (prod) (push) Has been skipped
CI / Tests (dev) (push) Has been skipped
CI / Race (prod) (push) Has been skipped
CI / Race (dev) (push) Has been skipped
CI / Frontend QC (lint) (push) Successful in 2m18s
CI / Svelte strict check (push) Successful in 43s
test: add coverage tests across backend + fix mock for PENDING checkout support
New test files cover previously untested paths across DAV, validators,
S3, Square, mw, bookings, user, and payments packages.

Includes mock fix: HoldCheckouts flag on MockClient allows tests to
pause auto-complete goroutine for testing PENDING checkout states.

Coverage: 50.4% → 65.0% (+14.6pp)
2026-07-10 18:13:44 +01:00

10 KiB

Test Coverage Improvement Report

Date: 2026-07-10 Overall Coverage: 50.4% of statements Test Command: go test -tags "test,dev" -coverprofile=coverage.out -covermode=atomic ./...


Coverage by Package

Package Coverage Status
handlers/webhooks 100.0%
internal/images 100.0%
internal/jobs 93.5%
mw 89.3% (small gaps)
internal/validators 82.9%
clock 80.0%
internal/square 74.4%
handlers/scheduling 73.1%
handlers/notifications 71.5%
handlers/admin 68.6%
handlers/auth 66.0%
db 66.7%
handlers/today 64.5%
handlers/payments 55.4% 🟡 Moderate
handlers/bookings 47.7% 🟡 Low
handlers/portfolio 48.9% 🟡 Low
auth 45.0% 🟡 Low
handlers/user 39.1% 🔴 Very Low
handlers/services 30.3% 🔴 Very Low
crussell (root) 5.4% 🔴 Critical
internal/dav 0.0% Zero
internal/logutil 0.0% Zero
internal/s3 0.0% Zero
internal/zxcvbnjs 0.0% Zero

Critical Finding: Cross-Package Coverage Blind Spot

Several functions show 0% in their own package but HAVE tests in the admin package. Go's per-package coverage (go test ./handlers/services/) only counts tests within that package. Cross-package test calls (from handlers/admin/) don't count.

Affected handlers (all tested in handlers/admin/ but show 0% in their own package):

Function Actual Coverage Where Tested
handlers/services: ToggleService, CreateServiceHandler, DeleteServiceHandler, AllServicesHandler Tested admin/services_test.go
handlers/user: GetAdminUserHandler, ListAdminUsersHandler, GetEligiblePatchTestServicesHandler, AddPatchTestHandler Tested admin/users_test.go
handlers/bookings: GetAllAdminBookingsHandler, GetAdminBookingHandler, GetAllBookingsByUserHandler, SearchAdminBookingsHandler, etc. Untested No admin tests written
handlers/bookings: AdminListPendingBookingsHandler, AdminGetInProgressBookingHandler Dead code No routes registered in main.go

Priority 1: Immediate Wins (<30 min each, no new dependencies)

1.1 internal/logutil/logutil_test.go — 70 lines, pure stdlib

Functions: ColoredDuration(d time.Duration) string, ColoredRows(n int) string What to test:

  • ColoredDuration: 3 branches (<500ms → green, <5s → yellow, >=5s → red)
  • ColoredRows: singular (n=1) vs plural (n≠1)
  • NO_COLOR env var behavior (ANSI codes vs empty strings) Lines added: ~40

1.2 internal/zxcvbnjs/zxcvbn_test.go — 68 Go lines, 1 public function

Function: Score(password string) (int, error) What to test:

  • Known weak passwords ("password", "123456") → score 0-1
  • Strong passwords → score 3-4
  • Empty string → handle gracefully
  • sync.Once lazy init works across repeated calls Lines added: ~30

1.3 auth/jwt_test.go — Refresh token functions (already have handler-level tests)

Functions: generateRefreshTokenString(), GenerateRefreshToken(ctx, userID, role), VerifyRefreshToken(ctx, tokenString) What to test:

  • generateRefreshTokenString: format (64-char hex) + uniqueness (100 calls)
  • GenerateRefreshToken: stores in DB, returns non-empty token
  • VerifyRefreshToken: verify + token rotation (2nd call fails), expired, revoked Pattern: Already works in handlers/auth/auth_test.go:TestRefreshToken_Generation — adapt for package-level Test infra: auth/testmain_test.go already sets up InitJWT + test DB pool — no setup needed

1.4 handlers/bookings/repo_test.go — 4 trivial DB helpers (~5 lines each)

Functions: GetBookingStatus, GetBookingStartTime, BookingExists, CountUserBookingsInStatus What to test:

  • Happy path: insert booking → call function → assert result
  • Not-found: non-existent ID → assert error Pattern: ctx, tx := testutils.SetupTestTx(t)fixtures.CreateTestUser(tx)fixtures.CreateTestService(tx)fixtures.CreateTestBooking(tx, userID, svcID) → call function directly

1.5 mw/response_test.go — Trivial helpers

Functions: RespondJSON(w, status, data), RespondError(w, status, message) What to test:

  • RespondJSON: basic write, error encoding, nil data
  • RespondError: confirm JSON shape {"error": "..."}
  • Action: RespondError is dead code (never called anywhere) — either remove it or test + start using it Ease: 5-line pure functions, httptest.ResponseRecorder + standard assertions

1.6 internal/dav/types_test.go — Pure string builders

Functions: GenerateICalEvent(EventInput) string, GenerateVCard(ContactInput) string What to test:

  • All-day vs timed events, attendees list, special characters (iCal escaping)
  • vCard formatting with various field combinations
  • Edge cases: empty fields, long strings No DB needed: Pure functions, no dependencies

Priority 2: Medium Effort (follow existing test patterns, 1-2h each)

2.1 handlers/services/services_test.go — Jump 30% → ~90%

What: Move/add in-package tests for ToggleService, CreateServiceHandler, DeleteServiceHandler, AllServicesHandler Why: These are already tested in handlers/admin/services_test.go — just need to replicate in the services package Pattern: Add helper makeAdminContextRequest (5 lines setting mw.UserIDKey + mw.UserRoleKey on chi context like admin/test_helpers.go) Bonus: Remove dead code on lines 200-208 of services.go (unreachable after unconditional return)

2.2 handlers/user/*_test.go — Jump 39% → ~55%

What: Add in-package tests for GetAdminUserHandler, ListAdminUsersHandler, GetEligiblePatchTestServicesHandler, AddPatchTestHandler Why: Same cross-package issue — tested in admin/users_test.go Also add:

  • CreateGuestUserHandler success path (currently only validation failure tests)
  • processProfileImage unit test with a real JPEG file in testdata/
  • GetAdminUserHandler error paths (not-found, social login query error)

2.3 handlers/bookings/admin_*_test.go — Admin GET handler tests

What: Add HTTP handler tests for uncovered admin GET handlers in bookings.go:

  • GetAllAdminBookingsHandler, GetAdminBookingHandler, GetAllBookingsByUserHandler
  • SearchAdminBookingsHandler, GetOverlappingBookings*, GetBookingsBy*Range* Pattern: serveChiHandler with manual admin context injection (already used in overlap_test.go and admin_reserve_test.go) Verify: AdminListPendingBookingsHandler and AdminGetInProgressBookingHandler — confirmed unreachable (no routes). Either remove or add routes + tests.

2.4 mw/ratelimit_test.go — Middleware wrapper tests

Functions (dev stubs): ProgressiveRateLimit, RateLimit What to test: Wrap handler with middleware in dev mode, verify pass-through behavior Pattern: httptest.NewRecorder + httptest.NewRequest + standard HTTP test


Priority 3: Heavier Effort (needs mocks or refactoring, 2-4h each)

3.1 handlers/portfolio/images_test.go — UploadImage (currently 7.5%)

Blockers: s3.Client is nil in tests → handler returns "Storage not configured" early Fix: Mock s3.Uploader interface (already exists), set s3.Client = &mockUploader{} in test setup What to test: Multi-part form with JPEG data, verify 200, verify DB record inserted with correct URLs Also: ListFilters 31.7% — add tests for ?filter[category]=value (dual-query path), ?tag=forest, combined filters

3.2 handlers/user/profile_test.go — UploadProfilePictureHandler (8.8%)

Blocker: Same S3 nil issue as portfolio Fix: Same mock approach — s3.Client = &mockUploader{} Also: updateCardDAV (11.1%) — currently requires DAV_BASE_URL env var and HTTP server. Mock the function or use httptest.NewServer.

3.3 internal/dav/shared_test.go — DB-backed CardDAV operations

Functions: 14 BaseService methods (CRUD for calendars, contacts, events) Requires: PostgreSQL test DB pool (existing pattern in testutils/testdb/) Pattern: Create test fixtures (calendar, contact records), call methods, assert DB state Note: Importing dav package triggers service_dev.go's init() which tries to connect to Postgres. Either set GO_TESTING env var or construct BaseService directly.

3.4 internal/s3/s3_test.go — S3 storage

Approach:

  • Unit tests via Uploader interface mock
  • Connect() integration test requires local S3 (RustFS/MinIO) → tag //go:build integration
  • GetURL URL-builder logic can be tested independently (currently 0% on both build tags)

Dead Code Found During Investigation

Location Function Status
mw/response.go:19 RespondError Defined but NEVER called — remove or wire up
bookings/manage.go:309 AdminListPendingBookingsHandler No route registered in main.go
bookings/manage.go:320 AdminGetInProgressBookingHandler No route registered in main.go
services/services.go:200-208 Duplicate-key error block Unreachable — after unconditional return on line 198

Summary: Coverage Impact by Action

Action Estimated Coverage Gain
Fix cross-package gap (services) +15-20% in handlers/services
Fix cross-package gap (user admin handlers) +10-15% in handlers/user
Add repo.go tests +2-3% in handlers/bookings
Add admin booking GET handler tests +15-20% in handlers/bookings
Auth refresh token tests +15-20% in auth
logutil + zxcvbnjs tests 0% → 80-100% in those packages
RespondJSON test 0% → 100% in mw/response.go
Portfolio S3 mock + tests 7.5% → 60-70% in handlers/portfolio
dav/types pure func tests 0% → ~25% in internal/dav
Remove dead code Removes false-negative 0% entries
Total estimated improvement ~50% → ~65-70% overall