Import AITURK IDE 1.0.0-beta.1 from Hermes 63279301; preserve MIT license
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
"""_summarize_cron_failure_for_delivery must not mislabel the scheduler's own
|
||||
inactivity-timeout abort as a provider/fallback-chain failure, and must not
|
||||
claim a fallback chain was "exhausted" when none is configured.
|
||||
|
||||
Field-reported regression: a stuck `terminal`
|
||||
tool call tripped the 600s cron inactivity watchdog. The TimeoutError raised
|
||||
by the watchdog contains the substring "limit 600s" and its message reads
|
||||
"idle for 1239s (limit 600s)" -- no provider or fallback chain was ever
|
||||
involved -- but the old branch order matched the generic "timed out"/"timeout"
|
||||
substring check before any inactivity-specific check existed, so the operator
|
||||
saw "provider timeout. Fallback chain was exhausted or unavailable." for a
|
||||
failure that had nothing to do with either.
|
||||
|
||||
Second bug bundled into the same fix: even on a *genuine* provider failure,
|
||||
"Fallback chain was exhausted or unavailable." fired unconditionally --
|
||||
regardless of whether fallback_providers was ever configured. Most installs
|
||||
have fallback_providers: [], so the message always implied an
|
||||
attempted-and-failed fallback that never existed. _fallback_chain_phrase() now checks the effective chain
|
||||
via get_fallback_chain() and reports "No fallback chain configured." when
|
||||
it's empty.
|
||||
"""
|
||||
|
||||
import cron.scheduler as scheduler
|
||||
from cron.scheduler import _summarize_cron_failure_for_delivery
|
||||
|
||||
|
||||
def test_inactivity_timeout_is_not_reported_as_provider_timeout():
|
||||
job = {"name": "Daily Repo Sweep", "id": "82d65bdd5ba9"}
|
||||
error = (
|
||||
"TimeoutError: Cron job 'Daily Repo Sweep' idle for 1239s "
|
||||
"(limit 600s) — last activity: terminal command running (30s elapsed)"
|
||||
)
|
||||
msg = _summarize_cron_failure_for_delivery(job, error)
|
||||
assert "provider timeout" not in msg
|
||||
assert "fallback chain" not in msg.lower()
|
||||
assert "stalled" in msg.lower()
|
||||
assert "Daily Repo Sweep" in msg
|
||||
|
||||
|
||||
def test_genuine_provider_timeout_with_no_fallback_configured(monkeypatch):
|
||||
monkeypatch.setattr(scheduler, "load_config", lambda: {"fallback_providers": []})
|
||||
monkeypatch.setattr(scheduler, "get_fallback_chain", lambda cfg: [])
|
||||
job = {"name": "CI Autofix Poller", "id": "f7fe78574bda"}
|
||||
error = "Request timed out."
|
||||
msg = _summarize_cron_failure_for_delivery(job, error)
|
||||
assert "provider timeout" in msg
|
||||
assert "No fallback chain configured" in msg
|
||||
assert "exhausted or unavailable" not in msg
|
||||
|
||||
|
||||
def test_genuine_provider_timeout_with_fallback_configured(monkeypatch):
|
||||
monkeypatch.setattr(scheduler, "load_config", lambda: {
|
||||
"fallback_providers": [{"provider": "openrouter", "model": "anthropic/claude-sonnet-5"}]
|
||||
})
|
||||
monkeypatch.setattr(
|
||||
scheduler,
|
||||
"get_fallback_chain",
|
||||
lambda cfg: [{"provider": "openrouter", "model": "anthropic/claude-sonnet-5"}],
|
||||
)
|
||||
job = {"name": "CI Autofix Poller", "id": "f7fe78574bda"}
|
||||
error = "Request timed out."
|
||||
msg = _summarize_cron_failure_for_delivery(job, error)
|
||||
assert "provider timeout" in msg
|
||||
assert "Fallback chain was exhausted or unavailable." in msg
|
||||
assert "No fallback chain configured" not in msg
|
||||
|
||||
|
||||
def test_fallback_chain_phrase_fails_open_on_config_error(monkeypatch):
|
||||
def _raise():
|
||||
raise RuntimeError("config unreadable")
|
||||
|
||||
monkeypatch.setattr(scheduler, "load_config", _raise)
|
||||
assert scheduler._fallback_chain_phrase() == "Fallback chain was exhausted or unavailable."
|
||||
|
||||
|
||||
def test_readtimeout_error_still_classified_as_provider_timeout(monkeypatch):
|
||||
monkeypatch.setattr(scheduler, "load_config", lambda: {"fallback_providers": []})
|
||||
monkeypatch.setattr(scheduler, "get_fallback_chain", lambda cfg: [])
|
||||
job = {"name": "some-job", "id": "abc123"}
|
||||
error = "httpx.ReadTimeout: The read operation timed out"
|
||||
msg = _summarize_cron_failure_for_delivery(job, error)
|
||||
assert "provider timeout" in msg
|
||||
|
||||
|
||||
def test_rate_limit_classification_still_takes_priority_over_inactivity_text(monkeypatch):
|
||||
# A rate-limit error mentioning "usage limit" must still classify as a
|
||||
# rate limit even though it could theoretically also contain "timeout"-
|
||||
# adjacent wording; rate-limit check runs first and should be unaffected
|
||||
# by the new inactivity branch inserted after it.
|
||||
monkeypatch.setattr(scheduler, "load_config", lambda: {"fallback_providers": []})
|
||||
monkeypatch.setattr(scheduler, "get_fallback_chain", lambda cfg: [])
|
||||
job = {"name": "some-job", "id": "abc123"}
|
||||
error = "HTTP 429: weekly usage limit exceeded"
|
||||
msg = _summarize_cron_failure_for_delivery(job, error)
|
||||
assert "weekly usage limit" in msg
|
||||
assert "No fallback chain configured" in msg
|
||||
Reference in New Issue
Block a user