Files

3.5 KiB

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).

$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.