From 4af0b9dbbd99b6982ed364ed2eeef6d28e2df7ba Mon Sep 17 00:00:00 2001 From: Anish Sarkar <104695310+AnishSarkar22@users.noreply.github.com> Date: Thu, 9 Jul 2026 19:09:06 +0530 Subject: [PATCH] test(instagram): add regression test for InstagramAccessBlockedError handling Introduced a new test to ensure that InstagramAccessBlockedError is properly propagated without causing deadlocks in the scraper's fan_out function. This regression test verifies that the error surfaces correctly when a blocked job is encountered, enhancing the resilience of the fetch process. --- .../instagram/test_fetch_resilience.py | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/surfsense_backend/tests/unit/platforms/instagram/test_fetch_resilience.py b/surfsense_backend/tests/unit/platforms/instagram/test_fetch_resilience.py index 037268db2..0ded704f2 100644 --- a/surfsense_backend/tests/unit/platforms/instagram/test_fetch_resilience.py +++ b/surfsense_backend/tests/unit/platforms/instagram/test_fetch_resilience.py @@ -295,6 +295,34 @@ async def test_fan_out_empty_jobs_is_noop(): assert out == [] +async def test_fan_out_propagates_blocked_without_deadlock(monkeypatch): + # Regression: a worker that raises InstagramAccessBlockedError used to strand + # the exception on its task and deadlock the consumer on results.get(). It + # must surface as InstagramAccessBlockedError, not hang. + async def _fake_open(): + return _TrackingHolder() + + @asynccontextmanager + async def _fake_bind(_holder): + yield _holder + + monkeypatch.setattr(scraper, "open_proxy_holder", _fake_open) + monkeypatch.setattr(scraper, "bind_proxy_holder", _fake_bind) + + async def _blocked_job() -> AsyncIterator[dict]: + raise InstagramAccessBlockedError("login wall") + yield {} # unreachable; makes this an async generator + + raised = False + try: + async with asyncio.timeout(5): # fail fast if the deadlock regresses + async for _ in scraper.fan_out([_blocked_job()], concurrency=1): + pass + except InstagramAccessBlockedError: + raised = True + assert raised, "hard block must propagate, not deadlock" + + def _profile_payload(username: str, n: int) -> dict: # IDs namespaced per target so cross-target de-dup doesn't collapse them. return {