Files
X-Financial/server/tests/test_backend_pagination.py
caoxiaozhu aa965da69d feat(server): 报销单输出工号/邮箱并扩展申请人邮箱前缀匹配
- ExpenseClaimRead 新增 employee_no/employee_email 字段,ExpenseClaim 模型补对应只读属性
- expense_claim_access_policy 在姓名匹配未果时,按 candidate@% 邮箱前缀匹配 Employee.email,命中唯一记录即返回
- test_backend_pagination/test_expense_claim_service 补充工号/邮箱字段断言与邮箱前缀匹配用例
- 更新公司通信费报销规则表
2026-06-22 15:55:48 +08:00

139 lines
4.3 KiB
Python

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
assert payload["items"][0]["employee_no"] == "E-PAGE"
assert payload["items"][0]["employee_email"] == "page-user@example.com"
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