test(backend): factorise les doubles de session
Chaque test reecrivait sa classe de session et sa fonction d'override, soit trois fois le meme decor pour un seul endpoint. FakeSession et la fixture fake_session portent ce decor, make_settings fabrique une Settings dont les valeurs priment sur l'environnement.
This commit is contained in:
@@ -1,12 +1,9 @@
|
||||
from collections.abc import AsyncIterator
|
||||
from collections.abc import Callable
|
||||
|
||||
import pytest
|
||||
from fastapi import FastAPI
|
||||
from httpx import AsyncClient
|
||||
from sqlalchemy.exc import OperationalError
|
||||
|
||||
from app.db.session import get_session
|
||||
|
||||
|
||||
async def test_liveness_exposes_service_metadata(client: AsyncClient) -> None:
|
||||
response = await client.get("/api/v1/health/live")
|
||||
@@ -20,15 +17,10 @@ async def test_liveness_exposes_service_metadata(client: AsyncClient) -> None:
|
||||
}
|
||||
|
||||
|
||||
async def test_readiness_reports_the_timescaledb_version(app: FastAPI, client: AsyncClient) -> None:
|
||||
class ReadySession:
|
||||
async def scalar(self, *_: object, **__: object) -> str:
|
||||
return "2.22.1"
|
||||
|
||||
async def override() -> AsyncIterator[ReadySession]:
|
||||
yield ReadySession()
|
||||
|
||||
app.dependency_overrides[get_session] = override
|
||||
async def test_readiness_reports_the_timescaledb_version(
|
||||
fake_session: Callable[..., None], client: AsyncClient
|
||||
) -> None:
|
||||
fake_session(result="2.22.1")
|
||||
|
||||
response = await client.get("/api/v1/health/ready")
|
||||
|
||||
@@ -41,16 +33,9 @@ async def test_readiness_reports_the_timescaledb_version(app: FastAPI, client: A
|
||||
|
||||
|
||||
async def test_readiness_returns_503_when_the_extension_is_missing(
|
||||
app: FastAPI, client: AsyncClient
|
||||
fake_session: Callable[..., None], client: AsyncClient
|
||||
) -> None:
|
||||
class SessionWithoutExtension:
|
||||
async def scalar(self, *_: object, **__: object) -> None:
|
||||
return None
|
||||
|
||||
async def override() -> AsyncIterator[SessionWithoutExtension]:
|
||||
yield SessionWithoutExtension()
|
||||
|
||||
app.dependency_overrides[get_session] = override
|
||||
fake_session(result=None)
|
||||
|
||||
response = await client.get("/api/v1/health/ready")
|
||||
|
||||
@@ -67,16 +52,9 @@ async def test_readiness_returns_503_when_the_extension_is_missing(
|
||||
ids=["erreur_sqlalchemy", "erreur_reseau_asyncpg"],
|
||||
)
|
||||
async def test_readiness_returns_503_when_database_is_unreachable(
|
||||
app: FastAPI, client: AsyncClient, failure: Exception
|
||||
fake_session: Callable[..., None], client: AsyncClient, failure: Exception
|
||||
) -> None:
|
||||
class UnreachableSession:
|
||||
async def scalar(self, *_: object, **__: object) -> None:
|
||||
raise failure
|
||||
|
||||
async def override() -> AsyncIterator[UnreachableSession]:
|
||||
yield UnreachableSession()
|
||||
|
||||
app.dependency_overrides[get_session] = override
|
||||
fake_session(failure=failure)
|
||||
|
||||
response = await client.get("/api/v1/health/ready")
|
||||
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import os
|
||||
from collections.abc import AsyncIterator, Iterator
|
||||
from collections.abc import AsyncIterator, Callable, Iterator
|
||||
|
||||
import pytest
|
||||
from fastapi import FastAPI
|
||||
from httpx import ASGITransport, AsyncClient
|
||||
|
||||
from app.core.config import get_settings
|
||||
from app.db.session import get_engine, get_session_factory
|
||||
from app.db.session import get_engine, get_session, get_session_factory
|
||||
from app.main import create_app
|
||||
from tests.factories import FakeSession
|
||||
|
||||
|
||||
# Piege : les variables d'environnement priment sur apps/backend/.env. Celles qu'on ne
|
||||
@@ -52,3 +53,14 @@ async def client(app: FastAPI) -> AsyncIterator[AsyncClient]:
|
||||
transport = ASGITransport(app=app)
|
||||
async with AsyncClient(transport=transport, base_url="http://test") as async_client:
|
||||
yield async_client
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def fake_session(app: FastAPI) -> Callable[..., None]:
|
||||
def install(result: object = None, failure: Exception | None = None) -> None:
|
||||
async def override() -> AsyncIterator[FakeSession]:
|
||||
yield FakeSession(result=result, failure=failure)
|
||||
|
||||
app.dependency_overrides[get_session] = override
|
||||
|
||||
return install
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
from typing import Any
|
||||
|
||||
from app.core.config import Settings
|
||||
|
||||
SETTINGS_DE_TEST: dict[str, Any] = {
|
||||
"env": "local",
|
||||
"debug": False,
|
||||
"log_level": "WARNING",
|
||||
"cors_origins": "",
|
||||
"secret_key": "secret-de-test",
|
||||
"database_url": "postgresql+asyncpg://enervision:change_me@localhost:5433/enervision_test",
|
||||
}
|
||||
|
||||
|
||||
class FakeSession:
|
||||
"""Session factice : renvoie `result`, ou leve `failure` si elle est fournie."""
|
||||
|
||||
def __init__(self, result: object = None, failure: Exception | None = None) -> None:
|
||||
self._result = result
|
||||
self._failure = failure
|
||||
|
||||
async def scalar(self, *_: object, **__: object) -> object:
|
||||
return self._repondre()
|
||||
|
||||
async def execute(self, *_: object, **__: object) -> object:
|
||||
return self._repondre()
|
||||
|
||||
def _repondre(self) -> object:
|
||||
if self._failure is not None:
|
||||
raise self._failure
|
||||
return self._result
|
||||
|
||||
|
||||
# Piege : les arguments nommes priment sur l'environnement et sur .env, contrairement
|
||||
# aux variables posees par la fixture `environment`, qui restent surchargeables.
|
||||
def make_settings(**overrides: Any) -> Settings:
|
||||
return Settings(**{**SETTINGS_DE_TEST, **overrides})
|
||||
Reference in New Issue
Block a user