Skip to content

Commit e182b57

Browse files
committed
restructure test fixtures to allow single app
1 parent 058fea0 commit e182b57

4 files changed

Lines changed: 31 additions & 20 deletions

File tree

src/main.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import argparse
22
import asyncio
33
import sys
4-
from collections.abc import AsyncGenerator
4+
from collections.abc import AsyncIterator
55
from contextlib import asynccontextmanager
66
from pathlib import Path
77

@@ -32,7 +32,9 @@
3232

3333

3434
@asynccontextmanager
35-
async def lifespan(app: FastAPI | None) -> AsyncGenerator[None, None]: # noqa: ARG001
35+
async def lifespan(
36+
app: FastAPI | None, # noqa: ARG001 # parameter required by FastAPI/Starlette
37+
) -> AsyncIterator[None]:
3638
"""Manage application lifespan - startup and shutdown events."""
3739
yield
3840
asyncio.gather(

src/routers/dependencies.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from collections.abc import AsyncGenerator
1+
from collections.abc import AsyncIterator
22
from typing import Annotated
33

44
from fastapi import Depends
@@ -11,13 +11,13 @@
1111
from database.users import APIKey, User
1212

1313

14-
async def expdb_connection() -> AsyncGenerator[AsyncConnection, None]:
14+
async def expdb_connection() -> AsyncIterator[AsyncConnection]:
1515
engine = expdb_database()
1616
async with engine.connect() as connection, connection.begin():
1717
yield connection
1818

1919

20-
async def userdb_connection() -> AsyncGenerator[AsyncConnection, None]:
20+
async def userdb_connection() -> AsyncIterator[AsyncConnection]:
2121
engine = user_database()
2222
async with engine.connect() as connection, connection.begin():
2323
yield connection
@@ -26,7 +26,7 @@ async def userdb_connection() -> AsyncGenerator[AsyncConnection, None]:
2626
async def fetch_user(
2727
api_key: APIKey | None = None,
2828
user_data: Annotated[AsyncConnection | None, Depends(userdb_connection)] = None,
29-
) -> AsyncGenerator[User | None, None]:
29+
) -> AsyncIterator[User | None]:
3030
if not (api_key and user_data):
3131
yield None
3232
return

tests/conftest.py

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import contextlib
22
import json
3-
from collections.abc import AsyncGenerator, AsyncIterator, Iterable, Iterator
3+
from collections.abc import AsyncIterator, Iterable, Iterator
44
from pathlib import Path
55
from typing import Any, NamedTuple
66

@@ -9,11 +9,13 @@
99
import pytest
1010
from _pytest.config import Config
1111
from _pytest.nodes import Item
12+
from asgi_lifespan import LifespanManager
13+
from fastapi import FastAPI
1214
from sqlalchemy import text
1315
from sqlalchemy.ext.asyncio import AsyncConnection, AsyncEngine
1416

1517
from database.setup import expdb_database, user_database
16-
from main import create_api, lifespan
18+
from main import create_api
1719
from routers.dependencies import expdb_connection, userdb_connection
1820

1921
PHP_API_URL = "http://php-api:80/api/v1/json"
@@ -51,12 +53,6 @@ async def temporary_records(
5153
await connection.commit()
5254

5355

54-
@pytest.fixture(autouse=True, scope="session")
55-
async def one_lifespan() -> AsyncGenerator[None, None]:
56-
async with lifespan(app=None):
57-
yield
58-
59-
6056
@pytest.fixture
6157
async def expdb_test() -> AsyncIterator[AsyncConnection]:
6258
async with automatic_rollback(expdb_database()) as connection:
@@ -75,14 +71,26 @@ async def php_api() -> AsyncIterator[httpx.AsyncClient]:
7571
yield client
7672

7773

74+
@pytest.fixture(scope="session")
75+
async def app() -> AsyncIterator[FastAPI]:
76+
_app = create_api(Path(__file__).parent / "config.test.toml")
77+
async with LifespanManager(_app):
78+
yield _app
79+
80+
7881
@pytest.fixture
7982
async def py_api(
80-
expdb_test: AsyncConnection, user_test: AsyncConnection
83+
expdb_test: AsyncConnection, user_test: AsyncConnection, app: FastAPI
8184
) -> AsyncIterator[httpx.AsyncClient]:
82-
app = create_api(Path(__file__).parent / "config.test.toml")
85+
"""Create test client which automatically rolls back database updates on teardown."""
86+
# Using the function-scoped database fixtures automatically benefits the
87+
# automatic rollbacks, but also lets a test author write to a database
88+
# transaction that is shared with the app. That is, it enables:
89+
#
90+
# def my_test(expdb_test, py_api):
91+
# expdb_test.execute(...) # write some data # noqa: ERA001
92+
# py_api.get(...) # read that data # noqa: ERA001
8393

84-
# We use async generator functions because fixtures may not be called directly.
85-
# The async generator returns the test connections for FastAPI to handle properly
8694
async def override_expdb() -> AsyncIterator[AsyncConnection]:
8795
yield expdb_test
8896

@@ -91,6 +99,7 @@ async def override_userdb() -> AsyncIterator[AsyncConnection]:
9199

92100
app.dependency_overrides[expdb_connection] = override_expdb
93101
app.dependency_overrides[userdb_connection] = override_userdb
102+
94103
# We do not use the Lifespan manager for now because our auto-use fixture
95104
# `one_lifespan` will do setup and teardown at a session scope level instead.
96105
async with httpx.AsyncClient(

tests/routers/openml/migration/setups_migration_test.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import asyncio
22
import contextlib
33
import re
4-
from collections.abc import AsyncGenerator, Callable, Iterable
4+
from collections.abc import AsyncIterator, Callable, Iterable
55
from contextlib import AbstractAsyncContextManager
66
from http import HTTPStatus
77

@@ -22,7 +22,7 @@ def temporary_tags(
2222
@contextlib.asynccontextmanager
2323
async def _temporary_tags(
2424
tags: Iterable[str], setup_id: int, *, persist: bool = False
25-
) -> AsyncGenerator[None]:
25+
) -> AsyncIterator[None]:
2626
insert_queries = [
2727
(
2828
"INSERT INTO setup_tag(`id`,`tag`,`uploader`) VALUES (:setup_id, :tag, :user_id);",

0 commit comments

Comments
 (0)