مخزن اختصاصی برنامهٔ وب؛ محتوای پوشهٔ web در ریشهٔ مخزن قرار گرفت. تاریخچهٔ پروژهٔ ربات (agy-bot) در این مخزن وجود ندارد و هیچ وابستگی runtime به آن نیست.
78 lines
2.5 KiB
Python
78 lines
2.5 KiB
Python
"""Configuration safety guards.
|
|
|
|
A production API booted with a placeholder ``JWT_SECRET`` lets anyone forge a
|
|
token for any tenant. These tests pin the fail-closed behaviour.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import pytest
|
|
from pydantic import ValidationError
|
|
|
|
from app.core.config import DEVELOPMENT_CORS_ORIGINS, MIN_SECRET_LENGTH, Settings
|
|
|
|
_SAFE = {
|
|
"app_env": "production",
|
|
"jwt_secret": "s" * MIN_SECRET_LENGTH,
|
|
"encryption_key": "k" * 44,
|
|
"cors_origins": "https://app.example.com",
|
|
}
|
|
|
|
|
|
def _build(**overrides) -> Settings:
|
|
return Settings(**{**_SAFE, **overrides})
|
|
|
|
|
|
def test_development_tolerates_placeholder_secrets():
|
|
"""A fresh clone must run with zero configuration."""
|
|
settings = Settings(
|
|
app_env="development", jwt_secret="change-me", encryption_key=""
|
|
)
|
|
assert settings.is_production is False
|
|
|
|
|
|
def test_development_cors_allows_both_loopback_spellings():
|
|
"""``localhost`` and ``127.0.0.1`` are different browser origins.
|
|
|
|
Listing only one makes the dev UI fail with an opaque "Disallowed CORS
|
|
origin" — which is exactly what happened when the frontend was started with
|
|
``-H 127.0.0.1`` while the API allowed only ``localhost``.
|
|
"""
|
|
origins = Settings(app_env="development").cors_origin_list
|
|
assert "http://localhost:3000" in origins
|
|
assert "http://127.0.0.1:3000" in origins
|
|
|
|
|
|
def test_production_rejects_the_placeholder_secret():
|
|
with pytest.raises(ValidationError, match="placeholder"):
|
|
_build(jwt_secret="change-me")
|
|
|
|
|
|
@pytest.mark.parametrize("secret", ["", "short", "x" * (MIN_SECRET_LENGTH - 1)])
|
|
def test_production_rejects_a_short_secret(secret: str):
|
|
with pytest.raises(ValidationError, match="JWT_SECRET"):
|
|
_build(jwt_secret=secret)
|
|
|
|
|
|
def test_production_requires_an_encryption_key():
|
|
"""Without it, stored provider credentials cannot be encrypted at rest."""
|
|
with pytest.raises(ValidationError, match="ENCRYPTION_KEY"):
|
|
_build(encryption_key="")
|
|
|
|
|
|
def test_production_rejects_wildcard_cors():
|
|
with pytest.raises(ValidationError, match="CORS_ORIGINS"):
|
|
_build(cors_origins="*")
|
|
|
|
|
|
def test_production_rejects_the_development_cors_default():
|
|
"""A production deploy left on the dev allowlist has not been configured."""
|
|
with pytest.raises(ValidationError, match="development default"):
|
|
_build(cors_origins=DEVELOPMENT_CORS_ORIGINS)
|
|
|
|
|
|
def test_production_accepts_a_fully_configured_environment():
|
|
settings = _build()
|
|
assert settings.is_production is True
|
|
assert settings.cors_origin_list == ["https://app.example.com"]
|