Files
worldquant-alpha-system/backend/tests/test_pnl_backfill.py
T
2026-09-09 20:10:20 +08:00

104 lines
4.1 KiB
Python

"""Submitted PnL backfill covers the local library and preserves existing caches."""
import pytest
from sqlalchemy import func, select
from app.alphas import upsert_alpha
from app.models import Pnl
from app.worldquant import WqError
from tests.conftest import alpha
from tests.test_jobs import ready_runner, result
URL = "/api/v1/sync-jobs"
async def start(client):
response = await client.post(URL, json={"kind": "pnl_backfill"})
assert response.status_code == 202
return response.json()
async def test_backfill_all_submitted_beyond_page_limit_and_deduplicates(app, logged_in):
runner = await ready_runner(app)
async with runner.sessions() as db:
for i in range(105):
await upsert_alpha(db, alpha(
f"ref{i:03}", status="DECOMMISSIONED" if i % 2 else "ACTIVE",
hidden=bool(i % 2), settings={"region": "EUR" if i % 2 else "USA"},
))
for raw in [alpha("pending"), alpha("unknown", status=None), alpha("cached", status="ACTIVE")]:
await upsert_alpha(db, raw)
db.add(Pnl(alpha_id="cached", raw={"original": True}, points=[]))
await db.commit()
first = await start(logged_in)
assert first["total"] == 105
assert first["payload"]["alpha_ids"] == [f"ref{i:03}" for i in range(105)]
# Another task can populate an item after this snapshot was fixed.
async with runner.sessions() as db:
db.add(Pnl(alpha_id="ref000", raw={"original": True}, points=[]))
await db.commit()
assert (await start(logged_in))["id"] == first["id"]
calls = []
original_pnl = runner.client.pnl
async def pnl(alpha_id):
calls.append(alpha_id)
return await original_pnl(alpha_id)
runner.client.pnl = pnl
await runner.execute(first["id"])
finished = await result(runner, first["id"])
assert (finished.status, finished.processed, finished.failed) == ("completed", 105, 0)
assert calls == [f"ref{i:03}" for i in range(1, 105)]
async with runner.sessions() as db:
assert await db.scalar(select(func.count()).select_from(Pnl)) == 106
assert (await db.get(Pnl, "cached")).raw == {"original": True}
assert (await db.get(Pnl, "ref000")).raw == {"original": True}
empty = await start(logged_in)
assert empty["status"] == "completed" and empty["total"] == 0
assert empty["payload"]["alpha_ids"] == []
async def test_backfill_keeps_progress_and_retries_only_unfinished_items(app, logged_in):
runner = await ready_runner(app)
async with runner.sessions() as db:
for name in ("a", "b", "c"):
await upsert_alpha(db, alpha(name, status="ACTIVE"))
await db.commit()
calls = []
blocked = True
original_pnl = runner.client.pnl
async def pnl(alpha_id):
calls.append(alpha_id)
if blocked and alpha_id == "b":
raise WqError("无权访问", "access_denied")
if blocked and alpha_id == "c":
raise WqError("平台数据仍在准备,请稍后重试", "pending")
return await original_pnl(alpha_id)
runner.client.pnl = pnl
job = await start(logged_in)
await runner.execute(job["id"])
failed = await result(runner, job["id"])
assert (failed.status, failed.processed, failed.failed) == ("failed", 1, 1)
assert failed.checkpoint["alpha_id"] == "c"
async with runner.sessions() as db:
assert await db.get(Pnl, "a") is not None
assert await db.get(Pnl, "b") is None
assert await db.get(Pnl, "c") is None
blocked = False
assert (await logged_in.post(f"{URL}/{job['id']}/retry")).status_code == 200
await runner.execute(job["id"])
finished = await result(runner, job["id"])
assert (finished.status, finished.processed, finished.failed) == ("completed", 3, 0)
assert calls == ["a", "b", "c", "b", "c"]
@pytest.mark.parametrize("extra", [
{"alpha_ids": ["a"]}, {"submission": "UNSUBMITTED"}, {"date_from": "2025-01-01"},
])
async def test_backfill_rejects_client_scope(logged_in, extra):
response = await logged_in.post(URL, json={"kind": "pnl_backfill", **extra})
assert response.status_code == 422