Testing
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.
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.
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.
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.
Next steps
Learn the everyday command map in Bun and turbo, then read the repository layout in Getting started before you add your first suite.