This should fix that. test_settings.py written by qwen3.8 Assisted-by: local-qwen3.8 <localllm@ahtlon.de>
94 lines
3.3 KiB
Python
94 lines
3.3 KiB
Python
"""Checks that the suite never resolves settings from .env or the ambient OS env."""
|
|
|
|
import json
|
|
import os
|
|
|
|
from app.services.settings import Settings, settings
|
|
from test.conftest import TEST_ENV
|
|
|
|
# Values a developer's .env / shell would plausibly contribute.
|
|
HOSTILE_ENV = {
|
|
"MOCK": "False",
|
|
"DISABLE_CALDAV": "False",
|
|
"DISABLE_CARDS": "False",
|
|
"CALDAV_URL": "https://caldav.example.com/dav.php",
|
|
"CALDAV_USER": "real-user",
|
|
"CALDAV_PASSWORD": "real-password",
|
|
"CALDAV_CALENDARS": '["Family"]',
|
|
"CALDAV_REFRESH_INTERVAL": "1",
|
|
"MIFARE_APP_MASTER_KEY": "a" * 16,
|
|
"MIFARE_ACL_READ_BASE_KEY": "b" * 16,
|
|
"MIFARE_ACL_WRITE_BASE_KEY": "c" * 16,
|
|
"SQLALCHEMY_DATABASE_URL": "postgresql://db.example.com:5432/gatekeeper",
|
|
"MQTT_HOST": "mqtt.example.com",
|
|
"SECRET_KEY": "leaked-secret",
|
|
}
|
|
|
|
|
|
def test_every_settings_field_is_pinned_by_the_test_env():
|
|
ambient = {name.upper() for name in Settings.model_fields}
|
|
|
|
missing = sorted(ambient - set(TEST_ENV))
|
|
assert not missing, (
|
|
f"Settings fields not pinned by conftest.TEST_ENV: {missing} — their "
|
|
"value could come from .env or the OS environment"
|
|
)
|
|
|
|
stale = [name for name in TEST_ENV if name not in ambient]
|
|
assert not stale, f"TEST_ENV names that are no longer Settings fields: {stale}"
|
|
|
|
|
|
def test_ambient_env_and_dotenv_could_not_supply_any_value():
|
|
"""No field is left to .env: each one has a pinned value in os.environ."""
|
|
ambient = {name.upper() for name in Settings.model_fields}
|
|
unpinned = sorted(ambient - set(TEST_ENV))
|
|
assert not unpinned, f"unpinned Settings fields: {unpinned}"
|
|
|
|
for name, expected in TEST_ENV.items():
|
|
assert os.environ.get(name) == expected, (
|
|
f"conftest did not pin {name} (os.environ={os.environ.get(name)!r})"
|
|
)
|
|
|
|
for name, hostile in HOSTILE_ENV.items():
|
|
leaked = os.environ.get(name)
|
|
assert leaked != hostile, f"{name} leaked from the ambient env ({leaked!r})"
|
|
|
|
|
|
def test_shared_settings_come_from_the_test_env():
|
|
for name, expected in TEST_ENV.items():
|
|
actual = getattr(settings, name.lower())
|
|
assert _matches(actual, expected), (
|
|
f"{name}: settings={actual!r} != test value {expected!r}"
|
|
)
|
|
|
|
|
|
def _matches(actual, expected: str) -> bool:
|
|
"""Compare a pydantic-parsed value against the raw string pinned in TEST_ENV."""
|
|
if str(actual) == expected:
|
|
return True
|
|
try:
|
|
return actual == json.loads(expected)
|
|
except json.JSONDecodeError:
|
|
return False
|
|
|
|
|
|
def test_env_vars_outrank_env_file(monkeypatch):
|
|
"""Why the scrub matters: an injected env value wins over TEST_ENV/.env.
|
|
|
|
Scoped to this test (monkeypatch restores os.environ on teardown), which is how
|
|
a test that needs other settings must do it.
|
|
"""
|
|
for name, value in HOSTILE_ENV.items():
|
|
monkeypatch.setenv(name, value)
|
|
|
|
leaked = Settings()
|
|
assert leaked.mock is False
|
|
assert leaked.disable_caldav is False
|
|
assert leaked.sqlalchemy_database_url.startswith("postgresql://")
|
|
assert leaked.mqtt_host == "mqtt.example.com"
|
|
|
|
# The shared settings object the rest of the suite uses is untouched.
|
|
assert settings.mock is True
|
|
assert settings.disable_caldav is True
|
|
assert settings.disable_cards is True
|