Skip to content
Developer Guide

Testing

Running RedScribe's Django test suite locally, the testing style used throughout the codebase, and the checks to run yourself since there's no CI.

This page covers running RedScribe’s Django test suite, the testing conventions used across apps, and what to check yourself after changing the code, since there’s no CI to catch it for you.

Terminal window
python manage.py test

Run this from a Method 4 local dev environment, since it needs the same DJANGO_SETTINGS_MODULE=config.settings.dev, database connection, and root key as runserver does. The suite is DB-backed. Django creates and tears down a throwaway test database automatically around the run, so it never touches your real development data.

Django’s test runner accepts a dotted path, so you can scope a run to exactly what you’re working on instead of the full suite:

Terminal window
python manage.py test apps.findings # one app
python manage.py test apps.findings.tests.FindingViewTests # one test class
python manage.py test apps.findings.tests.FindingViewTests.test_non_member_cannot_create_finding # one test method

This is worth using while iterating. The full suite takes real time to run against a real Postgres instance, and there’s no need to pay that cost on every save while you’re working inside a single app.

Every app has exactly one tests.py at apps/<app>/tests.py, following Django’s default test discovery convention rather than a separate tests/ package per app. There’s no pytest, no factory_boy, and no fixture files.

Every test is a plain django.test.TestCase subclass grouped by the feature it covers. For example, apps/findings/tests.py has FindingViewTests, CommentThreadTests, CatalogueTests, and CatalogueVersioningTests, among others, each with its own setUp() building whatever engagement, finding, or user state that group of tests needs from scratch. Most tests exercise real views through Django’s test Client rather than calling internal functions directly, so they cover permission checks, redirects, and template rendering the same way an actual request would.

As of this writing the suite has 900+ individual test methods across all apps. Run grep -rc "def test_" apps/*/tests.py from the repo root for an exact current count. Treat any number given here as a lower bound, since the suite only grows over time.

There is currently no CI. A .github/workflows/test.yml workflow ran these checks automatically for a while, but was removed, since a broken workflow with no CI runner to diagnose it against was only producing failure-notification emails with no way to see why. The suite runs identically well locally. Run both of these after any code change:

Terminal window
python manage.py makemigrations --check --dry-run
python manage.py test

The migration check matters on its own. It catches a model change with no migration file committed immediately, rather than as a confusing downstream test error partway through the suite.

Test against a real postgres:17-alpine database, not SQLite. RedScribe’s suite leans on real Postgres row-locking behavior in several places (encryption, the audit hash chain, the report job slot limiter) that a lighter-weight substitute wouldn’t exercise faithfully. Local development already sets this up for you.

A change that alters behavior should come with tests covering it, written in the existing style of the relevant app’s tests.py rather than introducing a new testing pattern for just that one change. Run the suite locally before committing. There’s no CI running it again, so this is the only check a change gets.

RedScribe doesn’t accept pull requests; every code change is made by the maintainers. To suggest a fix or a change, open an issue as described in Contributing.