slack5-1770544098/.sdlc/features/user-preferences/review.md
rdev-worker 32982b089f
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
build: /review-feature user-preferences
2026-02-08 10:51:39 +00:00

6.6 KiB

Code Review: User Preferences API

Summary

Overall assessment: NEEDS_FIX

The implementation is well-structured and follows the hexagonal architecture pattern correctly. Domain validation, service layer, handlers, routes, OpenAPI spec, and database migration are all present and properly wired. Tests pass and cover the key scenarios. However, there is one blocker (compiled binary committed to git) and several warnings that should be addressed before merging.

Findings

Blockers

  • services/preferences-api/serverCompiled binary (12MB) committed to git — A Go compiled binary has been tracked and committed to the repository. This bloats the repo, is platform-specific, and should never be checked in. Remove it from git tracking with git rm services/preferences-api/server and add it to .gitignore.

Warnings

  • services/preferences-api/internal/domain/preferences.go:34-43Map iteration order is non-deterministicValidatePreferences iterates over the map and returns on the first error. Due to Go's random map iteration, the same invalid input can produce different error messages across runs. This is not a bug per se, but makes error messages inconsistent for users sending multiple invalid keys. Consider sorting keys before validation or collecting all errors.

  • services/preferences-api/internal/api/handlers/preferences.go:47-65Missing domain error mapping on Get path — The Get handler returns h.svc.Get(...) errors raw (line 60-61), while the Update handler correctly maps domain errors via mapPreferencesDomainError. While Get currently cannot produce domain validation errors, any future change to the service's Get method that introduces domain errors would leak as 500s. Consider applying mapPreferencesDomainError consistently.

  • services/preferences-api/internal/domain/preferences.goNo domain-level unit tests — The QA plan specifies 8 domain validation unit tests (DV-1 through DV-8), but there are no test files in the domain package. Domain validation is tested indirectly via the service layer tests, but direct domain tests provide better isolation and clearer failure diagnostics. AC-13 specifically requires "Domain layer validates preference keys and values independently of HTTP layer."

  • services/preferences-api/internal/api/handlers/preferences_test.go:80-82auth.SetUser used directly in tests bypasses middleware — The handler tests use withAuthUser to inject the user into context, but there is no test verifying that the auth middleware itself is applied to the route group. Auth enforcement is tested by convention (route registration review) rather than by execution. A test that hits the route without auth context would provide stronger assurance, though this may require middleware integration testing.

  • services/preferences-api/internal/api/routes.go:32Auth is conditionally applied based on cfg.AuthEnabled — If AUTH_ENABLED is not set to "true", all preference endpoints are completely unauthenticated. While this is useful for development, it means auth.MustGetUser in the handler will panic in production if the middleware isn't applied. Consider either: (a) documenting this clearly, (b) using auth.GetUser with a nil check instead of auth.MustGetUser, or (c) making auth always required in production builds.

Suggestions

  • services/preferences-api/internal/adapter/postgres/preferences.goNo adapter-level tests — The PostgreSQL adapter has no test coverage. While testing against a real database is harder, the adapter could be tested with integration tests or at minimum documented as requiring manual verification.

  • services/preferences-api/internal/api/handlers/preferences_test.go:21-22Test user UUIDs don't matchotherUserID is 550e8400-e29b-41d4-a716-446655440001 which only differs from testUserID (...440000) by the last digit. This works but could be more distinctive for readability (e.g., a completely different UUID).

  • services/preferences-api/internal/api/spec.go:24-30UpdatePreferencesRequest schema doesn't mark individual preference keys as optional — The OpenAPI schema has preferences as required, but individual preference keys within the object aren't marked as optional. Since partial updates are supported, the schema should indicate that individual preference fields are optional to accurately document the API behavior.

Spec Alignment

The implementation matches the spec well across all major requirements:

Spec Requirement Status Notes
GET returns preferences as key-value pairs OK
GET returns empty preferences (not 404) for new users OK Service layer creates empty struct
PUT creates or updates (upsert semantics) OK PostgreSQL ON CONFLICT
PUT supports partial updates (merge) OK JSONB `
Supported preference keys validated OK Domain layer enforces closed set
Invalid values rejected with 400 OK Domain validation with descriptive errors
Unknown keys rejected with 400 OK
Both endpoints require authentication (401) OK Via auth.Middleware (when enabled)
Users can only access own preferences (403) OK checkOwnership in both handlers
PostgreSQL persistence OK
Standard {data, meta} envelope OK Via httpresponse.OK
OpenAPI spec documented OK Full spec with schemas, params, security
Domain layer validates independently Partial Validation exists but no direct unit tests
Hexagonal architecture followed OK Clean separation across all layers
Service layer unit tests OK 10 test cases
Handler integration tests OK 10 test cases

Test Coverage Assessment

Covered by tests:

  • Service layer: Get (empty user, existing user, repo error), Update (valid prefs, unknown key, invalid theme, invalid language, invalid notifications_enabled, repo error, partial merge)
  • Handler layer: Get (200 existing, 200 empty, 400 invalid UUID, 403 mismatch), Update (200 success, 400 unknown key, 400 invalid value, 400 missing prefs, 400 invalid UUID, 403 mismatch)

Missing test coverage:

  • Domain validation unit tests — DV-1 through DV-8 from QA plan are not implemented
  • Edge case EC-3 — Empty preferences object {} on PUT (what happens?)
  • Error cases ER-20, ER-21, ER-22 — Null values, nested objects, arrays as preference values
  • Auth middleware integration — No test verifying unauthenticated requests get 401
  • PostgreSQL adapter — No tests for the adapter layer

All tests pass:

  • go test ./... — 20 tests, all PASS
  • go vet ./... — clean