Introduce explicit ApplicationContext and remove raw request.app.state usage

This commit is contained in:
2026-04-12 19:56:01 +02:00
parent 72488b14b2
commit ee880e6296
3 changed files with 144 additions and 36 deletions

View File

@@ -0,0 +1,78 @@
from __future__ import annotations
from pathlib import Path
from unittest.mock import MagicMock
import aiohttp
import pytest
from fastapi import FastAPI
from starlette.requests import Request
from app.config import Settings
from app.dependencies import (
ApplicationContext,
get_app_context,
get_http_session,
get_scheduler,
get_settings,
get_session_cache,
)
from app.main import create_app
from app.models.server import ServerStatus
def _make_test_request(app: FastAPI) -> Request:
scope = {
"type": "http",
"method": "GET",
"path": "/",
"headers": [],
"query_string": b"",
"client": ("test", 0),
"server": ("test", 0),
"scheme": "http",
"app": app,
}
return Request(scope)
@pytest.mark.asyncio
async def test_app_context_dependency_exposes_shared_resources(test_settings: Settings) -> None:
app = create_app(settings=test_settings)
session = aiohttp.ClientSession()
scheduler = MagicMock()
app.state.http_session = session
app.state.scheduler = scheduler
app.state.server_status = ServerStatus(online=False)
app.state.pending_recovery = None
app.state.last_activation = None
request = _make_test_request(app)
app_context = await get_app_context(request)
assert isinstance(app_context, ApplicationContext)
assert app_context.settings is test_settings
assert app_context.http_session is session
assert app_context.scheduler is scheduler
assert app_context.session_cache is app.state.session_cache
assert app_context.runtime_state is app.state.runtime_state
assert await get_settings(app_context) is test_settings
assert await get_http_session(app_context) is session
assert await get_scheduler(app_context) is scheduler
assert await get_session_cache(app_context) is app.state.session_cache
await session.close()
def test_request_app_state_access_is_only_allowed_in_dependencies() -> None:
app_root = Path(__file__).resolve().parents[1] / "app"
bad_modules: list[str] = []
for path in sorted(app_root.rglob("*.py")):
if path.name == "dependencies.py":
continue
text = path.read_text()
if "request.app.state" in text:
bad_modules.append(str(path))
assert not bad_modules, f"Direct request.app.state access found in: {bad_modules}"