# Write Vitest Tests for a Next.js App

Yes. An agent wires Vitest into a Next.js app with no tests, tests session signing and the Drizzle queries against a real Postgres, and opens a pull request.

[Run this on Rush](https://cloud.getrush.ai/run/write-vitest-tests/nextjs/), in a Project that has your repository.

## 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`

```json
{
  "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"
    }
  ]
}
```

1. **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.
2. **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.
3. **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`.

## 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.
