aps-agent/tests/golden/test_async_jobs.py

99 lines
3.1 KiB
Python
Raw Normal View History

# ============================================================
# 方向 F 黄金测试:轻量异步任务队列(矩阵 88 行剩余)
# 覆盖:提交/轮询/完成+结果、失败记录、取消(pending/running)、
# 取消终态返回 False、列表与统计
# ============================================================
from __future__ import annotations
import threading
import time
from server.agent_core.async_jobs import JobCancelled, JobQueue
def test_submit_poll_done_with_result():
"""提交 → 轮询 → done,result/progress/时间戳完整,poll 可重复读。"""
q = JobQueue()
job_id = q.submit(
"sensitivity.recompute",
lambda rec: {"robustness": 0.92},
params={"strategy": "COMPREHENSIVE"},
actor="planner",
)
record = q.wait(job_id, timeout=10)
assert record["status"] == "done"
assert record["result"] == {"robustness": 0.92}
assert record["progress"] == 100
assert record["params"]["strategy"] == "COMPREHENSIVE"
assert record["actor"] == "planner"
assert record["started_at"] and record["finished_at"]
again = q.poll(job_id)
assert again is not None
assert again["status"] == "done"
assert again["result"]["robustness"] == 0.92
def test_failed_job_records_error():
"""fn 抛异常 → failed,error 显式记录,result 为空。"""
q = JobQueue()
def boom(rec):
raise RuntimeError("求解器崩溃")
job_id = q.submit("montecarlo.recompute", boom)
record = q.wait(job_id, timeout=10)
assert record["status"] == "failed"
assert record["error"] == "求解器崩溃"
assert record["result"] is None
def test_cancel_running_job():
"""运行中任务可取消:置标记 → 终态 cancelled,结果丢弃。"""
q = JobQueue()
started = threading.Event()
def long_run(rec):
started.set()
while not rec.cancel_requested:
time.sleep(0.01)
raise JobCancelled()
job_id = q.submit("montecarlo.recompute", long_run)
assert started.wait(timeout=5), "任务未进入运行"
assert q.cancel(job_id) is True
record = q.wait(job_id, timeout=10)
assert record["status"] == "cancelled"
assert record["result"] is None
assert record["cancel_requested"] is True
def test_cancel_unknown_or_terminal_returns_false():
"""不存在或已终态任务取消返回 False。"""
q = JobQueue()
assert q.cancel("job-不存在") is False
job_id = q.submit("t", lambda rec: 1)
q.wait(job_id, timeout=10)
assert q.cancel(job_id) is False
assert q.poll(job_id)["status"] == "done"
def test_list_and_stats():
"""列表可按状态过滤,统计计数与终态一致。"""
q = JobQueue()
def boom(rec):
raise RuntimeError("x")
q.submit("a", lambda rec: 1)
q.submit("b", boom)
q.submit("c", lambda rec: 2)
for item in q.list():
q.wait(item["job_id"], timeout=10)
stats = q.stats()
assert stats["total"] == 3
assert stats["done"] == 2
assert stats["failed"] == 1
assert stats["pending"] == 0
failed = q.list(status="failed")
assert len(failed) == 1 and failed[0]["kind"] == "b"