From 540237c99e56d53daf6325ef07cbd9d79d9c9e70 Mon Sep 17 00:00:00 2001 From: Ben Barclay Date: Thu, 20 Aug 2026 20:43:45 +1000 Subject: [PATCH] test(cron): pin the auto-mocked D6 accessor; prove the native scalar fallback with a real adapter shape MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Unspecced MagicMock/AsyncMock adapters fabricate supports_inchannel_continuable_for_platform as a truthy callable, so the scheduler's duck-typed D6 gate silently took the relay accessor branch in every in-channel test — the native scalar fallback the fixtures describe was never exercised, and setting supports_inchannel_continuable=False on a mock could not force thread mode. Pin the accessor to None on both mock fixtures (matching a real native adapter, which never defines the method), and add a fallback-boundary test with a real minimal adapter class: scalar False -> in_channel fails safe to thread, flat seed never fires. --- tests/cron/test_scheduler.py | 42 ++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/tests/cron/test_scheduler.py b/tests/cron/test_scheduler.py index 5c30515e5a..062d741cdb 100644 --- a/tests/cron/test_scheduler.py +++ b/tests/cron/test_scheduler.py @@ -371,6 +371,9 @@ class TestDeliverResultWrapping: relay.send_for_platform = AsyncMock(return_value=MagicMock(success=True)) relay.send_voice = AsyncMock(return_value=MagicMock(success=True)) relay.supports_inchannel_continuable = False + # Not a real RelayAdapter: keep the auto-created accessor from + # shadowing the scalar False (MagicMock fabricates truthy callables). + relay.supports_inchannel_continuable_for_platform = None config = GatewayConfig( platforms={ @@ -2390,6 +2393,13 @@ class TestCronContinuableSurfaceInChannel: ) # Capability flag read via getattr in the scheduler. adapter.supports_inchannel_continuable = supports_inchannel + # Pin the per-platform accessor OFF: an unspecced AsyncMock would + # auto-create it as a truthy callable, silently routing every test + # through the relay accessor branch instead of the native scalar + # fallback these tests describe (and making supports_inchannel=False + # unenforceable). None -> not callable -> scalar path, like a real + # native adapter that never defines the method. + adapter.supports_inchannel_continuable_for_platform = None # A live session store so the in_channel seed can CREATE the flat row # (the real bug: without a create step the mirror no-ops on a missing # session and the brief is lost). Use a plain MagicMock store. @@ -2567,6 +2577,38 @@ class TestCronContinuableSurfaceInChannel: ) assert "scope_id" not in captured["metadata"] + def test_native_adapter_scalar_false_fails_safe_to_thread(self): + """D6 fallback boundary with a REAL (non-mock) adapter shape: a native + adapter defines only the scalar supports_inchannel_continuable and no + per-platform accessor — MagicMock-based fixtures can't prove this + branch because they fabricate a truthy accessor. Scalar False must + fail safe to thread mode: the in_channel seed never fires.""" + + class _NativeShapedAdapter: + supports_inchannel_continuable = False + _session_store = None + + def __init__(self): + self.sent = [] + + async def send(self, chat_id, content, metadata=None, **kwargs): + self.sent.append((chat_id, content)) + return MagicMock(success=True, message_id="msg_1", + raw_response=None) + + adapter = _NativeShapedAdapter() + assert not callable( + getattr(adapter, "supports_inchannel_continuable_for_platform", None) + ) + with patch("cron.scheduler._seed_cron_channel_session") as seed_mock: + self._run_inchannel_delivery( + {"slack": {"cron_continuable_surface": "in_channel"}}, adapter, + attach_to_session=False, + ) + # Capability absent -> surface fails safe to thread -> flat seed + # must NOT run (D6). + seed_mock.assert_not_called() + def test_seed_mirrors_into_exact_created_session_on_populated_chat(self): """REGRESSION (live, Alice 2026-08-19 19:17): 'in_channel seed did NOT land'. The seed created the flat session row, then mirror_to_session