---
title: Testing
description: Run vitest suites, bypass login rate limits in tests, and reset demo data.
---

You test the Hono server with vitest suites that boot a real seeded server per run. The config lives in `apps/server/vitest.config.ts`, the suites live in `apps/server/__test__/`, and the seed script lives in `packages/database/src/seed.ts`. Follow this page to run green tests locally and in CI.

### Run the server suite

You run every server test with the workspace script, which maps to `vitest run` in `apps/server/package.json`. The config includes `__test__/**/*.test.ts` with a 30 second timeout and the `forks` pool.

```bash
bun run --cwd apps/server test
bun run test
```

Use the first command when you iterate on the API, and the root turbo command when you want every workspace suite. Suites are feature scoped, so `prescriptions.test.ts`, `sales.test.ts`, and `medicines.test.ts` each cover their own routes end to end. The other packages own their suites too: `apps/register/__test__/` with `vitest run` (including `tauri-command-conformance.test.ts`, which keeps the frontend invoke names in sync with the Rust commands), `packages/database/__test__/` with `bun test`, `packages/shared/__test__/`, and the Rust targets under `apps/console/tests/` declared in `apps/console/Cargo.toml`.

### Understand the global setup

You get a fresh isolated backend on every run from `apps/server/__test__/global-setup.ts`. It requires the bun binary, seeds a temp SQLite file with `bun --cwd packages/database seed` under a throwaway `DATABASE_PATH`, spawns `bun src/index.ts` on a free port with `VITEST=true` and `TRUST_PROXY=true`, and polls `/health` until `database_pool` reports `Connected`.

Helpers in `apps/server/__test__/helpers.ts` read the server URL from the meta file and wrap `fetch` with JSON parsing. Your tests never hardcode a port; always go through `apiUrl()` and `requestJson()` when you add a suite.

### Bypass login rate limits in tests

You authenticate in tests with the same login route your app uses, `POST /auth/login` in `apps/server/src/features/auth/route.ts`, guarded by `loginRateLimit` in `apps/server/src/core/auth/middleware.ts`. The limiter allows five attempts per 60 second window per address, keyed by `LOGIN_ATTEMPT_MAX` and `LOGIN_ATTEMPT_WINDOW_MS` in `apps/server/src/core/auth/login-attempts.ts`, which would flake your suites.

Send the `x-test-bypass: pharmaflow-test` header and the middleware skips the limiter, but only when the server runs with `VITEST=true`. The `loginAs` helper in `apps/server/__test__/helpers.ts` already does this for you.

`requestJson` in `apps/server/__test__/helpers.ts` automatically prefixes `/api/v1` to every path except `/health`, matching the mount in `apps/server/src/app.ts`, so you pass `/auth/login`, not `/api/v1/auth/login`.

```bash
curl -X POST -H "x-test-bypass: pharmaflow-test" \
  -H "Content-Type: application/json" \
  -d '{"email":"admin@demo.pharmaflow","password":"Demo@1234"}' \
  "$API/api/v1/auth/login"
```

Never send this header outside tests. Production never sets `VITEST=true`, so the bypass stays inert there by construction.

### Reset demo data with the seed script

You reset your local database with the seed script from `packages/database/package.json`, exposed as `bun --cwd packages/database seed`. Point `DATABASE_PATH` at a throwaway file when you want to experiment without touching your working data.

```bash
bun --cwd packages/database seed
DATABASE_PATH=/tmp/throwaway.db bun --cwd packages/database seed
```

Seeding restores the demo users (`admin@demo.pharmaflow`, `pharm1@demo.pharmaflow`, `pharm2@demo.pharmaflow`, all `Demo@1234`) and the catalog your tests assume. Reseed whenever your local stock numbers stop matching what the suite expects.

### Add a new suite for your feature

You add one file per feature in `apps/server/__test__/`, named `<feature>.test.ts` to match the `include` glob. Import `apiUrl`, `requestJson`, and `loginAs` from `./helpers`, sign in as the role your route requires, and send the bearer token on every request.

Keep each test independent: create the records you assert on inside the test instead of relying on execution order. Your seeded catalog gives you medicines, users, and branches, so you only create the sale, prescription, or reminder your case needs. Look at `apps/server/__test__/prescriptions.test.ts` for the full login, create, dispense, and assert pattern before you write yours.

### Debug a failing run

You read failures top down: a `seed failed` error means your bun install or `packages/database` state is broken, while `server exited early` means `apps/server/src/index.ts` crashed before `/health` passed. A `loginAs ... failed` error usually means the seed did not run or the demo password changed.

Rerun the single suite with `bun run --cwd apps/server test <name>` to shorten the loop, reseed your working database when stock assertions drift, and confirm `VITEST=true` is set if the rate limiter starts rejecting your logins. Your everyday commands are in [Bun and turbo](/tooling/bun-and-turbo).

### Next steps

Learn the everyday command map in [Bun and turbo](/tooling/bun-and-turbo), then read the repository layout in [Getting started](/getting-started) before you add your first suite.
