Files
openswarm/backend/tests/test_router_unavailable_envelope.py

43 lines
2.0 KiB
Python

"""The router-down envelope must NAME the cause.
Found by the forced-failure battery on 2026-08-07: holding port 20128 with a dead socket so 9Router
could not rebind produced a real terminal failure whose envelope read `subkind=unclassified`, with a
breadcrumb trail that simply stopped after the prep phases. The cause was sitting in plain text in
`error_preview` ("9Router is not running; cannot use sonnet-cc") but nothing could be queried on it.
"""
import inspect
from backend.apps.agents.core.error_classify import is_router_unreachable_error
from backend.apps.agents.core.is_router_unavailable_error import is_router_unavailable_error
from backend.apps.agents.manager.run import handle_run_error
def test_the_verbatim_live_refusal_is_classified():
# Exact string raised by configure_provider_env and captured in the battery envelope.
assert is_router_unavailable_error(
"9Router is not running; cannot use sonnet-cc. Install Node.js and restart the app, "
"or switch to a model with a direct API key."
)
def test_the_mid_turn_unreachable_shapes_still_qualify():
for text in ("API Error: Unable to connect. Is the computer able to access the url?",
"fetch failed", "connect ECONNREFUSED 127.0.0.1:20128"):
assert is_router_unavailable_error(text), text
assert is_router_unreachable_error(text), "the narrower resume-path check must keep matching too"
def test_unrelated_failures_are_not_swallowed():
for text in ("", " ", "Prompt is too long", "Invalid API key",
"The router of the story is that nothing broke"):
assert not is_router_unavailable_error(text), text
def test_the_rung_sits_above_unclassified():
src = inspect.getsource(handle_run_error)
assert "is_router_unavailable_error" in src
assert src.index("is_router_unavailable_error") < src.index('p_report_model_error("unclassified"'), \
"a router death must be named before the catch-all claims it"
assert 'p_report_model_error("router_unavailable"' in src