Skip to content

Commit 0a3f40f

Browse files
committed
chore: add tests covering session dependency usage
1 parent c0903c4 commit 0a3f40f

File tree

5 files changed

+97
-19
lines changed

5 files changed

+97
-19
lines changed

pyproject.toml

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,12 @@ version = "0.2.0"
44
description = "SQLAlchemy extension for FastAPI with support for asynchronous SQLAlchemy sessions and pagination."
55
readme = "README.md"
66
requires-python = ">=3.12"
7-
authors = [{ name = "Hadrien David", email = "[email protected]" }]
8-
dependencies = ["sqlalchemy[asyncio]>=2.0.34,<3", "structlog>=24.4.0"]
7+
authors = [{ name = "Hadrien David", email = "[email protected]" }]
8+
dependencies = [
9+
"fastapi>=0.115.6",
10+
"sqlalchemy[asyncio]>=2.0.34,<3",
11+
"structlog>=24.4.0",
12+
]
913
license = { text = "MIT License" }
1014

1115
[tool.uv]
@@ -22,7 +26,6 @@ dev-dependencies = [
2226
"ruff>=0.6.4",
2327
"toml>=0.10.2",
2428
"aiosqlite>=0.20.0",
25-
"fastapi>=0.114.0",
2629
"python-semantic-release>=9.8.8",
2730
"twine>=5.1.1",
2831
]

src/fastapi_async_sqla.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919

2020
__all__ = ["Session", "lifespan", "open_session"]
2121

22-
SessionFactory = async_sessionmaker(class_=AsyncSession, expire_on_commit=False)
22+
SessionFactory = async_sessionmaker(expire_on_commit=False)
2323

2424
logger = get_logger(__name__)
2525

tests/conftest.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from unittest.mock import patch
22

33
from pytest import fixture
4-
from sqlalchemy.ext.asyncio import create_async_engine
4+
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
55

66

77
@fixture
@@ -22,6 +22,12 @@ async def engine(environ):
2222
await engine.dispose()
2323

2424

25+
@fixture
26+
async def session(engine):
27+
async with engine.connect() as conn:
28+
yield AsyncSession(bind=conn)
29+
30+
2531
@fixture(autouse=True)
2632
def tear_down():
2733
from sqlalchemy.orm import clear_mappers
Lines changed: 74 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,89 @@
1+
from http import HTTPStatus
2+
3+
from fastapi import HTTPException
4+
from pydantic import BaseModel, ConfigDict
15
from pytest import fixture
26
from sqlalchemy import select, text
7+
from sqlalchemy.exc import IntegrityError
8+
from sqlalchemy.orm import Mapped, mapped_column
9+
10+
11+
@fixture(autouse=True)
12+
async def setup_tear_down(engine):
13+
async with engine.connect() as conn:
14+
await conn.execute(
15+
text("""
16+
CREATE TABLE user (
17+
id INTEGER PRIMARY KEY AUTOINCREMENT,
18+
email TEXT UNIQUE NOT NULL,
19+
name TEXT NOT NULL
20+
)
21+
""")
22+
)
323

424

525
@fixture
6-
def app(app):
7-
from fastapi_async_sqla import Session
26+
def app(setup_tear_down, app):
27+
from fastapi_async_sqla import Base, Item, Session
28+
29+
class User(Base):
30+
__tablename__ = "user"
31+
id: Mapped[int] = mapped_column(primary_key=True)
32+
email: Mapped[str] = mapped_column(unique=True)
33+
name: Mapped[str]
34+
35+
class UserIn(BaseModel):
36+
email: str
37+
name: str
38+
39+
class UserModel(UserIn):
40+
model_config = ConfigDict(from_attributes=True)
41+
id: int
842

943
@app.get("/session-dependency")
1044
async def get_session(session: Session):
1145
res = await session.execute(select(text("'OK'")))
1246
return {"data": res.scalar()}
1347

48+
@app.post("/users", response_model=Item[UserModel], status_code=HTTPStatus.CREATED)
49+
async def create_user(user_in: UserIn, session: Session):
50+
user = User(**user_in.model_dump())
51+
user_in.model_dump
52+
session.add(user)
53+
try:
54+
await session.flush()
55+
except IntegrityError:
56+
raise HTTPException(status_code=400)
57+
return {"data": user}
58+
1459
return app
1560

1661

1762
async def test_it(client):
18-
response = await client.get("/session-dependency")
19-
assert response.status_code == 200
20-
assert response.json() == {"data": "OK"}
63+
res = await client.get("/session-dependency")
64+
assert res.status_code == HTTPStatus.OK, (res.status_code, res.content)
65+
assert res.json() == {"data": "OK"}
66+
67+
68+
async def test_session_is_commited(client, session):
69+
payload = {"email": "[email protected]", "name": "Bobby"}
70+
res = await client.post("/users", json=payload)
71+
72+
assert res.status_code == HTTPStatus.CREATED, (res.status_code, res.content)
73+
74+
all_users = (await session.execute(text("SELECT * FROM user"))).mappings().all()
75+
assert all_users == [{"id": 1, **payload}]
76+
77+
78+
@fixture
79+
async def bob_exists(session):
80+
await session.execute(
81+
text("INSERT INTO user (email, name) VALUES ('[email protected]', 'Bobby')")
82+
)
83+
await session.commit()
84+
yield
85+
86+
87+
async def test_with_an_integrity_error(client, bob_exists):
88+
res = await client.post("/users", json={"email": "[email protected]", "name": "Bobby"})
89+
assert res.status_code == HTTPStatus.BAD_REQUEST, (res.status_code, res.content)

uv.lock

Lines changed: 9 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)