Nobody has started this yet — be first.
Business impact
Categories that still have expenses pointing at them are protected from deletion at the database level (a foreign key constraint) -- that part still works. But the application is supposed to translate that DB-level rejection into a clean, actionable 409 Conflict ("category is still used by existing expenses"). Right now it doesn't recognize the error and falls through to a generic 500, which looks like the server crashed instead of telling the user exactly what to do (remove or reassign the expenses first). This is a support-ticket generator: users will report "the app is broken" for entirely expected, preventable behavior.
Problem
Repo.Delete checks the Postgres error code for a foreign-key violation using the wrong SQLSTATE -- it compares against unique_violation (23505) instead of foreign_key_violation (23503), so the in-use case is never actually recognized and falls through to a plain 500.
Current behavior
Deleting a category that still has expenses against it returns a bare 500 Internal Server Error instead of a clean 409 Conflict explaining why.
Expected behavior
Deleting a category still referenced by at least one expense returns 409 with a clear message, not 500.
Steps to reproduce
Create a private category, then create an expense against it. DELETE /categories/{id} for that category, with a valid token.
Why this matters
23503 (foreign key) and 23505 (unique) are both common Postgres constraint-violation codes and easy to mix up when writing this kind of error-mapping code from memory -- a single wrong constant, still compiles, still type-checks, only shows up when the specific constraint actually fires.
Suggested approach
Compare the SQLSTATE string Repo.Delete checks for against the one Repo.Create/Repo.Update check a few lines above (they're handling a different constraint -- read the migration's comments on both indexes/constraints to see which code belongs to which check).
Acceptance criteria
Verification
go test ./practicetickets/... -run TestTicket01 -v
Hints (0/2)
Try it without hints first — the reading is the exercise.
Working on this ticket
Work on a branch named for the ticket — that's what you'll submit.
Branch off your fork
$git checkout -b fix/category-delete-returns-500Fix it and commit
Meet every acceptance criterion, and add a test that would have caught this.
Push the branch
$git push -u origin fix/category-delete-returns-500Submit it below
Paste your fork URL and the branch name, with a short write-up of the root cause.
Questions
Ask about anything unclear in the ticket — the maintainer and anyone who has solved it can answer. Please don't post full solutions.
Sign in to ask a question or reply.
Sign indocker-compose.ymlmigrations/0001_init.sqldocker-entrypoint-initdb.dexpense_trackerdbappcurl http://localhost:8082/healthz
To run locally against your own Postgres instead: cp .env.example .env (edit if your Postgres isn't on localhost:5434), then make migrate (applies migrations/*.sql via psql) and make run (go run ./cmd/server).
Work the tickets in PRACTICE_TICKETS.md (TICKET-01 through TICKET-10); each names one Go test in practicetickets/:
go test ./practicetickets/... -run TestTicket01 -v # a single ticket
./practice_tickets_run.sh # all 10, clean pass/fail summary
This project's own integration suite (cmd/server/*_test.go) needs a real, reachable Postgres -- point TEST_DATABASE_URL at any empty/disposable database (migrations are applied automatically, and every test truncates+reseeds before it runs) and run make test. The practicetickets/ suite defaults to postgres://postgres:postgres@localhost:5434/expense_tracker_test if TEST_DATABASE_URL is unset.
Level 1
Fix a bug
Read existing behaviour, correct it.