rebuild(stage-15): complete governance and handoff
This commit is contained in:
@@ -1,3 +1,8 @@
|
||||
import asyncio
|
||||
from dataclasses import replace
|
||||
|
||||
import httpx
|
||||
from cryptography.fernet import Fernet
|
||||
from fastapi import Query
|
||||
|
||||
from backend.bootstrap.application import create_application
|
||||
@@ -17,6 +22,8 @@ def test_health_reports_runtime_environment(tmp_path) -> None:
|
||||
"components": {"process": "ok", "database": "ok"},
|
||||
}
|
||||
assert len(response.headers["X-Request-ID"]) == 32
|
||||
assert response.headers["X-Content-Type-Options"] == "nosniff"
|
||||
assert response.headers["X-Frame-Options"] == "DENY"
|
||||
|
||||
|
||||
def test_unknown_failure_uses_safe_error_contract(tmp_path) -> None:
|
||||
@@ -81,13 +88,38 @@ def test_production_frontend_serves_spa_without_capturing_api_404(tmp_path) -> N
|
||||
settings.frontend_dist_directory.joinpath("index.html").write_text(
|
||||
"<main>application</main>", encoding="utf-8"
|
||||
)
|
||||
settings.frontend_dist_directory.joinpath("asset.js").write_text(
|
||||
settings.frontend_dist_directory.joinpath("assets").mkdir()
|
||||
settings.frontend_dist_directory.joinpath("assets", "asset.js").write_text(
|
||||
"window.ready=true", encoding="utf-8"
|
||||
)
|
||||
application = create_application(settings)
|
||||
|
||||
assert request(application, "/review/history").text == "<main>application</main>"
|
||||
assert request(application, "/asset.js").text == "window.ready=true"
|
||||
asset_response = request(application, "/assets/asset.js")
|
||||
assert asset_response.text == "window.ready=true"
|
||||
assert asset_response.headers["Cache-Control"] == "public,max-age=31536000,immutable"
|
||||
api_response = request(application, "/api/unknown")
|
||||
assert api_response.status_code == 404
|
||||
assert api_response.json()["error"]["code"] == "not_found"
|
||||
|
||||
|
||||
def test_production_security_headers_are_strict_on_https(tmp_path) -> None:
|
||||
settings = Settings.for_test(tmp_path)
|
||||
settings = replace(
|
||||
settings,
|
||||
environment="production",
|
||||
encryption_key=Fernet.generate_key().decode("ascii"),
|
||||
)
|
||||
application = create_application(settings)
|
||||
|
||||
async def get():
|
||||
transport = httpx.ASGITransport(app=application)
|
||||
async with application.router.lifespan_context(application):
|
||||
async with httpx.AsyncClient(
|
||||
transport=transport, base_url="https://testserver"
|
||||
) as client:
|
||||
return await client.get("/api/health")
|
||||
|
||||
response = asyncio.run(get())
|
||||
assert "default-src 'self'" in response.headers["Content-Security-Policy"]
|
||||
assert response.headers["Strict-Transport-Security"].startswith("max-age=31536000")
|
||||
|
||||
Reference in New Issue
Block a user