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.
Running the suite
Section titled “Running the suite”python manage.py testRun 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.
Running a subset
Section titled “Running a subset”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:
python manage.py test apps.findings # one apppython manage.py test apps.findings.tests.FindingViewTests # one test classpython manage.py test apps.findings.tests.FindingViewTests.test_non_member_cannot_create_finding # one test methodThis 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.
Testing style
Section titled “Testing style”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.
No CI, run these checks yourself
Section titled “No CI, run these checks yourself”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:
python manage.py makemigrations --check --dry-runpython manage.py testThe 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.
Tests for a change
Section titled “Tests for a change”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.