All jobs

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 RushIn 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 with this body:

{
  "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.

The use case this job follows, with its automation and trigger: Write the missing tests.

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.