# Write Supertest Tests for an Express API

Yes. An agent maps your Express routes and middleware to the tests that exist, writes Supertest cases for the gaps against a real MongoDB, and opens a PR.

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

## What the agent does

The agent starts by finding out what is already tested, because most Express APIs have some tests and the useful work is in the gaps. In an API like hagopj13/node-express-boilerplate it reads `package.json` and finds Jest 26 and Supertest 6, a `test` script of `jest -i --colors --verbose --detectOpenHandles`, and `yarn.lock`, so it installs with Yarn. `tests/integration/auth.test.js` and `tests/integration/user.test.js` already hold 87 `test(...)` cases between them, with a `describe` block for every route in `src/routes/v1/auth.route.js` and `src/routes/v1/user.route.js`. It lists each route with the status codes its tests assert, so the pull request can show what was missing rather than restate what was there.

Then it reads `src/app.js`, where most of the untested behaviour sits. Every request goes through `helmet()`, `xss()` from xss-clean, `mongoSanitize()`, `cors()` and passport's JWT strategy, then an unknown path falls through to an `ApiError` with `404`. None of the existing tests send a body containing a `$gt` operator or a `<script>` tag, and none hit a path that does not exist. The `authLimiter` in `src/middlewares/rateLimiter.js` allows 20 failed requests per 15 minutes but is mounted only when `config.env === 'production'`, so it is never exercised under `NODE_ENV=test`. The agent writes cases for each: a login with `{"email": {"$gt": ""}}` that must not log anyone in, a registration whose name contains markup that comes back escaped, a `404` with the error handler's body, and a rate-limit test that builds its own small Express app with the real `authLimiter` mounted, since the production check lives in `app.js`.

For auth it adds the token cases the suite skips: an access token signed with the right secret but expired, a refresh token used as an access token, and a user whose role is `user` calling the `manageUsers` routes defined in `src/config/roles.js`. It reuses the existing fixtures in `tests/fixtures/user.fixture.js` and `token.fixture.js` and the `setupTestDB` helper, which connects with `config.mongoose.url` (the `MONGODB_URL` with `-test` appended when `NODE_ENV` is `test`) and clears every collection before each test.

MongoDB runs for real. The agent starts `mongod` in its sandbox, sets `MONGODB_URL` and `JWT_SECRET` the way `.env.example` names them, and runs `yarn test`. The existing tests stub only `emailService.transport.sendMail` so no mail leaves the run, and the new tests follow that rule and nothing more: no mocked models or services. When a new case fails because of the application, the agent keeps it with `test.skip` and a comment saying what the API returned, and lists it in the pull request.

## Start it with one API call

`POST https://api.prix.dev/o/{org}/sessions`

```json
{
  "agent": "claude",
  "input": "Write Jest and Supertest integration tests for every route in this Express API that lacks them, against a real MongoDB started in the sandbox, covering auth failures and validation errors. First list each route and middleware in app.js and the routes directory with the cases the existing tests already cover, and only add what is missing: operator injection and markup in request bodies, unknown paths, expired and wrong-type tokens, role checks, and any middleware that only runs in production, tested by mounting it on a small app in the test. Reuse the existing fixtures and test database helper, and do not mock models, services or the database; stubbing outbound email is fine. Run yarn test. If a new test fails because of the application, keep it skipped with a comment saying what the API returned. Open a pull request with the before and after table of routes and cases.",
  "resources": [
    {
      "url": "https://github.com/your-org/your-api"
    }
  ]
}
```

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

The pull request adds test files under `tests/integration/`, for a repository shaped like this boilerplate typically `security.test.js` for sanitizing, escaping and the `404` fallback, and `rateLimiter.test.js` for the limiter, plus new `test(...)` cases appended to `auth.test.js` and `user.test.js` for tokens and roles. It changes nothing under `src/`. The description opens with the table of routes and middleware, the cases that existed and the cases added, so the reviewer reads the coverage change without opening every file.

The proof is the `yarn test` output kept in the session from the agent's own sandbox, with Jest's verbose list of every test name and its result, and the repository's CI on the pull request if it has one. A skipped test appears both in that output and in the description, with what the API returned.

Before merging, a reviewer checks that the injection and markup cases assert on the database state as well as the response, that the rate-limit test cannot leak its counters into other tests, and that each skipped test is a real defect the team wants to fix rather than a test written against the wrong expectation.

## Run it on every pull request that adds a route

Run it from a GitHub `pull_request` webhook. When a pull request changes a file under `src/routes/` and nothing under `tests/`, your server sends this request with the pull request URL in `resources`, so the agent checks out that branch and writes the missing cases for the routes it adds. Each pull request gets its own session in its own sandbox, with its own MongoDB.

Set a webhook URL on the API key and Rush POSTs `run.completed` or `run.failed` when the turn finishes, with the session URL, so your server can link the test pull request from the original one.

## Why run it on Rush?

Testing an Express API against a real MongoDB on a laptop means a local `mongod` or Docker container, an `.env` file, and a test database that holds whatever the last interrupted run left behind. On Rush the agent starts MongoDB in its own sandbox, runs `yarn test` there with Jest's `--detectOpenHandles` on, and none of it touches your machine. Each run starts from an empty database, so a passing suite is not passing because of leftover rows.

What comes back is checkable. The session keeps each `yarn test` run with its full Jest output, so the claim that a `$gt` login is refused can be traced to the test and the run that shows it. The same request can go to several Express services at once, each in its own sandbox with its own database, each with a pull request someone reviews.

## Frequently asked questions

### My API already has tests. Will it duplicate them?

The prompt asks it to list what the existing tests cover first and add only what is missing, and the pull request description shows that table. In this boilerplate every route already has a `describe` block, so the new work is the middleware and token cases those blocks do not reach.

### Does it need a MongoDB I host?

No. It starts MongoDB inside its own sandbox and points `MONGODB_URL` at it, the variable your config already reads. Nothing in the tests connects to your development or production database, and the database is gone when the sandbox is.

### Will it use mongodb-memory-server?

Only if your repository already does. It runs the same `mongod` binary either way, but adding a package that downloads MongoDB on install changes your dependencies and CI, so by default the agent uses a real server started before the tests, matching how `setupTestDB` connects.

### Does it work with Vitest or Mocha instead of Jest?

Yes. Supertest takes the Express `app` and works under any runner. The agent reads which runner your `test` script calls and writes the new cases in that style, with the same `describe` names and assertion library the existing files use.
