Assignment 4: add unit, integration, and e2e tests; refactor for testability

This commit is contained in:
angelicatesvara07-crypto
2026-10-03 19:09:40 +07:00
parent 4ac21847fc
commit 3945920abb
24 changed files with 2753 additions and 147 deletions
+100
View File
@@ -0,0 +1,100 @@
# 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.