from __future__ import annotations from collections.abc import Generator from datetime import UTC, datetime from decimal import Decimal from fastapi.testclient import TestClient from sqlalchemy import create_engine from sqlalchemy.orm import Session, sessionmaker from sqlalchemy.pool import StaticPool from app.api.deps import get_db from app.db.base import Base from app.main import create_app from app.models.employee import Employee from app.models.financial_record import ExpenseClaim def build_client() -> tuple[TestClient, sessionmaker[Session]]: engine = create_engine( "sqlite+pysqlite:///:memory:", connect_args={"check_same_thread": False}, poolclass=StaticPool, ) Base.metadata.create_all(bind=engine) session_factory = sessionmaker(bind=engine, autoflush=False, autocommit=False) app = create_app() def override_db() -> Generator[Session, None, None]: db = session_factory() try: yield db finally: db.close() app.dependency_overrides[get_db] = override_db return TestClient(app), session_factory def seed_claims(db: Session) -> None: employee = Employee( id="emp-page", employee_no="E-PAGE", name="Page User", email="page-user@example.com", position="Analyst", grade="P4", ) db.add(employee) for index in range(3): db.add( ExpenseClaim( id=f"claim-page-{index}", claim_no=f"EXP-PAGE-{index}", employee_id=employee.id, employee_name=employee.name, department_id="dept-page", department_name="Market", project_code=None, expense_type="office", reason=f"Office purchase {index}", location="Shanghai", amount=Decimal("100.00"), currency="CNY", invoice_count=0, occurred_at=datetime(2026, 5, 20 + index, tzinfo=UTC), submitted_at=None, status="draft", approval_stage="draft", risk_flags_json=[], created_at=datetime(2026, 5, 20 + index, tzinfo=UTC), updated_at=datetime(2026, 5, 20 + index, tzinfo=UTC), ) ) db.commit() def test_expense_claims_support_page_envelope_and_keep_legacy_list() -> None: client, session_factory = build_client() with session_factory() as db: seed_claims(db) headers = {"x-auth-username": "E-PAGE", "x-auth-name": "Page User"} legacy_response = client.get("/api/v1/reimbursements/claims", headers=headers) assert legacy_response.status_code == 200 assert isinstance(legacy_response.json(), list) page_response = client.get( "/api/v1/reimbursements/claims?page=1&page_size=2", headers=headers, ) assert page_response.status_code == 200 payload = page_response.json() assert [key for key in payload if key in {"items", "total", "page", "page_size"}] == [ "items", "total", "page", "page_size", ] assert len(payload["items"]) == 2 assert payload["total"] == 3 assert payload["page"] == 1 assert payload["page_size"] == 2 assert payload["total_pages"] == 2 assert payload["has_next"] is True assert payload["has_previous"] is False def test_employee_directory_supports_backend_pagination() -> None: client, _ = build_client() response = client.get("/api/v1/employees?page=2&page_size=10") assert response.status_code == 200 payload = response.json() assert len(payload["items"]) == 10 assert payload["total"] >= 30 assert payload["page"] == 2 assert payload["page_size"] == 10 def test_budget_allocations_support_backend_pagination() -> None: client, _ = build_client() response = client.get( "/api/v1/budgets/allocations?page=1&page_size=2", headers={"x-auth-username": "admin", "x-auth-role-codes": "manager"}, ) assert response.status_code == 200 payload = response.json() assert len(payload["items"]) <= 2 assert payload["total"] >= len(payload["items"]) assert payload["page"] == 1 assert payload["page_size"] == 2