"""HTTP-route tests for the finance-flux report import.
The unit tests in ``test_report_import.py`` exercise ``parse_report`` /
``import_report`` directly. These cover the *route* the browser actually hits:
``multipart/form-data`` upload → RBAC gate → audit → commit. A regression guard
after a user-reported "import failed" where everything below the HTTP layer
worked in isolation.
"""
from __future__ import annotations
import os
from pathlib import Path
import pytest
from httpx import AsyncClient
from sqlalchemy import select
from app.domains.finance_flux import constants as c
from app.domains.finance_flux.models import FinanceFluxPeriod
pytestmark = pytest.mark.asyncio
_XLS_CT = "application/vnd.ms-excel"
def _ss(rows: list[list[str]]) -> bytes:
"""Minimal SpreadsheetML workbook mirroring a NetSuite export."""
def cells(r: list[str]) -> str:
return "".join(
f'| {v} | ' for v in r)
body = "".join(f"{cells(r)}
" for r in rows)
return (
''
''
f''
).encode()
BS_FIXTURE = _ss([
["PingCAP"],
["虚拟母公司 : PingCap Ltd. (已合并)"],
["Consolidated Balance Sheet_no comparatives"],
["May,2026 末"],
[],
["Finance:line", " "],
["ASSETS", ""],
["Current assets", ""],
["Cash and cash equivalents", ""],
["1002 - 银行存款", ""],
["总计 - 1002 - 银行存款", "24353092.92"],
["Cash and cash equivalents", "32228212.53"],
["Financial assets at fair value", "138066640.78"],
["Elim-5001 - Elim-生产成本", "0.00"],
["Current assets", "185841193.91"],
["Non-current assets", ""],
["Right-of-use assets", "264219.92"],
["Non-current assets", "1513555.35"],
["Total assets", "187354749.25"],
])
def _headers(finance_user) -> dict[str, str]:
_, token = finance_user
return {"Authorization": f"Bearer {token}"}
async def test_import_endpoint_multipart_ok(
client: AsyncClient, finance_user, db_session
):
"""A real multipart upload through the route creates the period."""
resp = await client.post(
"/api/v1/finance-flux/import",
files={"file": ("ConsolidatedBalanceSheet.xls", BS_FIXTURE, _XLS_CT)},
headers=_headers(finance_user),
)
assert resp.status_code == 200, resp.text
body = resp.json()
assert body["success"] is True
assert body["data"]["period_key"] == "2026-05"
assert body["data"]["statement"] == c.STATEMENT_BS
assert body["data"]["line_count"] == 6
period = (await db_session.execute(
select(FinanceFluxPeriod).where(FinanceFluxPeriod.period_key == "2026-05")
)).scalar_one()
assert period.sync_status == c.SYNC_SYNCED
async def test_import_endpoint_requires_finance(
client: AsyncClient, employee_user
):
"""A non-finance/non-admin caller is rejected (403), not 500."""
_, token = employee_user
resp = await client.post(
"/api/v1/finance-flux/import",
files={"file": ("x.xls", BS_FIXTURE, _XLS_CT)},
headers={"Authorization": f"Bearer {token}"},
)
assert resp.status_code == 403, resp.text
async def test_import_endpoint_rejects_empty(client: AsyncClient, finance_user):
resp = await client.post(
"/api/v1/finance-flux/import",
files={"file": ("empty.xls", b"", _XLS_CT)},
headers=_headers(finance_user),
)
assert resp.status_code == 422, resp.text
# --- Local diagnostic: the user's real export. Auto-skips in CI / when the
# file isn't present so no real financial data is ever committed or required. ---
_REAL = Path(os.path.expanduser(
"~/Downloads/ConsolidatedBalanceSheet_nocomparatives-227.xls"))
@pytest.mark.skipif(not _REAL.exists(), reason="real NetSuite export not present")
async def test_import_endpoint_real_export(
client: AsyncClient, finance_user, db_session
):
resp = await client.post(
"/api/v1/finance-flux/import",
files={"file": (_REAL.name, _REAL.read_bytes(), _XLS_CT)},
headers=_headers(finance_user),
)
assert resp.status_code == 200, resp.text
assert resp.json()["data"]["statement"] == c.STATEMENT_BS