# Assignment 4: Testing the Snippet Sharing API ## How to Run Tests ### Unit tests (no database) go test ./internal/auth/... go test ./internal/middleware/... go test ./internal/handler/... ### Store tests (require PostgreSQL) Store tests truncate tables. The database name MUST end with `_test`. `TestMain` refuses to run otherwise. export DB_HOST=localhost export DB_PORT=5432 export DB_USER=postgres export DB_PASSWORD=postgres export DB_NAME=snippet_test go test ./internal/store/... ### E2E tests (require a running server) Start the server in one terminal: go run ./cmd/server Then run the e2e suite in another terminal: API_BASE_URL=http://localhost:8080 go test ./e2e/... ### Full suite go vet ./... go test -race -shuffle=on -count=2 ./... --- ## Bugs My Tests Found The following defects were found in Assignment 3 code while writing tests for Assignment 4, and were fixed. ### 1. `POST /register` returned 400 for duplicate username (should be 409) Found by `TestRegister/duplicate_username_->_409` and `TestE2E_RegisterDuplicate`. Fix: `UserStore.Create` translates PostgreSQL SQLSTATE 23505 to `store.ErrDuplicate`; handler returns `409`. ### 2. `POST /logout` returned 200 with JSON body (should be 204 empty) Found by `TestLogout` and `TestE2E_LogoutInvalidatesToken`. Fix: Handler writes `204 No Content` with empty body. ### 3. `POST /logout` did not verify token exists Found by `TestLogout/unknown_token_->_401`. Fix: Handler calls `SessionStore.Get` first; unknown token → `401`. ### 4. `GET /snippets` returned every user's snippets (mutant M5) Found by `TestListSnippets` and `TestE2E_DataIsolation`. Fix: Added `ListByOwner(ctx, ownerID)` and handler passes caller's id. ### 5. `DELETE /snippets/{id}` had no ownership check (mutant M1) Found by `TestDeleteSnippet/non-owner_->_403`. Fix: Handler fetches snippet first, compares `UserID`, returns `403`. ### 6. `DELETE /snippets/{id}` returned 204 for nonexistent id (mutant M2) Found by `TestDeleteSnippet/nonexistent_id_->_404`. Fix: Store checks `RowsAffected`, returns `ErrNotFound`, handler maps to `404`. ### 7. Error responses used `http.Error` (text/plain, not JSON) (mutant M8) Found by `TestRequireAuth` and `TestE2E_ContentTypeAndSchema`. Fix: Added JSON error helpers setting `Content-Type: application/json`. ### 8. `GET /snippets/{id}` required auth (should be public) (mutant M10) Found by `TestGetSnippet` and `TestE2E_PublicRead`. Fix: Removed middleware wrapper in `main.go`. ### 9. Empty snippet list serialized as `null` (should be `[]`) (mutant M7) Found by `TestListSnippets/no_snippets_->_body_is_literally_[]`. Fix: Store and handler build non-nil slices. ### 10. Snippet responses could include `user_id` (mutant M9) Found by `TestE2E_ContentTypeAndSchema`. Fix: Introduced `snippetResponse` DTO with only `id`, `title`, `content`. ### 11. Malformed JSON returned 500 instead of 400 Found by `TestE2E_BadInput`. Fix: All decoders return `400` on JSON decode error. ### 12. Login failures distinguishable (user enumeration) Found by `TestE2E_LoginFailuresIndistinguishable`. Fix: Same `401` body for wrong-password and unknown-user. --- ## Refactor Summary Handlers and middleware now depend on interfaces defined in their own packages (`UserStore`, `SessionStore`, `SnippetStore`, `SessionLookup`). Concrete types in `internal/store` satisfy these interfaces without any import cycle. Public API behavior is unchanged.