Files
W04_048_Zagar_Database/README.md
T

80 lines
3.5 KiB
Markdown

# Snippet Sharing API
A Go + PostgreSQL REST API for sharing code snippets (Assignment 3), with a full test suite (Assignment 4).
**Name:** Zagar Nashrullah
**NRP:** 5803025048
Module: `5803025048/W04`
## Run the server
Set the database variables, then start the server. It listens on `PORT` (default `8080`).
```powershell
$env:DB_HOST="..."; $env:DB_PORT="..."
$env:DB_USER="..."; $env:DB_PASSWORD="..."; $env:DB_NAME="..."
go run ./cmd/server/main.go
```
`PORT` is the HTTP port of the API. `DB_PORT` is the PostgreSQL port.
## Run the tests
Each layer has its own command:
```
go test ./internal/auth/... # unit tests
go test ./internal/middleware/... # unit tests, hand-written fake
go test ./internal/handler/... # unit tests, hand-written fakes
go test ./internal/store/... # integration tests, real PostgreSQL
go test ./e2e/... # black-box tests over HTTP
```
What each layer needs:
- **auth, middleware, handler:** nothing.
- **store:** the `DB_*` variables, and `DB_NAME` **must end in `_test`**.
- **e2e:** a running server. It uses `API_BASE_URL` (default `http://localhost:8080`).
To run everything the way the grader does (start the server first, because `./...` includes e2e):
```
go vet ./...
go test -race -shuffle=on -count=2 ./...
go test -coverpkg=./internal/... -coverprofile=cover.out ./internal/...
```
### Warning: store tests wipe the database
The store tests `TRUNCATE` all tables at the start of every test. For safety, `TestMain` in `internal/store` refuses to run unless `DB_NAME` ends in `_test` (for example `w04_5803025048_test`). Never point them at a real database.
If the `DB_*` variables are missing or the database is unreachable, the tests fail. They never skip.
The e2e tests create their own unique usernames, so they work against a server that already has data.
## Bugs My Tests Found
Defects in my Assignment 3 code that these tests exposed, and how I fixed them:
1. **Errors were not JSON.** Every 400/401/403/404/409/500 used `http.Error`, which sends `text/plain`.
*Fix:* a JSON error writer in the handlers and in the middleware.
2. **A token without the `Bearer` scheme was accepted.** The middleware and logout used the whole header as the token.
*Fix:* one strict `middleware.BearerToken` helper. A missing scheme, `Basic ...`, or an empty token now gives 401.
3. **Logout returned 204 for tokens that did not exist.**
*Fix:* logout checks that the session exists and returns 401 otherwise.
4. **"Not found" leaked the database driver.** Handlers compared against `pgx.ErrNoRows`, and `SnippetStore.Delete` returned `nil` for an unknown id.
*Fix:* added `store.ErrNotFound`, returned by every lookup. `Delete` returns it when no row was affected.
5. **Passwords longer than 72 bytes caused a 500 on `/register`** (bcrypt rejects them).
*Fix:* they now return 400.
6. **Username length was counted in bytes, not characters.**
*Fix:* it is now counted in runes.
7. **`GET /snippets` could return `null` for an empty list.**
*Fix:* the handler always returns `[]`, even if the store returns a nil slice.
8. **Any login lookup error was reported as 401.**
*Fix:* a real database failure is now 500. Only "user not found" and "wrong password" give the (identical) 401.
## Refactor for testability
No behavior change. Handlers depend on the `UserStore`, `SessionStore` and `SnippetStore` interfaces (package `handler`). The middleware depends on a small `SessionLookup` interface. The concrete types in `internal/store` satisfy them without importing `handler` or `middleware`.