52 lines
1.9 KiB
Python
52 lines
1.9 KiB
Python
"""B-15: secrets that would only break at first use must stop the app at startup."""
|
|||
|
|
|
||
|
|
import pytest
|
||
|
|
|
||
|
|
from app.config import MIN_JWT_SECRET_LENGTH, ConfigError, Settings, validate_runtime_secrets
|
||
|
|
|
||
|
|
|
||
|
|
def _settings(**overrides) -> Settings:
|
||
|
|
base = {
|
||
|
|
"jwt_secret": "x" * MIN_JWT_SECRET_LENGTH,
|
||
|
|
"xprv_encryption_key": "a-fernet-key",
|
||
|
|
}
|
||
|
|
base.update(overrides)
|
||
|
|
# _env_file=None so a developer's real .env can't make this test pass or fail.
|
||
|
|
return Settings(_env_file=None, **base)
|
||
|
|
|
||
|
|
|
||
|
|
def test_valid_secrets_pass():
|
||
|
|
validate_runtime_secrets(_settings())
|
||
|
|
|
||
|
|
|
||
|
|
def test_empty_jwt_secret_is_refused():
|
||
|
|
"""An empty JWT_SECRET makes PyJWT raise InvalidKeyError on every single login —
|
||
|
|
a 500 with no hint about the real cause, on a container that started up healthy."""
|
||
|
|
with pytest.raises(ConfigError, match="JWT_SECRET"):
|
||
|
|
validate_runtime_secrets(_settings(jwt_secret=""))
|
||
|
|
|
||
|
|
|
||
|
|
def test_short_jwt_secret_is_refused():
|
||
|
|
with pytest.raises(ConfigError, match="JWT_SECRET"):
|
||
|
|
validate_runtime_secrets(_settings(jwt_secret="x" * (MIN_JWT_SECRET_LENGTH - 1)))
|
||
|
|
|
||
|
|
|
||
|
|
def test_empty_xprv_encryption_key_is_refused():
|
||
|
|
"""Without it Fernet fails on the first key derivation — i.e. the first time
|
||
|
|
anyone registers or a transaction needs signing."""
|
||
|
|
with pytest.raises(ConfigError, match="XPRV_ENCRYPTION_KEY"):
|
||
|
|
validate_runtime_secrets(_settings(xprv_encryption_key=" "))
|
||
|
|
|
||
|
|
|
||
|
|
def test_all_problems_are_reported_at_once():
|
||
|
|
with pytest.raises(ConfigError) as exc_info:
|
||
|
|
validate_runtime_secrets(_settings(jwt_secret="", xprv_encryption_key=""))
|
||
|
|
message = str(exc_info.value)
|
||
|
|
assert "JWT_SECRET" in message and "XPRV_ENCRYPTION_KEY" in message
|
||
|
|
|
||
|
|
|
||
|
|
def test_an_empty_admin_token_is_not_fatal():
|
||
|
|
"""require_admin already denies every request when it's unset, so the effect is a
|
||
|
|
locked admin panel rather than an open one — no reason to refuse to boot."""
|
||
|
|
validate_runtime_secrets(_settings(admin_token=""))
|