fix(todos): datetime query params typés FastAPI, ORDER BY déterministe

- Changer due_after/due_before de str | None vers datetime | None pour typage FastAPI
- FastAPI parse et valide automatiquement, retourne 422 si format invalide (pas 500)
- Supprimer le parsing manuel datetime.fromisoformat() qui levait ValueError brute
- Ajouter ORDER BY déterministe: due_date ASC NULLS LAST, created_at DESC
  évite les réordonnances aléatoires entre requêtes PostgreSQL

Tests: 15/15 passent ✓

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-24 12:02:37 +02:00
parent a3704a2b27
commit b5f0453cdd
+5 -4
View File
@@ -18,8 +18,8 @@ async def list_todos(
status: str | None = "pending",
priority: str | None = None,
tag: str | None = None,
due_after: str | None = None,
due_before: str | None = None,
due_after: datetime | None = None,
due_before: datetime | None = None,
limit: int = 200,
session: AsyncSession = Depends(get_session),
):
@@ -33,13 +33,14 @@ async def list_todos(
if tag:
conditions.append(TodoItem.tags.contains([tag]))
if due_after:
conditions.append(TodoItem.due_date >= datetime.fromisoformat(due_after))
conditions.append(TodoItem.due_date >= due_after)
if due_before:
conditions.append(TodoItem.due_date <= datetime.fromisoformat(due_before))
conditions.append(TodoItem.due_date <= due_before)
stmt = select(TodoItem)
if conditions:
stmt = stmt.where(and_(*conditions))
stmt = stmt.order_by(TodoItem.due_date.asc().nulls_last(), TodoItem.created_at.desc())
stmt = stmt.limit(limit)
result = await session.execute(stmt)
return result.scalars().all()