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 already set 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 instance.
New apps created with
npm create @pylonsync/pylon@latest already include bunfig.toml and tests/setup.ts (which registers happy-dom so component tests render), the @testing-library/react package as a dev dependency, 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
Keep the decisions that matter in pure functions underlib/: access and plan gating, pricing, credit math, validation, and formatting. Test them exhaustively. These tests need no server, run instantly, and cover the code where bugs matter most. Keep your query, mutation, and action handlers as thin wrappers around these functions, so you can test the logic without a running app.
tests/credits.test.ts
Tier 2: React components
@testing-library/react and happy-dom are set up in tests/setup.ts. Render a component. 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 cannot cover a case, run pylon dev in another terminal. Call the API the way a client would. resetDb() from @pylonsync/functions clears the in-memory database between test cases. It does nothing if the server is not running, and it refuses to run against production.
tests/things.test.ts
These calls are unauthenticated. They pass only if the surface is reachable by an anonymous client. Server functions default to
auth: "user", so a call with no session gets a 401. An entity with no read policy is default-denied (403). To test this way, mark the function auth: "guest" and give the entity a read policy. Otherwise, authenticate the request first: call 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 setup by hand:bunfig.toml and 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. It hits a running app and reports auth and policy holes (unguarded functions, policies that allow what they should not). Start the app, then run it:
CI
pylon test exits with a non-zero code on failure, so it runs directly in CI:
pylon dev in the background first. Then run pylon test.