Test settings: they still read the developer's .env, so the suite differs from one machine to the next #414
Labels
No labels
accessibility
authentication
breaking change
bug
documentation
enhancement
interface
internationalisation
observability
performance
security
tier
1
tier
2
tier
3
tier/4
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
Postulo/postulo#414
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Found on 2026-09-29 in a read-only review of postulo at
95348908d, reading how the test settings are built and running them, and two security tests, with a fewPOSTULO_*variables set.Checked again on 2026-10-01 against
mainat0a5cad587: still present.Related to #76 and #117 (both closed), which fixed particular tests that depended on a developer's
.env. This issue is about the cause they left in place: the test settings themselves are still computed from that file.config/settings/test.pycalls itself "independent of the developer's .env", but it starts withfrom .base import *, andconfig/settings/base.py:23readsREPO_DIR / ".env"into the environment and computes everyPOSTULO_*setting from it at import. The autouse fixture_no_inherited_environmentclears those variables fromos.environbefore each test, which is after the settings exist, so it changes only whatsite.overridden_byreports.test.pypins some settings (secret key, database, mail, media, static) and leaves the rest to whatever the file says: the cache, background work, the log handler,POSTULO_METRICS_ENABLED,POSTULO_ADMIN_URL,POSTULO_TRUSTED_PROXIES,POSTULO_CONNECTIONS_ALLOW_PRIVATE, the rate limits and the email-code limits.config/settings/base.py:22-23(environ.Env.read_env(REPO_DIR / ".env"), unconditional),:171-173(CACHESfromPOSTULO_CACHE_URL),:347(POSTULO_METRICS_ENABLED),:383(POSTULO_ADMIN_URL),:637(POSTULO_BACKGROUND_WORK),:739and:782-806(thefilehandler exists only whenPOSTULO_LOG_DIRis non-empty).config/settings/test.py:1(the docstring),:7(from .base import *) and:39-40(LOGGING["handlers"]["file"]["filename"] = ...).tests/conftest.py:68-82(_no_inherited_environment).tests/security/test_metrics.py:37-40andtests/security/test_admin_exposure.py:68-71, two tests that assert a default..envwould set them (read_envusessetdefault, so a line in the file and a variable in the environment have the same effect), the test settings carry them:POSTULO_METRICS_ENABLED=trueandPOSTULO_ADMIN_URL=back-officemaketest_off_by_default_and_saying_nothingandtest_the_default_mounts_no_admin_at_allfail (assert True is False,assert 'back-office/' == ''). Without the variables both pass.POSTULO_CACHE_URL=redis://localhost:6379/1(the example in.env.example:99) gives the suiteRedisCacheat that address. Tests callcache.clear()(tests/security/test_rate_limits.py:39,tests/security/test_candidate_file.py:56), so a run empties the developer's own cache database, and a run with Redis stopped fails in unrelated places.POSTULO_BACKGROUND_WORK=truemakeserrands.sendenqueue instead of working inline, so every test that expects an errand to be done finds it waiting.POSTULO_LOG_DIR=(empty, to turn the file log off) makes importing the test settings raiseKeyError: 'file'attest.py:40, so the suite cannot start..envsees security tests fail locally that pass in CI. The reverse also holds: a.envcan make a test pass locally that fails in CI, or hide a regression in the default of a security setting. #76 and #117 each fixed a symptom (the subprocess reader intests/security/conftest.py, which stubsread_envfor exactly this reason, and the per-test environment scrub); the next setting read at import will repeat the problem..envfor the test settings. Either move theread_envcall out ofbase.pyintodev.pyandprod.py, or guard it so that it is skipped whenDJANGO_SETTINGS_MODULEis the test module (or whentest.pyhas set a flag before the import). pytest-django then builds the test settings from the process environment alone. Because a variable exported in the shell still reachesbase.py, also pin intest.pywhat decides behaviour:CACHES = {"default": {"BACKEND": "django.core.cache.backends.locmem.LocMemCache"}}(or the database cache on the test database),POSTULO_BACKGROUND_WORK = False, and build thefilehandler unconditionally rather than indexing into one thatbase.pymay have left out. Keep the docstring oftest.pytrue.Done when
tests/test_settings_isolation.py(new) loadspostulo.config.settings.testin a subprocess with a temporary.envsettingPOSTULO_ADMIN_URLandPOSTULO_METRICS_ENABLED, and asserts that both keep their defaults. It fails today.POSTULO_LOG_DIR=,POSTULO_CACHE_URL=redis://localhost:6379/1andPOSTULO_BACKGROUND_WORK=truein the environment, and asserts that the import succeeds, the cache is not Redis andPOSTULO_BACKGROUND_WORKis false. It fails today withKeyError: 'file'.