What the agent does
The agent reads the app to see what can be tested and what is in the way. In nextjs/saas-starter there is no test script, no test file and no .github/ directory; package.json has Next.js 15 canary, React 19.1, TypeScript 5.8, drizzle-orm with drizzle-kit, the postgres driver, jose, bcryptjs, zod and swr, and tsconfig.json maps @/* to the project root. The two modules the prompt names are lib/auth/session.ts, where hashPassword and comparePasswords wrap bcryptjs with ten salt rounds and signToken and verifyToken sign and verify an HS256 JWT with jose that expires in one day, and lib/db/queries.ts, where getUser, getUserWithTeam, getTeamByStripeCustomerId, updateTeamSubscription, getActivityLogs and getTeamForUser run Drizzle queries against the users, teams, team_members and activity_logs tables from lib/db/schema.ts.
Two details decide the setup. session.ts builds its signing key from AUTH_SECRET at module load and getSession and setSession call cookies() from next/headers, so the test sets AUTH_SECRET before importing the module and mocks next/headers with vi.mock for the two cookie functions while testing signToken and verifyToken for real. lib/db/drizzle.ts throws at import when POSTGRES_URL is missing and connects with the postgres driver, so the database tests need a running Postgres rather than a mock, which is what the prompt asks for. The agent starts one in the sandbox, points POSTGRES_URL at it, and applies lib/db/migrations/0000_soft_the_anarchist.sql with pnpm db:migrate, the same drizzle-kit migrate the README uses; pnpm db:seed is skipped because it creates Stripe products and needs a Stripe key.
It adds vitest and @vitest/coverage-v8 as devDependencies, a vitest.config.ts with environment: 'node', an alias for @/ matching tsconfig.json, a setupFiles entry that loads .env.test, and "test": "vitest run" in package.json. lib/auth/session.test.ts checks that a hashed password compares true and a wrong one false, that a signed token verifies to the same user.id and expires, that a token signed with another key is rejected, and that an expired token throws. lib/db/queries.test.ts inserts a user, a team and a membership in beforeEach, truncates the four tables in afterEach, and asserts that getUserWithTeam returns the team id, getTeamForUser returns members with only id, name and email, getTeamByStripeCustomerId finds by the unique column, updateTeamSubscription writes the four subscription fields and bumps updatedAt, and getActivityLogs returns at most ten rows newest first. getUser is tested through the mocked cookie with a valid, an expired and a missing token.
The agent runs pnpm test, then npx tsc --noEmit so the new files type-check with the project's strict settings, and pnpm build to confirm nothing it added leaks into the Next.js bundle. Production files are untouched; the description notes that getUser's expiry check and the cookie read are covered through the mock rather than a browser.
Start it with one API call
POST https://api.prix.dev/o/{org}/sessions with this body:
{
"agent": "claude",
"input": "Add Vitest to this Next.js app and write unit tests for lib/auth/session.ts (password hashing, token signing and verification, including a wrong key and an expired token) and for the Drizzle query helpers in lib/db/queries.ts, run against a real Postgres started in the sandbox with the project's migrations applied rather than against mocks. Mock only next/headers for the cookie functions. Add a vitest.config.ts with the @/ alias and a test script to package.json, run the tests, tsc --noEmit and pnpm build, and open a pull request that lists each test and what it asserts. Do not change production code.",
"resources": [
{
"url": "https://github.com/your-org/your-app"
}
]
}- Get an API key. In the console, open Settings, then API keys, and create a key for your org. Send it in the Authorization header as a Bearer token.
- Send the request. POST the body in request.json to https://api.prix.dev/o/{org}/sessions, with your repository as the resources URL. The run starts in its own cloud sandbox.
- Read what came back. Follow the run on its live stream or wait for the run.completed webhook, then read the result and its trajectory.
What comes back
What comes back is a pull request on a new branch with vitest.config.ts, a .env.test.example, two devDependencies and a test script in package.json, the matching pnpm-lock.yaml change, and two test files: lib/auth/session.test.ts and lib/db/queries.test.ts. Nothing under app/, lib/auth/session.ts or lib/db/queries.ts changes, and middleware.ts is untouched.
The description lists each test by name with the behaviour it asserts, says how Postgres was provided and which migration was applied, and quotes the Vitest summary, the tsc --noEmit result and the pnpm build result. The trajectory keeps the first failing runs too, for example the import error before AUTH_SECRET was set, and the fix.
Before merging, a reviewer checks that the tests clean the tables they write to so they can run in any order, that the next/headers mock is limited to cookies and does not hide a real bug in getUser, that the alias in vitest.config.ts matches tsconfig.json, and that .env.test is gitignored while its example file is committed.
Run it on every new module under lib
Create an automation in the Project with a github trigger on pull requests and this prompt narrowed to the files the pull request adds under lib/. Each run opens its own pull request against the feature branch with tests for the new helpers, written against the same Postgres setup, so coverage grows with the code.
A schedule trigger once a week with the full prompt picks up what slipped through. Give the API key a webhook URL so run.completed arrives with the pull request link and the Vitest summary in result.
The use case this job follows, with its automation and trigger: Write the missing tests.
Why run it on Rush?
The first test in a Next.js app is the expensive one: a runner, an alias, a way around next/headers, a database to run against, and an hour of reading errors about modules that only load inside Next.js. On Rush the agent pays that cost in its own sandbox with a real Postgres, and the pull request arrives with the runner configured and the first two suites green.
Testing the queries against a database rather than a mock means the tests say something about the schema and the migration, not just about the code, and the record shows the migration being applied and the suites passing. Run the same request on each app that still has no test script.
Frequently asked questions
Why a real Postgres instead of mocking Drizzle?
Because the queries are the thing under test: joins, the deletedAt filter, the ten-row limit and ordering. A mocked db would pass whatever the mock returns. The sandbox runs Postgres locally and applies the project's own migration, so the tests see the real schema.
How does it handle cookies() from next/headers?
With vi.mock('next/headers') returning a cookies object whose get yields the token the test wants. Signing and verifying are not mocked, so getUser is exercised with a real valid, expired and missing token against the mocked cookie.
Does it need Stripe?
No. pnpm db:seed would create Stripe products and is skipped; the tests insert their own users and teams. STRIPE_SECRET_KEY is set to a placeholder so modules that import lib/payments/stripe.ts load without a call.
Will the tests run in CI?
The repository has no workflow, so the pull request adds none; the description includes the three commands and the Postgres requirement so you can add a job. A follow-up run can write the GitHub Actions workflow with a Postgres service.
Does it test React components or pages?
Not in this run. The prompt is unit tests for lib/auth and lib/db, which cover the logic that guards every page. Component tests would add jsdom and Testing Library and are a separate request.