Files
PlantAI-rag-new/bioflow_service/tests/test_events.py
T

100 lines
3.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""tests/test_events.py —— 事件总线(发布/订阅/持久/快照/回放/seq)。"""
from __future__ import annotations
import asyncio
import json
import pytest
from app.services.bioflow.events import EVENT_TYPES, EventBus
pytestmark = pytest.mark.bioflow_unit
def _run(coro):
return asyncio.new_event_loop().run_until_complete(coro)
def test_publish_replay_persistence(tmp_path):
bus = EventBus(tmp_path / "events")
e1 = bus.publish("job_created", {"job_id": "j1", "title": "t", "stages": []})
e2 = bus.publish("stage_started", {"job_id": "j1", "stage": "identify"})
assert e2["seq"] == e1["seq"] + 1
# 持久文件存在
f = tmp_path / "events" / "j1.jsonl"
assert f.exists()
lines = f.read_text(encoding="utf-8").splitlines()
assert len(lines) == 2
assert json.loads(lines[0])["type"] == "job_created"
# 回放
replay = bus.replay("j1")
assert [e["type"] for e in replay] == ["job_created", "stage_started"]
# after_seq
assert bus.replay("j1", after_seq=e1["seq"])[0]["type"] == "stage_started"
def test_unknown_event_type_rejected(tmp_path):
bus = EventBus(tmp_path / "events")
with pytest.raises(ValueError, match="未知事件类型"):
bus.publish("nope", {"job_id": "j"})
def test_event_types_complete():
"""TC-BIO-SSE-001:12 类事件全集。"""
expected = {
"job_created", "plan_ready", "stage_started", "stage_log", "stage_done",
"stage_failed", "stage_skipped", "stage_await_confirm", "stage_await_files",
"job_done", "job_paused", "job_archived",
}
assert expected <= set(EVENT_TYPES)
def test_subscribe_broadcast(tmp_path):
async def scenario():
bus = EventBus(tmp_path / "events")
sub1 = await bus.subscribe(job_filter="j1")
sub2 = await bus.subscribe(job_filter="j1")
sub3 = await bus.subscribe(job_filter="j2")
bus.publish("stage_started", {"job_id": "j1", "stage": "identify"})
e1 = await asyncio.wait_for(sub1.queue.get(), timeout=2)
e2 = await asyncio.wait_for(sub2.queue.get(), timeout=2)
assert e1["type"] == "stage_started"
assert e2["seq"] == e1["seq"]
assert sub3.queue.empty() # job 过滤
await bus.unsubscribe(sub1)
await bus.unsubscribe(sub2)
await bus.unsubscribe(sub3)
return True
assert _run(scenario())
def test_snapshot(tmp_path):
bus = EventBus(tmp_path / "events")
bus.publish("job_created", {"job_id": "j1", "title": "t", "stages": ["identify", "phylogeny"]})
bus.publish("stage_started", {"job_id": "j1", "stage": "identify"})
bus.publish("stage_done", {"job_id": "j1", "stage": "identify", "exit_code": 0})
snap = bus.snapshot("j1")
assert snap["type"] == "snapshot"
assert snap["job"]["status"] == "created"
stages = {s["stage"]: s["status"] for s in snap["stages"]}
assert stages["identify"] == "done"
assert stages["phylogeny"] == "waiting"
assert snap["last_seq"] == 3
assert bus.snapshot("nope") is None
def test_snapshot_job_done(tmp_path):
bus = EventBus(tmp_path / "events")
bus.publish("job_created", {"job_id": "j1", "stages": []})
bus.publish("job_done", {"job_id": "j1"})
assert bus.snapshot("j1")["job"]["status"] == "done"
def test_memory_replay_after_seq(tmp_path):
bus = EventBus(tmp_path / "events")
e1 = bus.publish("stage_started", {"job_id": "j1", "stage": "a"})
bus.publish("stage_done", {"job_id": "j1", "stage": "a"})
replay = bus.memory_replay(after_seq=e1["seq"])
assert [e["type"] for e in replay] == ["stage_done"]