"""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