115 lines
3.2 KiB
Python
115 lines
3.2 KiB
Python
import asyncio
|
|
from datetime import datetime, timezone
|
|
from typing import Any
|
|
|
|
import pytest
|
|
from fastapi import HTTPException
|
|
from sqlalchemy.dialects import sqlite
|
|
|
|
from api.v1.async_tasks.router import (
|
|
API_V1_ASYNC_TASKS_ROUTER,
|
|
check_async_task_status,
|
|
list_async_tasks,
|
|
)
|
|
from models.sql.async_task import AsyncTaskModel
|
|
|
|
|
|
class _RowsResult:
|
|
def __init__(self, rows: list[Any]):
|
|
self._rows = rows
|
|
|
|
def scalars(self):
|
|
return self
|
|
|
|
def all(self):
|
|
return self._rows
|
|
|
|
|
|
class _FakeAsyncSession:
|
|
def __init__(self, get_results: dict[Any, Any] | None = None):
|
|
self._get_results = get_results or {}
|
|
self.executed_statement = None
|
|
|
|
async def get(self, _model: Any, key: Any):
|
|
return self._get_results.get(key)
|
|
|
|
async def execute(self, statement: Any):
|
|
self.executed_statement = statement
|
|
return _RowsResult(list(self._get_results.values()))
|
|
|
|
|
|
def test_async_tasks_routes_use_hyphenated_endpoint():
|
|
paths = {route.path for route in API_V1_ASYNC_TASKS_ROUTER.routes}
|
|
|
|
assert "/api/v1/async-tasks" in paths
|
|
assert "/api/v1/async-tasks/status/{id}" in paths
|
|
assert "/api/v1/async_tasks" not in paths
|
|
|
|
|
|
def test_check_async_task_status_returns_task():
|
|
task = AsyncTaskModel(
|
|
type="template.create",
|
|
status="pending",
|
|
message="Queued for template creation",
|
|
)
|
|
|
|
response = asyncio.run(
|
|
check_async_task_status(
|
|
id=task.id,
|
|
sql_session=_FakeAsyncSession({task.id: task}),
|
|
)
|
|
)
|
|
|
|
assert response == task
|
|
|
|
|
|
def test_check_async_task_status_returns_404_for_missing_task():
|
|
with pytest.raises(HTTPException) as exc:
|
|
asyncio.run(
|
|
check_async_task_status(
|
|
id="task-missing",
|
|
sql_session=_FakeAsyncSession(),
|
|
)
|
|
)
|
|
|
|
assert exc.value.status_code == 404
|
|
assert exc.value.detail == "No async task found"
|
|
|
|
|
|
def test_list_async_tasks_filters_and_orders_tasks():
|
|
task = AsyncTaskModel(
|
|
type="template.create",
|
|
status="completed",
|
|
message="Template creation completed",
|
|
)
|
|
session = _FakeAsyncSession({task.id: task})
|
|
|
|
response = asyncio.run(
|
|
list_async_tasks(
|
|
task_type="template.create",
|
|
status="completed",
|
|
created_at=datetime(2026, 6, 1, tzinfo=timezone.utc),
|
|
created_at_from=None,
|
|
created_at_to=datetime(2026, 6, 30, tzinfo=timezone.utc),
|
|
order_by="updated_at",
|
|
order="asc",
|
|
limit=25,
|
|
offset=5,
|
|
sql_session=session,
|
|
)
|
|
)
|
|
|
|
assert response == [task]
|
|
compiled = str(
|
|
session.executed_statement.compile(
|
|
dialect=sqlite.dialect(),
|
|
compile_kwargs={"literal_binds": True},
|
|
)
|
|
)
|
|
assert "WHERE async_tasks.type = 'template.create'" in compiled
|
|
assert "async_tasks.status = 'completed'" in compiled
|
|
assert "async_tasks.created_at >= '2026-06-01 00:00:00.000000'" in compiled
|
|
assert "async_tasks.created_at <= '2026-06-30 00:00:00.000000'" in compiled
|
|
assert "ORDER BY async_tasks.updated_at ASC" in compiled
|
|
assert "LIMIT 25 OFFSET 5" in compiled
|