my-library

Testing

Eight tests against a real Postgres that lives inside the test process, plus a Postman collection that doubles as a deployment check.

The harness

Tests run against pglite — Postgres compiled to WebAssembly, running in-process. Not a mock, not SQLite pretending: the actual engine, with the real migrations applied, and no server or network anywhere.

tests/helpers/db.ts
const pg = new PGlite();                                   // in memory
const db = drizzle(pg, { schema, casing: "snake_case" });
await migrate(db, { migrationsFolder: "db/migrations" });  // the real SQL
setDatabase(db as unknown as Database);                    // inject

That last line is the whole trick, and it is what the lazy proxy in db/client.ts exists for (see Database). Because no connection opens until first use, a test can substitute the database before route handlers ever run — so the handlers are tested unmodified, importing the same db they use in production.

Why this matters more than the count

Eight tests is not many. But they exercise real SQL: identity columns, the ownership WHERE clauses, timestamp defaults, RETURNING. A mocked Drizzle would have passed while the actual queries were wrong. The migrations are also under test — if a generated migration is broken, every test fails at setup.

What is covered

TestGuards
POST accepts a single bookThe one-or-many normalisation.
POST accepts an arrayBatch insert in one statement.
GET lists the caller's booksScoping and ordering.
GET /:id, unknown id is 404That a missing row is not an exception.
PUT updates and bumps updatedAtPartial update, and that the timestamp moves.
DELETE removes, re-fetch is 404Hard delete really deletes.
No token is 401The auth gate, on the handler rather than in theory.
mapDoc maps OpenLibrary fieldsThe import's field mapping — pure, so no database needed.
npm test        # node --import tsx --test tests/*.test.ts
npm run typecheck

The gaps, stated plainly

  • No tests on lib/queries.ts. Paging, search and the Bayesian rating order are all untested. That is the largest hole: the rating expression is hand-written SQL and exactly the kind of thing that should be pinned by a test.
  • No component tests. The web pages are verified by looking at them.
  • No test for the enrichment scripts beyond mapDoc. The resumability logic — the part most likely to be subtly wrong — is covered only by having been run.

CI

.github/workflows/ci.yml
typecheck + test, on every push and pull request

It needs no secrets, because pglite means the suite has no external dependency. Deployment is Vercel’s Git integration and is independent of this workflow — CI reports, it does not gate.

Postman as a smoke test

postman/ holds a collection of 12 requests with 51 assertions. It is git-ignored — it is local tooling, not part of the app — but it is the fastest way to check a real deployment, because every request carries tests:

npx newman run postman/my-library.postman_collection.json \
  --env-var "baseUrl=https://mylibrary.mblrc.me" \
  --env-var "apiToken=$TOKEN"

The Books folder runs as a flow: create captures the new id into a variable, the following requests reuse it, and delete removes it. The batch-create request deletes its own two rows in a test script. A full run against production leaves the library exactly as it found it, which is what makes it safe to point at a live deployment.

The error folder covers 401 (no token and bad token), 404, and 422 for both invalid fields and malformed JSON — the failure modes the clients map onto typed errors.