"""Channel adapter concurrency tests. Covers per-channel in-flight cap, cap-formula scaling with ``max_concurrency``, done-callback exception surfacing, channel-stop cancellation, relay-close in finally, typing-keepalive lifecycle, and deadlock-free stress under 10K requests / 100 sessions. """ from __future__ import annotations import asyncio from typing import Any from unittest.mock import AsyncMock, MagicMock, patch import pytest from opensquilla.gateway.channel_dispatch import ( _ChannelInFlightSet, _compute_channel_cap, _deliver_runtime_channel_reply, _resolve_channel_overflow_policy, ) # ── Helpers ────────────────────────────────────────────────────────────────── def _make_config(*, channel_inflight_cap: int = 8, max_concurrency: int = 4) -> Any: cfg = MagicMock() cfg.task_runtime.channel_inflight_cap = channel_inflight_cap cfg.task_runtime.max_concurrency = max_concurrency return cfg def _make_task_runtime(*, delay: float = 0.0, succeed: bool = True) -> Any: rt = MagicMock() async def _wait(task_id: str) -> Any: if delay > 0: await asyncio.sleep(delay) record = MagicMock() record.status.value = "succeeded" if succeed else "failed" record.error_message = None record.terminal_reason = None return record rt.wait = _wait return rt def _make_channel() -> Any: ch = MagicMock() ch.send = AsyncMock() ch.build_reply_message = None ch.streaming_reply_kwargs = None return ch def _make_inbound() -> Any: msg = MagicMock() msg.content = "hello" return msg def _make_route_envelope() -> Any: env = MagicMock() env.reply_target = None env.thread_id = None env.channel_id = "ch-1" return env def _make_session_manager() -> Any: sm = MagicMock() async def _read_transcript(sk: str) -> list: record = {"role": "assistant", "content": "reply text"} return [record] sm.read_transcript = _read_transcript return sm # ── _ChannelInFlightSet cap and formula ────────────────────────────────────── def test_inflight_set_cap_enforced() -> None: """In-flight set tracks tasks and reports full() at cap.""" ifs = _ChannelInFlightSet(cap=3) assert not ifs.full() tasks = [] for _ in range(3): t = MagicMock(spec=asyncio.Task) ifs.add(t) tasks.append(t) assert ifs.full() ifs.discard(tasks[0]) assert not ifs.full() @pytest.mark.parametrize( "max_concurrency,channel_inflight_cap,expected_cap", [ (1, 8, 2), # min(8, max(2×1,1)) = min(8,2) = 2 (4, 8, 8), # min(8, max(2×4,1)) = min(8,8) = 8 (16, 8, 8), # min(8, max(2×16,1)) = min(8,32) = 8 ], ) def test_inflight_cap_scales_with_concurrency( max_concurrency: int, channel_inflight_cap: int, expected_cap: int, ) -> None: """Cap formula min(channel_inflight_cap, max(2 × max_concurrency, 1)).""" cfg = _make_config( channel_inflight_cap=channel_inflight_cap, max_concurrency=max_concurrency, ) assert _compute_channel_cap(cfg) == expected_cap def test_compute_channel_cap_env_override(monkeypatch: pytest.MonkeyPatch) -> None: """Env path: OPENSQUILLA_CHANNEL_INFLIGHT_CAP=1 gives cap=min(1, 2×4)=1.""" monkeypatch.setenv("OPENSQUILLA_CHANNEL_INFLIGHT_CAP", "1") # _compute_channel_cap reads from config object, not env directly — # env is applied by GatewayConfig. Test via config mock. cfg = _make_config(channel_inflight_cap=1, max_concurrency=4) assert _compute_channel_cap(cfg) == 1 # ── _ChannelInFlightSet docstring must mention SEPARATE second-layer semaphore def test_ac2_2_docstring_mentions_separate_semaphore() -> None: """_ChannelInFlightSet docstring must reference global_sem separation.""" doc = _ChannelInFlightSet.__doc__ or "" assert "global_sem" in doc or "second-layer" in doc, ( "_ChannelInFlightSet docstring must mention 'second-layer' or 'global_sem'" ) # ── relay close in finally even when reply raises ─────────────────────────── @pytest.mark.asyncio async def test_relay_close_on_exception() -> None: """stream_relay.close() is called even when wait() raises.""" channel = _make_channel() session_manager = _make_session_manager() route_envelope = _make_route_envelope() inbound = _make_inbound() relay = MagicMock() relay.close = AsyncMock() relay.text_emitted = False relay.stream_error = None rt = MagicMock() rt.wait = AsyncMock(side_effect=RuntimeError("boom")) await _deliver_runtime_channel_reply( channel=channel, task_runtime=rt, session_manager=session_manager, session_key="s:test", task_id="task-1", route_envelope=route_envelope, inbound=inbound, transcript_watermark=0, stream_relay=relay, ) relay.close.assert_awaited_once() # A user-facing terminal message should be sent; raw exception detail stays in logs. channel.send.assert_awaited_once() sent_content = channel.send.call_args[0][0].content assert sent_content == "The task failed before it could finish." assert "boom" not in sent_content @pytest.mark.asyncio async def test_relay_close_on_success() -> None: """stream_relay.close() is also called on success path.""" channel = _make_channel() session_manager = _make_session_manager() route_envelope = _make_route_envelope() inbound = _make_inbound() relay = MagicMock() relay.close = AsyncMock() relay.text_emitted = False relay.stream_error = None rt = _make_task_runtime(succeed=True) await _deliver_runtime_channel_reply( channel=channel, task_runtime=rt, session_manager=session_manager, session_key="s:test", task_id="task-1", route_envelope=route_envelope, inbound=inbound, transcript_watermark=0, stream_relay=relay, ) relay.close.assert_awaited_once() # ── done_callback surfaces exceptions ──────────────────────────────────────── @pytest.mark.asyncio async def test_ac2_3_done_callback_logs_error_and_counter() -> None: """done_callback on a failing reply task logs error + turn_cancellations_total.""" metric_calls: list[str] = [] def _fake_emit(name: str, value: int = 1, **labels: Any) -> None: metric_calls.append(name) # Build a reply task that raises async def _failing_reply() -> None: raise ValueError("reply boom") ifs = _ChannelInFlightSet(cap=8) session_key = "s:cb-test" with patch("opensquilla.gateway.channel_dispatch._emit_metric", side_effect=_fake_emit): with patch("opensquilla.gateway.channel_dispatch.log") as mock_log: task = asyncio.create_task(_failing_reply()) ifs.add(task) def _reply_done(t: asyncio.Task[Any], _sk: str = session_key) -> None: ifs.discard(t) exc = t.exception() if not t.cancelled() else None if exc is not None: mock_log.error( "channel_dispatch.reply_task_error", session_key=_sk, error_type=type(exc).__name__, error=str(exc), exc_info=exc, ) _fake_emit( "turn_cancellations_total", value=1, reason="reply_task_error", session_key=_sk, ) task.add_done_callback(_reply_done) # Wait for task to complete await asyncio.gather(task, return_exceptions=True) # Give callbacks a chance to fire await asyncio.sleep(0) assert mock_log.error.called, "log.error must be called on reply task exception" assert "turn_cancellations_total" in metric_calls, ( "turn_cancellations_total counter must be incremented" ) # ── stop_channel cancels all in-flight tasks ──────────────────────────────── @pytest.mark.asyncio async def test_close_cancels_inflight() -> None: """_ChannelInFlightSet.cancel_all() cancels and awaits all tasks.""" ifs = _ChannelInFlightSet(cap=8) results: list[str] = [] async def _long_running(label: str) -> None: try: await asyncio.sleep(60) except asyncio.CancelledError: results.append(f"cancelled:{label}") raise tasks = [] for i in range(3): t = asyncio.create_task(_long_running(str(i))) ifs.add(t) tasks.append(t) # Yield to let tasks start (reach the sleep await point). await asyncio.sleep(0) await ifs.cancel_all() assert len(results) == 3, f"Expected 3 cancelled tasks, got {results}" assert all(r.startswith("cancelled:") for r in results) assert all(t.done() for t in tasks), "all tasks must be done after cancel_all" # ── typing_keepalive lifecycle bound to reply task ────────────────────────── @pytest.mark.asyncio async def test_keepalive_lifecycle() -> None: """When reply task is cancelled, keepalive task finishes within 1 s.""" keepalive_done = asyncio.Event() async def _fake_keepalive() -> None: try: await asyncio.sleep(60) except asyncio.CancelledError: keepalive_done.set() raise keepalive_task = asyncio.create_task(_fake_keepalive()) async def _reply_body() -> None: try: await asyncio.sleep(60) finally: keepalive_task.cancel() reply_task = asyncio.create_task(_reply_body()) # Cancel the reply task (simulating shutdown or cap-drop) await asyncio.sleep(0) # let tasks start reply_task.cancel() with pytest.raises(asyncio.CancelledError): await reply_task # keepalive should finish quickly after reply is cancelled done = await asyncio.wait_for(keepalive_done.wait(), timeout=1.0) assert done or keepalive_done.is_set(), ( "keepalive must be done within 1 s of reply cancel" ) # cleanup with pytest.raises(asyncio.CancelledError): await keepalive_task # ── deadlock stress test ──────────────────────────────────────────────────── @pytest.mark.asyncio async def test_no_deadlock_stress() -> None: """10K requests across 100 sessions — no timeout, no starvation. Uses mock task_runtime that returns immediately to avoid real LLM calls. """ from opensquilla.gateway.channel_dispatch import _ChannelInFlightSet total_requests = 10_000 cap = 8 ifs = _ChannelInFlightSet(cap=cap) completed: list[int] = [] dropped: list[int] = [] async def _mock_reply(req_id: int) -> None: await asyncio.sleep(0) # yield to event loop completed.append(req_id) async def _run_all() -> None: for req_id in range(total_requests): if ifs.full(): dropped.append(req_id) continue task = asyncio.create_task(_mock_reply(req_id)) ifs.add(task) def _done(t: asyncio.Task[Any], _id: int = req_id) -> None: ifs.discard(t) task.add_done_callback(_done) # Occasionally yield to let tasks complete if req_id % cap == 0: await asyncio.sleep(0) # Drain remaining tasks await asyncio.gather(*list(ifs._tasks), return_exceptions=True) await asyncio.wait_for(_run_all(), timeout=60.0) total_processed = len(completed) + len(dropped) assert total_processed == total_requests, ( f"processed {total_processed} != {total_requests}" ) # All non-dropped requests must have completed assert len(completed) > 0, "at least some requests must complete" # ── Integration: inflight cap causes busy-reply ─────────────────────────────── @pytest.mark.asyncio async def test_inflight_cap_triggers_busy_reply() -> None: """When cap is reached, channel gets 'Server busy' reply.""" from opensquilla.gateway.channel_dispatch import _ChannelInFlightSet ifs = _ChannelInFlightSet(cap=2) channel = _make_channel() # Fill the in-flight set with dummy tasks async def _noop() -> None: await asyncio.sleep(60) tasks = [asyncio.create_task(_noop()) for _ in range(2)] for t in tasks: ifs.add(t) assert ifs.full() # Simulate the busy-reply path from run_channel_dispatch from opensquilla.channels.types import OutgoingMessage if ifs.full(): await channel.send( OutgoingMessage( content="Server busy, please retry", reply_to="ch-1", ) ) channel.send.assert_awaited_once() sent = channel.send.call_args[0][0] assert "busy" in sent.content.lower() or "retry" in sent.content.lower() # cleanup for t in tasks: t.cancel() await asyncio.gather(*tasks, return_exceptions=True) # ── cap full → no transcript pollution, no enqueue ────────────────────────── @pytest.mark.asyncio async def test_cap_full_no_transcript_pollution() -> None: """When _in_flight is full, enqueue is not called and transcript is not written; user receives 'Server busy' reply. """ from opensquilla.channels.types import IncomingMessage from opensquilla.gateway.channel_dispatch import _ChannelInFlightSet, run_channel_dispatch # Build a full in-flight set (cap=1, one dummy task) ifs = _ChannelInFlightSet(cap=1) async def _noop() -> None: await asyncio.sleep(60) dummy = asyncio.create_task(_noop()) ifs.add(dummy) assert ifs.full() # Channel yields one message then raises CancelledError to end the loop. # Use plain MagicMock (no spec) so attribute access works in routing helpers. msg = MagicMock() msg.content = "hello" msg.metadata = {} msg.sender_id = "u-1" msg.channel_id = "ch-test" msg.thread_id = None msg.id = "msg-1" channel = MagicMock() channel.send = AsyncMock() channel.build_reply_message = None channel.streaming_reply_kwargs = None call_count = 0 async def _receive() -> IncomingMessage: nonlocal call_count call_count += 1 if call_count == 1: return msg raise asyncio.CancelledError channel.receive = _receive # task_runtime mock — enqueue must NOT be called task_runtime = MagicMock() task_runtime.enqueue = AsyncMock() # session_manager mock — append_message must NOT be called session_manager = MagicMock() session_manager.get_or_create = AsyncMock(return_value=(MagicMock(), False)) session_manager.update = AsyncMock() session_manager.read_transcript = AsyncMock(return_value=[]) config = _make_config(channel_inflight_cap=1, max_concurrency=1) fake_envelope = MagicMock() fake_envelope.thread_id = None fake_envelope.channel_id = "ch-test" with ( patch( "opensquilla.gateway.routing.build_channel_route_envelope", return_value=fake_envelope, ), patch("opensquilla.gateway.channel_dispatch._append_channel_user_message") as mock_append, patch("opensquilla.gateway.channel_dispatch.start_turn_via_runtime") as mock_enqueue, patch( "opensquilla.gateway.channel_dispatch._record_delivery_context", new=AsyncMock(return_value=(MagicMock(), False)), ), patch("opensquilla.gateway.channel_dispatch._should_skip_unmentioned", return_value=False), patch( "opensquilla.gateway.channel_dispatch._ingest_channel_message_attachments", new=AsyncMock(return_value=MagicMock(text="hello", attachments=[])), ), patch("opensquilla.gateway.channel_dispatch._status_reactor") as mock_reactor_factory, ): mock_append.return_value = (MagicMock(), "hello") mock_enqueue.return_value = MagicMock(task_id="t-1") mock_reactor = MagicMock() mock_reactor.received = AsyncMock() mock_reactor.completed = AsyncMock() mock_reactor.running = AsyncMock() mock_reactor.failed = AsyncMock() mock_reactor_factory.return_value = mock_reactor turn_runner = MagicMock(spec=[]) # no _get_session_lock → session_lock=None try: await run_channel_dispatch( channel=channel, turn_runner=turn_runner, session_manager=session_manager, session_key_builder=lambda _msg: "s:test-cap", session_prefix="test", task_runtime=task_runtime, config=config, _in_flight=ifs, ) except asyncio.CancelledError: pass # (a) transcript was NOT written mock_append.assert_not_called() # (b) enqueue was NOT called mock_enqueue.assert_not_called() # (c) user received "Server busy" channel.send.assert_awaited_once() sent = channel.send.call_args[0][0] assert "busy" in sent.content.lower() or "retry" in sent.content.lower() # cleanup dummy.cancel() await asyncio.gather(dummy, return_exceptions=True) # ── debounce path reservation enforced ────────────────────────────────────── @pytest.mark.asyncio async def test_debounce_reservation_enforced() -> None: """Two concurrent _dispatch_combined_message_after_debounce calls with cap=1 — second is rejected with busy reply; enqueue called exactly once. """ from unittest.mock import AsyncMock, MagicMock, patch from opensquilla.gateway.channel_dispatch import ( _ChannelInFlightSet, _dispatch_combined_message_after_debounce, ) ifs = _ChannelInFlightSet(cap=1) channel = MagicMock() channel.send = AsyncMock() channel.build_reply_message = None channel.streaming_reply_kwargs = None def _make_combined() -> Any: combined = MagicMock() combined.message = MagicMock() combined.message.content = "hello" combined.message.metadata = {} combined.message.sender_id = "u-1" combined.message.channel_id = "ch-test" combined.message.thread_id = None combined.message.id = "msg-1" combined.raw_content = "hello" combined.coalesced_count = 1 return combined fake_envelope = MagicMock() fake_envelope.thread_id = None fake_envelope.channel_id = "ch-test" enqueue_call_count = 0 async def _slow_enqueue(*args: Any, **kwargs: Any) -> Any: nonlocal enqueue_call_count enqueue_call_count += 1 # Yield so the second coroutine gets to run its cap check concurrently. await asyncio.sleep(0) result = MagicMock() result.task_id = "t-1" return result task_runtime = MagicMock() mock_reactor = MagicMock() mock_reactor.received = AsyncMock() mock_reactor.completed = AsyncMock() mock_reactor.running = AsyncMock() mock_reactor.failed = AsyncMock() with ( patch( "opensquilla.gateway.routing.build_channel_route_envelope", return_value=fake_envelope, ), patch( "opensquilla.gateway.channel_dispatch.start_turn_via_runtime", side_effect=_slow_enqueue, ), patch( "opensquilla.gateway.channel_dispatch._append_channel_user_message", new=AsyncMock(return_value=(MagicMock(), "hello")), ), patch( "opensquilla.gateway.channel_dispatch._record_delivery_context", new=AsyncMock(return_value=(MagicMock(), False)), ), patch( "opensquilla.gateway.channel_dispatch._ingest_channel_message_attachments", new=AsyncMock(return_value=MagicMock(text="hello", attachments=[])), ), patch( "opensquilla.gateway.channel_dispatch._transcript_watermark", new=AsyncMock(return_value=0), ), patch("opensquilla.gateway.channel_dispatch._RuntimeChannelStreamRelay") as mock_relay_cls, patch("opensquilla.gateway.channel_dispatch._status_reactor", return_value=mock_reactor), patch( "opensquilla.gateway.channel_dispatch._deliver_runtime_channel_reply", new=AsyncMock(), ), patch("opensquilla.gateway.channel_dispatch._emit_events", new=AsyncMock()), ): mock_relay_cls.maybe_start.return_value = None turn_runner = MagicMock(spec=[]) await asyncio.gather( _dispatch_combined_message_after_debounce( channel, _make_combined(), turn_runner, MagicMock(), "s:test", "test", task_runtime, None, None, ifs, ), _dispatch_combined_message_after_debounce( channel, _make_combined(), turn_runner, MagicMock(), "s:test", "test", task_runtime, None, None, ifs, ), ) # Exactly one enqueue — the second dispatch was rejected by the cap. assert enqueue_call_count == 1, ( f"enqueue must be called exactly once, got {enqueue_call_count}" ) # At least one 'Server busy' reply sent to channel. busy_replies = [ call for call in channel.send.call_args_list if "busy" in call[0][0].content.lower() or "retry" in call[0][0].content.lower() ] assert len(busy_replies) == 1, ( f"expected 1 busy reply, got {len(busy_replies)}" ) # ── Per-channel overflow policy resolution ────────────────────────────────── def _make_channel_with_id(channel_id: str) -> Any: ch = MagicMock() ch.channel_id = channel_id return ch def test_resolve_channel_overflow_policy_returns_none_without_config() -> None: assert _resolve_channel_overflow_policy(_make_channel_with_id("feishu"), None) is None def test_resolve_channel_overflow_policy_returns_none_when_map_empty() -> None: cfg = MagicMock() cfg.task_runtime.pending_overflow_policy_per_channel = {} assert _resolve_channel_overflow_policy(_make_channel_with_id("feishu"), cfg) is None def test_resolve_channel_overflow_policy_returns_none_when_channel_unmapped() -> None: cfg = MagicMock() cfg.task_runtime.pending_overflow_policy_per_channel = {"slack": "drop_oldest"} assert _resolve_channel_overflow_policy(_make_channel_with_id("feishu"), cfg) is None def test_resolve_channel_overflow_policy_returns_mapped_value() -> None: cfg = MagicMock() cfg.task_runtime.pending_overflow_policy_per_channel = {"feishu": "drop_oldest"} assert ( _resolve_channel_overflow_policy(_make_channel_with_id("feishu"), cfg) == "drop_oldest" ) def test_resolve_channel_overflow_policy_handles_missing_channel_id() -> None: cfg = MagicMock() cfg.task_runtime.pending_overflow_policy_per_channel = {"feishu": "drop_oldest"} channel_no_id = MagicMock(spec=[]) assert _resolve_channel_overflow_policy(channel_no_id, cfg) is None @pytest.mark.asyncio async def test_apply_overflow_policy_invoked_when_channel_override_present() -> None: """When a channel has an override configured, channel_dispatch invokes runtime.apply_overflow_policy(session_key, policy=) before start_turn_via_runtime so the override takes effect for that turn. """ from opensquilla.channels.types import IncomingMessage from opensquilla.gateway.channel_dispatch import _ChannelInFlightSet, run_channel_dispatch msg = MagicMock() msg.content = "hello" msg.metadata = {} msg.sender_id = "u-1" msg.channel_id = "ch-test" msg.thread_id = None msg.id = "msg-1" channel = MagicMock() channel.channel_id = "feishu" channel.send = AsyncMock() channel.build_reply_message = None channel.streaming_reply_kwargs = None call_count = 0 async def _receive() -> IncomingMessage: nonlocal call_count call_count += 1 if call_count == 1: return msg raise asyncio.CancelledError channel.receive = _receive task_runtime = MagicMock() task_runtime.enqueue = AsyncMock() task_runtime.apply_overflow_policy = AsyncMock() session_manager = MagicMock() session_manager.get_or_create = AsyncMock(return_value=(MagicMock(), False)) session_manager.update = AsyncMock() session_manager.read_transcript = AsyncMock(return_value=[]) cfg = _make_config(channel_inflight_cap=8, max_concurrency=4) cfg.task_runtime.pending_overflow_policy_per_channel = {"feishu": "drop_oldest"} fake_envelope = MagicMock() fake_envelope.thread_id = None fake_envelope.channel_id = "ch-test" ifs = _ChannelInFlightSet(cap=8) with ( patch( "opensquilla.gateway.routing.build_channel_route_envelope", return_value=fake_envelope, ), patch( "opensquilla.gateway.channel_dispatch._append_channel_user_message", new=AsyncMock(return_value=(MagicMock(), "hello")), ), patch( "opensquilla.gateway.channel_dispatch.start_turn_via_runtime", new=AsyncMock(return_value=MagicMock(task_id="t-1")), ), patch( "opensquilla.gateway.channel_dispatch._record_delivery_context", new=AsyncMock(return_value=(MagicMock(), False)), ), patch("opensquilla.gateway.channel_dispatch._should_skip_unmentioned", return_value=False), patch( "opensquilla.gateway.channel_dispatch._ingest_channel_message_attachments", new=AsyncMock(return_value=MagicMock(text="hello", attachments=[])), ), patch("opensquilla.gateway.channel_dispatch._status_reactor") as mock_reactor_factory, patch( "opensquilla.gateway.channel_dispatch._RuntimeChannelStreamRelay", ) as mock_relay_cls, patch( "opensquilla.gateway.channel_dispatch._deliver_runtime_channel_reply", new=AsyncMock(), ), patch("opensquilla.gateway.channel_dispatch._emit_events", new=AsyncMock()), ): mock_relay_cls.maybe_start.return_value = None mock_reactor = MagicMock() mock_reactor.received = AsyncMock() mock_reactor.completed = AsyncMock() mock_reactor.running = AsyncMock() mock_reactor.failed = AsyncMock() mock_reactor_factory.return_value = mock_reactor turn_runner = MagicMock(spec=[]) try: await run_channel_dispatch( channel=channel, turn_runner=turn_runner, session_manager=session_manager, session_key_builder=lambda _msg: "s:override", session_prefix="feishu", task_runtime=task_runtime, config=cfg, _in_flight=ifs, ) except asyncio.CancelledError: pass # Override hook fired with the per-channel policy. task_runtime.apply_overflow_policy.assert_awaited_once() args, kwargs = task_runtime.apply_overflow_policy.call_args assert kwargs.get("policy") == "drop_oldest" @pytest.mark.asyncio async def test_apply_overflow_policy_not_invoked_without_channel_override() -> None: """No override hook fires when the channel has no per-channel mapping.""" from opensquilla.channels.types import IncomingMessage from opensquilla.gateway.channel_dispatch import _ChannelInFlightSet, run_channel_dispatch msg = MagicMock() msg.content = "hello" msg.metadata = {} msg.sender_id = "u-1" msg.channel_id = "ch-test" msg.thread_id = None msg.id = "msg-1" channel = MagicMock() channel.channel_id = "discord" # not in override map channel.send = AsyncMock() channel.build_reply_message = None channel.streaming_reply_kwargs = None call_count = 0 async def _receive() -> IncomingMessage: nonlocal call_count call_count += 1 if call_count == 1: return msg raise asyncio.CancelledError channel.receive = _receive task_runtime = MagicMock() task_runtime.enqueue = AsyncMock() task_runtime.apply_overflow_policy = AsyncMock() session_manager = MagicMock() session_manager.get_or_create = AsyncMock(return_value=(MagicMock(), False)) session_manager.update = AsyncMock() session_manager.read_transcript = AsyncMock(return_value=[]) cfg = _make_config(channel_inflight_cap=8, max_concurrency=4) cfg.task_runtime.pending_overflow_policy_per_channel = {"feishu": "drop_oldest"} fake_envelope = MagicMock() fake_envelope.thread_id = None fake_envelope.channel_id = "ch-test" ifs = _ChannelInFlightSet(cap=8) with ( patch( "opensquilla.gateway.routing.build_channel_route_envelope", return_value=fake_envelope, ), patch( "opensquilla.gateway.channel_dispatch._append_channel_user_message", new=AsyncMock(return_value=(MagicMock(), "hello")), ), patch( "opensquilla.gateway.channel_dispatch.start_turn_via_runtime", new=AsyncMock(return_value=MagicMock(task_id="t-1")), ), patch( "opensquilla.gateway.channel_dispatch._record_delivery_context", new=AsyncMock(return_value=(MagicMock(), False)), ), patch("opensquilla.gateway.channel_dispatch._should_skip_unmentioned", return_value=False), patch( "opensquilla.gateway.channel_dispatch._ingest_channel_message_attachments", new=AsyncMock(return_value=MagicMock(text="hello", attachments=[])), ), patch("opensquilla.gateway.channel_dispatch._status_reactor") as mock_reactor_factory, patch( "opensquilla.gateway.channel_dispatch._RuntimeChannelStreamRelay", ) as mock_relay_cls, patch( "opensquilla.gateway.channel_dispatch._deliver_runtime_channel_reply", new=AsyncMock(), ), patch("opensquilla.gateway.channel_dispatch._emit_events", new=AsyncMock()), ): mock_relay_cls.maybe_start.return_value = None mock_reactor = MagicMock() mock_reactor.received = AsyncMock() mock_reactor.completed = AsyncMock() mock_reactor.running = AsyncMock() mock_reactor.failed = AsyncMock() mock_reactor_factory.return_value = mock_reactor turn_runner = MagicMock(spec=[]) try: await run_channel_dispatch( channel=channel, turn_runner=turn_runner, session_manager=session_manager, session_key_builder=lambda _msg: "s:no-override", session_prefix="discord", task_runtime=task_runtime, config=cfg, _in_flight=ifs, ) except asyncio.CancelledError: pass task_runtime.apply_overflow_policy.assert_not_called()