import { test, expect } from "bun:test"). The pylon test command discovers your test files and runs them; new projects scaffold with a test script, a starter test, and React component testing wired up.
pylon test discovers every *.test.ts / *.test.tsx (and .js/.jsx) file under tests/ (or functions/) and runs each with Bun against an in-memory Pylon.
New apps created with
npm create @pylonsync/pylon@latest already include bunfig.toml + tests/setup.ts (which registers happy-dom so component tests render), the @testing-library/react devDeps, and a starter test under tests/.Three tiers
Reach for the cheapest tier that proves what you need. Most of your coverage should be Tier 1.Tier 1 — pure logic (do this most)
Keep the decisions that matter — access and plan gating, pricing, credit math, validation, formatting — in pure functions inlib/, and test them exhaustively. No server, instant, and it’s where the bugs that matter live. Keep your query / mutation / action handlers as thin wrappers around these so the logic is testable without a running app.
tests/credits.test.ts
Tier 2 — React components
@testing-library/react + happy-dom are wired via tests/setup.ts. Render a component and assert on the DOM. The templates use the classic JSX transform, so add import React from "react" in .tsx tests.
tests/button.test.tsx
db.useQuery, callFn), mock the boundary with mock.module, then dynamic-import the component so the mock is in place first:
Tier 3 — functions over HTTP
A handler’s full behavior — policies,ctx.db, auth — only exists in the running app. When Tier 1 can’t cover it, run pylon dev in another terminal and call the API the way a client would. resetDb() from @pylonsync/functions clears the in-memory database between cases (it no-ops if the server isn’t up, and refuses to run against production).
tests/things.test.ts
These calls are unauthenticated, so they only pass if the surface is reachable by an anonymous client: server functions default to
auth: "user" (a call with no session gets a 401), and an entity with no read policy is default-denied (403). Mark the function auth: "guest" and give the entity a read policy to exercise it this way — otherwise authenticate the request first (POST /api/auth/guest for a guest session, then send the token as Authorization: Bearer <token>).Adding tests to an existing project
If your project predates the test scaffolding, add the on-ramp by hand:bunfig.toml + tests/setup.ts are only needed for Tier 2 component tests — pure-logic tests run without them.)
Security probe
pylon test:security is a separate adversarial probe, not a unit-test runner. It hits a running app and reports auth/policy holes (unguarded functions, policies that allow what they shouldn’t). Start the app, then run it:
CI
pylon test exits non-zero on any failure, so it drops straight into CI:
pylon dev in the background first, then run pylon test.