Testing
The suite is end-to-end, not unit — it boots the full app against an
in-memory SQLite database and drives it through app.request() (no port,
no server process). 62 tests across two files:
tests/app.test.ts— auth, guards, roles, password reset, Inertia protocol (409/404/SSR), CSRF, health, static assetstests/tus.test.ts— the tus protocol (creation, resume, checksum, termination, ownership) and the profile/avatar flows
Why --isolate is mandatory
Section titled “Why --isolate is mandatory”Each file is written as an independent suite: it sets its env
(DATABASE_PATH=:memory:, UPLOAD_DIR, …) in beforeAll before
importing the app modules (config/db read env at import), and calls
db.close() in afterAll. Bun 1.3 runs all test files in one shared
process, so without --isolate one file’s teardown finalizes the next
file’s prepared statements (“Statement has finalized”) and cached config
leaks across files.
bun run test # = bun test --isolateThe helpers
Section titled “The helpers”const xhr = { "x-inertia": "true" }; // simulate an Inertia SPA request
async function call(path, options) { // app.request with cookie/body return app.request(`${BASE}${path}`, { method, headers, body });}
function sessionCookie(res) { // extract the session cookie const cookie = allSetCookies(res).find((c) => c.startsWith("session=")); return cookie ? cookie.split(";")[0]! : "";}
async function registerUser(email, password = "password123") { const res = await call("/register", { method: "POST", headers: xhr, body: { name: "Test User", email, password }, }); expect(res.status).toBe(303); return sessionCookie(res); // returns the cookie for later calls}The test pattern
Section titled “The test pattern”- Register a user (returns a session cookie).
- Act —
call()the endpoint withxhr+ cookie. - Assert — status, Inertia payload (
component,props.errors,props.auth.user), redirect location, headers.
Inertia XHR requests return JSON page payloads, so assertions are structured:
const data = await res.json();expect(data.component).toBe("Dashboard");expect(data.props.auth.user.email).toBe("loginok@example.com");expect(JSON.stringify(data.props)).not.toContain("passwordHash");Writing a new test
Section titled “Writing a new test”Mirror the helpers at the top of the file and add a case to the relevant
describe block — or a new block. Env is already configured by
beforeAll; if your test needs different env (e.g. tus expiration), set it
in a new test FILE (each --isolate process gets fresh globals).
Keep tests deterministic: no sleeps, no external network (fetch is mocked
in the Google OAuth block), and control time explicitly where it matters
(the expiration sweep takes now as a parameter for this reason).