# Add Missing Tests to Every Pull Request in a Django App

Yes. An agent reads each pull request, writes Django TestCase tests for what it adds to your apps, runs manage.py test and pushes them to the branch.

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

## What the agent does

The agent starts from the pull request's diff, not from a coverage report. In a Django codebase like django/djangoproject.com, a change to the blog app lands in `blog/models.py` (the `Entry` and `Event` models and their managers), `blog/views.py`, `blog/feeds.py` or `blog/templatetags/weblog.py`; a change to the docs app lands in `docs/models.py`, `docs/views.py`, `docs/search.py` or `docs/templatetags`. For each changed function it searches the existing tests for one that reaches the new branch and writes down the behavior that nothing exercises yet.

Then it follows the layout each app already uses. The blog app keeps every test in one `blog/tests.py`, grouped as `EntryTestCase`, `EventTestCase` and `ViewsTestCase`, with a `DateTimeMixin` for dates and `time_machine` for frozen clocks. The docs app has a `docs/tests/` package split by concern: `test_models.py`, `test_views.py`, `test_templates.py`, `test_commands.py`, `test_builder.py` and `test_utils.py`. A new manager rule goes into the matching class in `blog/tests.py`; a new docs view goes into `docs/tests/test_views.py`. Tests that need a default docs release use the `ReleaseMixin` from `djangoproject/tests.py`, whose `setUpTestData` creates the `Release` and `DocumentRelease` rows, rather than building them again.

It sets the project up the way the repository's own workflow does, because a Django test run that cannot reach its database proves nothing. `.github/workflows/tests.yml` installs `requirements/tests.txt` on Python 3.12 (the version `pyproject.toml` pins with `requires-python = "==3.12.*"`), starts a Postgres 17 service, creates the `djangoproject` and `code.djangoproject` databases, loads `tracdb/trac.sql`, and writes a `conf/secrets.json` beside the checkout. The agent does the same in its sandbox, then runs the apps it touched with `python manage.py test blog docs --verbosity 2`, the same `manage test` the Makefile's `test` target wraps in `coverage run`.

When a new test fails, the agent reads why. If the test was wrong, it fixes the test. If the application code is wrong, it does not change it, because the prompt forbids that: it leaves the failing test out of the commit and explains the bug in its comment. It also leaves the Playwright tests alone unless the change is in a template they cover, since those need browsers the `make ci` target installs separately.

## Start it with one API call

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

```json
{
  "agent": "claude",
  "input": "This pull request changes the docs and blog apps. Read the diff against the base branch and list the new or changed behavior in docs/ and blog/ that no existing test covers. Set up the project the way .github/workflows/tests.yml does: Python 3.12, requirements/tests.txt, a Postgres database and conf/secrets.json. Write Django TestCase tests for each item next to the existing tests: blog/tests.py for the blog app, the matching module in docs/tests/ for the docs app. Reuse ReleaseMixin, DateTimeMixin and time_machine where they fit. Run python manage.py test blog docs until the new tests pass. Do not change application code: if a test fails because the code is wrong, leave it out and describe the failure. Push the tests to this pull request's branch and post a comment listing each test and the behavior it covers.",
  "resources": [
    {
      "url": "https://github.com/your-org/your-app/pull/123"
    }
  ]
}
```

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 commit on the pull request's branch that adds test methods and nothing else. In a project shaped like djangoproject.com the diff touches `blog/tests.py` and one or more files in `docs/tests/`: new `test_` methods in the existing classes where the behavior belongs, or a new `TestCase` class when the pull request added a whole new view or command. Model factories are plain `Entry.objects.create(...)` calls, as the existing tests use, so no new dependency appears in `pyproject.toml`.

The agent's comment on the pull request names each test, the behavior it pins ("an unpublished entry 404s for a user without change permission") and the `manage.py test` command it ran. When a test caught a real defect in the change, the comment quotes the failing assertion and points at the line in the app instead of committing a red test. The repository's own Tests workflow then runs on the new commit with coverage, and that check is what a reviewer trusts.

Before merging, a reviewer checks that each test asserts the intended behavior rather than snapshotting today's output, that frozen times with `time_machine` match the dates the test claims, and that no test writes outside the test database. They can read the run's trajectory to see the exact commands and the output of each test run.

## Run it on every pull request opened

Add an automation in the Project connected to your Django repository, give it this prompt, and add a GitHub trigger on `pull_request.opened` with `branch_patterns` set to the branch you merge into. Every new pull request then starts one run whose prompt includes the pull request's number, URL, title, author and base branch. Only authors who can write to the repository fire it; a pull request from a fork never does, so code from outside contributors does not start these runs.

To start runs from your own code instead, receive GitHub's `pull_request` webhook and on `opened` send this request with `https://github.com/<owner>/<repo>/pull/<n>` in `resources`; Rush clones that pull request's branch. A key runs at most `max_concurrent` sessions at once (4 by default) and answers `429` past that, so queue requests on a busy day.

## Why run it on Rush?

A full Django test setup is the slow part of this job: a Postgres service, two databases, a fixture SQL dump, a secrets file and the right Python. On a laptop you build that once and then keep switching branches inside it. On Rush each pull request gets a fresh sandbox at its own branch, the agent builds that setup there, and your local database and virtualenv are never touched, even when three pull requests open in the same hour.

What it hands back can be checked rather than trusted. The trajectory keeps every `manage.py test` command and its output, so a reviewer can see that the tests failed before a fix to the test and passed after, and the repository's own workflow reruns them on the pushed commit. The tests arrive inside the pull request they cover, while the author still remembers why the code changed.

## Frequently asked questions

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

It uses whatever the repository uses. djangoproject.com runs `python manage.py test` with `django.test.TestCase` and `SimpleTestCase`, so the new tests are `TestCase` methods. In a project with `pytest-django` configured, the same prompt with `pytest` in place of `manage.py test` gets pytest functions instead.

### Will it need my production database or secrets?

No. It creates a local Postgres database in the sandbox and writes a `conf/secrets.json` with throwaway values, the same way the repository's CI does. Tests never read production data, and nothing it writes for setup is committed.

### What if the pull request has no testable behavior?

A pull request that only changes a template's markup, a translation or a migration with no logic gets a short comment saying there was nothing new to cover and why. The agent does not add tests for the sake of a number.

### Can it break the existing test suite?

It only adds test methods and runs the apps it touched before pushing, so an existing test that fails after its commit fails in its own output first. If an existing test was already failing on the branch, the comment says so, so the agent's tests are not blamed for it.
