fix(trigger): defer local triggers until session idle

This commit is contained in:
chengyongru
2026-07-02 13:32:46 +08:00
committed by Xubin Ren
parent 09bde468eb
commit f32007c83f
13 changed files with 543 additions and 35 deletions
+92
View File
@@ -730,6 +730,56 @@ async def test_cron_turn_deferred_while_session_active(tmp_path):
assert loop.pending_cron_job_ids_for_session(session_key) == set()
@pytest.mark.asyncio
async def test_local_trigger_turn_deferred_while_session_active(tmp_path):
"""Local trigger turns wait for the active session instead of becoming injections."""
from nanobot.bus.events import InboundMessage
from nanobot.triggers.local_session_turns import LOCAL_TRIGGER_META
loop = _make_loop(tmp_path)
loop._dispatch = AsyncMock() # type: ignore[method-assign]
session_key = "websocket:chat-1"
pending = asyncio.Queue(maxsize=20)
loop._pending_queues[session_key] = pending
run_task = asyncio.create_task(loop.run())
msg = InboundMessage(
channel="websocket",
sender_id="trigger",
chat_id="chat-1",
content="review failed CI",
metadata={
LOCAL_TRIGGER_META: {
"trigger_id": "trg_123",
"trigger_name": "CI review",
"delivery_id": "tdl_123",
},
},
session_key_override=session_key,
)
await loop.bus.publish_inbound(msg)
for _ in range(20):
if loop._local_trigger_turns.deferred_queues.get(session_key):
break
await asyncio.sleep(0.05)
loop.stop()
await asyncio.wait_for(run_task, timeout=2)
assert pending.empty()
assert loop._dispatch.await_count == 0
assert loop._local_trigger_turns.deferred_queues[session_key] == [msg]
assert loop.pending_local_trigger_ids_for_session(session_key) == {"trg_123"}
assert await loop._local_trigger_turns.publish_next_deferred(session_key) is True
queued = await asyncio.wait_for(loop.bus.consume_inbound(), timeout=0.5)
assert queued is msg
assert session_key not in loop._local_trigger_turns.deferred_queues
assert loop.pending_local_trigger_ids_for_session(session_key) == set()
@pytest.mark.asyncio
async def test_submitted_cron_turn_reports_pending_until_completed(tmp_path):
"""Bound cron jobs remain marked pending while their session turn is in flight."""
@@ -766,6 +816,48 @@ async def test_submitted_cron_turn_reports_pending_until_completed(tmp_path):
assert loop.pending_cron_job_ids_for_session(session_key) == set()
@pytest.mark.asyncio
async def test_submitted_local_trigger_turn_reports_pending_until_completed(tmp_path):
"""Local triggers remain marked pending while their session turn is in flight."""
from nanobot.bus.events import InboundMessage, OutboundMessage
from nanobot.triggers.local_session_turns import LOCAL_TRIGGER_META
loop = _make_loop(tmp_path)
loop._running = True
session_key = "websocket:chat-1"
msg = InboundMessage(
channel="websocket",
sender_id="trigger",
chat_id="chat-1",
content="review failed CI",
metadata={
LOCAL_TRIGGER_META: {
"trigger_id": "trg_123",
"trigger_name": "CI review",
"delivery_id": "tdl_123",
},
},
session_key_override=session_key,
)
submit_task = asyncio.create_task(loop.submit_local_trigger_turn(msg))
queued = await asyncio.wait_for(loop.bus.consume_inbound(), timeout=0.5)
assert queued is msg
assert loop.pending_local_trigger_ids_for_session(session_key) == {"trg_123"}
response = OutboundMessage(
channel="websocket",
chat_id="chat-1",
content="done",
)
loop._local_trigger_turns.complete(msg, response=response)
assert await asyncio.wait_for(submit_task, timeout=0.5) is response
assert loop.pending_local_trigger_ids_for_session(session_key) == set()
@pytest.mark.asyncio
async def test_pending_queue_preserves_overflow_for_next_injection_cycle(tmp_path):
"""Pending queue should leave overflow messages queued for later drains."""