# [DEF:backend.tests.test_reports_api:Module] # @TIER: CRITICAL # @SEMANTICS: tests, reports, api, contract, pagination, filtering # @PURPOSE: Contract tests for GET /api/reports defaults, pagination, and filtering behavior. # @LAYER: Domain (Tests) # @RELATION: TESTS -> backend.src.api.routes.reports # @INVARIANT: API response contract contains {items,total,page,page_size,has_next,applied_filters}. from datetime import datetime, timedelta from types import SimpleNamespace from fastapi.testclient import TestClient from src.app import app from src.core.task_manager.models import Task, TaskStatus from src.dependencies import get_current_user, get_task_manager class _FakeTaskManager: def __init__(self, tasks): self._tasks = tasks def get_all_tasks(self): return self._tasks def _admin_user(): admin_role = SimpleNamespace(name="Admin", permissions=[]) return SimpleNamespace(username="test-admin", roles=[admin_role]) def _make_task(task_id: str, plugin_id: str, status: TaskStatus, started_at: datetime, finished_at: datetime = None, result=None): return Task( id=task_id, plugin_id=plugin_id, status=status, started_at=started_at, finished_at=finished_at, params={"environment_id": "env-1"}, result=result or {"summary": f"{plugin_id} {status.value.lower()}"}, ) def test_get_reports_default_pagination_contract(): now = datetime.utcnow() tasks = [ _make_task("t-1", "superset-backup", TaskStatus.SUCCESS, now - timedelta(minutes=10), now - timedelta(minutes=9)), _make_task("t-2", "superset-migration", TaskStatus.FAILED, now - timedelta(minutes=8), now - timedelta(minutes=7)), _make_task("t-3", "llm_dashboard_validation", TaskStatus.RUNNING, now - timedelta(minutes=6), None), ] app.dependency_overrides[get_current_user] = lambda: _admin_user() app.dependency_overrides[get_task_manager] = lambda: _FakeTaskManager(tasks) try: client = TestClient(app) response = client.get("/api/reports") assert response.status_code == 200 data = response.json() assert set(["items", "total", "page", "page_size", "has_next", "applied_filters"]).issubset(data.keys()) assert data["page"] == 1 assert data["page_size"] == 20 assert data["total"] == 3 assert isinstance(data["items"], list) assert data["applied_filters"]["sort_by"] == "updated_at" assert data["applied_filters"]["sort_order"] == "desc" finally: app.dependency_overrides.clear() def test_get_reports_filter_and_pagination(): now = datetime.utcnow() tasks = [ _make_task("t-1", "superset-backup", TaskStatus.SUCCESS, now - timedelta(minutes=30), now - timedelta(minutes=29)), _make_task("t-2", "superset-backup", TaskStatus.FAILED, now - timedelta(minutes=20), now - timedelta(minutes=19)), _make_task("t-3", "superset-migration", TaskStatus.FAILED, now - timedelta(minutes=10), now - timedelta(minutes=9)), ] app.dependency_overrides[get_current_user] = lambda: _admin_user() app.dependency_overrides[get_task_manager] = lambda: _FakeTaskManager(tasks) try: client = TestClient(app) response = client.get("/api/reports?task_types=backup&statuses=failed&page=1&page_size=1") assert response.status_code == 200 data = response.json() assert data["total"] == 1 assert data["page"] == 1 assert data["page_size"] == 1 assert data["has_next"] is False assert len(data["items"]) == 1 assert data["items"][0]["task_type"] == "backup" assert data["items"][0]["status"] == "failed" finally: app.dependency_overrides.clear() def test_get_reports_invalid_filter_returns_400(): now = datetime.utcnow() tasks = [_make_task("t-1", "superset-backup", TaskStatus.SUCCESS, now - timedelta(minutes=5), now - timedelta(minutes=4))] app.dependency_overrides[get_current_user] = lambda: _admin_user() app.dependency_overrides[get_task_manager] = lambda: _FakeTaskManager(tasks) try: client = TestClient(app) response = client.get("/api/reports?task_types=bad_type") assert response.status_code == 400 body = response.json() assert "detail" in body finally: app.dependency_overrides.clear() # [/DEF:backend.tests.test_reports_api:Module]