# Write Tests for a Django App

Yes. An agent finds thin spots in a Django app's TestCase suite, writes tests for untested branches, runs manage.py test, and opens a PR.

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

## What the agent does

The agent starts from the project's own test style instead of inventing a second framework. In a Django app like healthchecks/healthchecks it reads `requirements.txt` and finds `Django[argon2]==6.1.2`, `psycopg==3.3.6` and the rest of the runtime pins, then opens `manage.py`, which sets `DJANGO_SETTINGS_MODULE` to `hc.settings`. Default `DATABASES` in `hc/settings.py` is SQLite at `hc.sqlite` with `PRAGMA busy_timeout = 5000` and `transaction_mode` `IMMEDIATE`; `DB=postgres` switches the engine. Tests subclass `django.test.TestCase` or `TransactionTestCase`, and the shared helpers live in `hc/api/tests/__init__.py` as `BaseTestCase` with an Alice user, project and CSRF-aware client.

Next it compares production modules under `hc/front` and `hc/api` with the existing suites. `hc/front/tests/` already holds dozens of files such as `test_basics.py` and `test_add_check.py`, and `hc/api/tests/` covers notify paths with `TransactionTestCase` setup that builds a `Check`, `Channel` and `Flip`. The thinner spots are usually the management commands with no matching test file (`hc/api/management/commands/pruneobjects.py`, `prunetokenbucket.py`, `settelegramwebhook.py`, `submitchallenge.py`) and front commands like `populate_searchdb.py` and `render_docs.py`, plus branches in `hc/front/validators.py` (`WebhookValidator`, `CronValidator`, `OnCalendarValidator`) and API auth in `hc/api/decorators.py` (`authorize`, version sniffing for `/api/v2/` and `/api/v3/`).

For each gap it writes a new `test_*.py` next to the matching suite, importing the same models (`hc.accounts.models.Project`, `hc.api.models.Check`, `Channel`, `Flip`) and asserting through `self.client` or the command's `call_command` the way neighbouring tests do. It does not rewrite production views or transports. When a branch needs settings, it uses `@override_settings` the way `BasicsTestCase` does for `DEBUG`, `SECRET_KEY` and `REGISTRATION_OPEN`.

Before opening the pull request it installs from `requirements.txt` and `requirements-dev.txt` (which pins `django-stubs`, `mypy` and `time-machine`) and runs `python manage.py test` against the SQLite test database. Failures stay in the sandbox until the new cases pass. The pull request lists which modules were thin, which test files were added, and that production code was left alone.

## Start it with one API call

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

```json
{
  "agent": "claude",
  "input": "Find the integration modules in hc/front and hc/api with the fewest tests, write Django TestCase tests for their untested branches, run python manage.py test, and open a pull request. Change no 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. In a repository shaped like healthchecks/healthchecks it adds files under `hc/front/tests/` and `hc/api/tests/` (and sometimes `hc/integrations/<kind>/tests/` when an integration's notify path is the thin spot), each subclassing `TestCase` or `TransactionTestCase` and following the Alice/project fixtures from `BaseTestCase`. Production modules under `hc/front`, `hc/api` and `hc/integrations` are untouched.

The pull request description names the modules that had the fewest tests, the branches each new case exercises, and the exact `python manage.py test` invocation that passed in the sandbox. The session trajectory keeps every command and its output, including database setup and any settings overrides.

Before merging, a reviewer checks that the new tests assert behaviour rather than private helpers, that they use the project's `BaseTestCase` patterns instead of a one-off harness, and that no production file slipped into the diff. Once merged, the same suite runs in the repository's existing Tests workflow.

## Run it on every pull request labelled needs-tests

Add a GitHub repository webhook for `pull_request` events and, when the label `needs-tests` is applied, have your server POST this same body with that repository in `resources` and the open pull request's branch named in the prompt. Each labelled pull request gets its own sandbox run and its own follow-up commit or sibling pull request with the missing TestCase coverage.

Set a webhook URL on the API key so Rush POSTs `run.completed` or `run.failed` when the turn finishes. A key runs at most `max_concurrent` sessions at once (4 by default) and answers `429` past that, so a backfill across many open pull requests should space its starts.

## Why run it on Rush?

Filling Django coverage on a laptop means installing the right Django and database pins, waiting on `manage.py test`, and iterating while your working tree holds half-written cases. On Rush the agent does that loop in its own sandbox with its own clone: it reads the real `TestCase` style, writes only test files, and keeps `python manage.py test` green before you see the pull request. Your machine and your branch stay clean.

The result is checkable rather than claimed. The new tests run in the sandbox with the project's SQLite (or Postgres when `DB=postgres`) settings, the trajectory keeps the command output, and the pull request is the only thing that reaches GitHub. The same request can land missing tests on several Django apps at once, each reviewed before merge.

## Frequently asked questions

### Will it change my views, models or transports?

The prompt says to change no production code, and the agent keeps the diff to `test_*.py` files under the existing test packages. If a test reveals a real bug, it stops and describes the failure in the pull request instead of silently patching the bug.

### Does it use pytest or Django's test runner?

It follows the repository. In a Healthchecks-shaped app that means `python manage.py test` and `django.test.TestCase`, not a new pytest layout. If your app already uses pytest-django, it extends that layout instead of introducing a second runner.

### What database does it run against?

Whatever `hc/settings.py` (or your settings module) uses for tests. Healthchecks defaults to SQLite at `hc.sqlite` and switches to Postgres when `DB=postgres`. The agent does not invent a second database configuration.

### Can it cover management commands and validators?

Yes. Those are often the thinnest modules: commands under `hc/*/management/commands/` with no matching test file, and validators in `hc/front/validators.py`. It calls them through `call_command` or form cleaning the way neighbouring tests already do.
