Files
2026-07-13 13:12:33 +08:00

142 lines
5.3 KiB
Python

"""Unit tests for the code-task provider preflight + mid-run error mapping."""
from opensquilla.contrib.codetask import preflight as preflight_mod
from opensquilla.contrib.codetask.agent_config import build_per_run_agent_config
from opensquilla.contrib.codetask.preflight import provider_block_reason, provider_preflight
from opensquilla.onboarding.probe import ProviderProbeResult
from opensquilla.provider.failures import ProviderFailureKind
_TEMPLATE = {
"workspace_strict": False,
"sandbox": {"sandbox": False},
"tools": {"deny": ["memory*"]},
"meta_skill": {"enabled": False},
}
def _bundle(user):
return build_per_run_agent_config(dict(_TEMPLATE), user)
def _probe(monkeypatch, result=None, *, calls=None):
async def _fake(*, provider_id, model, **kw):
if calls is not None:
calls.append({"provider_id": provider_id, "model": model, **kw})
return result or ProviderProbeResult(ok=True, provider_id=provider_id, model=model)
monkeypatch.setattr(preflight_mod, "probe_llm_provider", _fake)
def test_preflight_passes_on_ok_probe(monkeypatch):
calls = []
_probe(monkeypatch, calls=calls)
bundle = _bundle({"llm": {"provider": "deepseek", "model": "deepseek-chat", "api_key": "sk-x"}})
ok, reason = provider_preflight(bundle)
assert ok is True and reason == ""
# Probes the operator's resolved provider/model with the transported key.
assert calls[0]["provider_id"] == "deepseek"
assert calls[0]["model"] == "deepseek-chat"
assert calls[0]["api_key"] == "sk-x"
def test_preflight_blocks_on_missing_key(monkeypatch):
_probe(
monkeypatch,
ProviderProbeResult(
ok=False,
provider_id="deepseek",
model="deepseek-chat",
failure_kind=ProviderFailureKind.AUTH_INVALID.value,
message="No API key available (checked $DEEPSEEK_API_KEY).",
),
)
bundle = _bundle({"llm": {"provider": "deepseek", "model": "deepseek-chat"}})
ok, reason = provider_preflight(bundle)
assert ok is False
assert "deepseek" in reason and "opensquilla onboard" in reason
def test_preflight_blocks_on_insufficient_credits(monkeypatch):
_probe(
monkeypatch,
ProviderProbeResult(
ok=False,
provider_id="deepseek",
model="deepseek-chat",
failure_kind=ProviderFailureKind.INSUFFICIENT_CREDITS.value,
message="insufficient balance",
),
)
bundle = _bundle({"llm": {"provider": "deepseek", "model": "deepseek-chat", "api_key": "k"}})
ok, _reason = provider_preflight(bundle)
assert ok is False
def test_preflight_fails_open_on_transient(monkeypatch):
# A transport blip must not block a run that might otherwise succeed.
_probe(
monkeypatch,
ProviderProbeResult(
ok=False,
provider_id="deepseek",
model="deepseek-chat",
failure_kind=ProviderFailureKind.TRANSPORT_TRANSIENT.value,
message="timeout",
),
)
bundle = _bundle({"llm": {"provider": "deepseek", "model": "deepseek-chat", "api_key": "k"}})
ok, reason = provider_preflight(bundle)
assert ok is True and reason == ""
def test_preflight_skips_keyless_provider(monkeypatch):
calls = []
_probe(monkeypatch, calls=calls)
bundle = _bundle({"llm": {"provider": "ollama", "model": "llama3", "base_url": "http://x"}})
ok, reason = provider_preflight(bundle)
assert ok is True and reason == ""
assert calls == [] # keyless: no network probe at all
def test_preflight_skips_cross_provider_tier_configs(monkeypatch):
# Cross-provider tiers resolve credentials per tier from profiles/pools the
# primary-provider probe can't see, so the preflight must not false-block
# a keyless primary there — skip without probing.
calls = []
_probe(monkeypatch, calls=calls)
bundle = _bundle(
{
"llm": {"provider": "deepseek", "model": "deepseek-chat"},
"squilla_router": {"enabled": True, "cross_provider_tiers": True},
}
)
ok, reason = provider_preflight(bundle)
assert ok is True and reason == ""
assert calls == []
def test_preflight_fails_open_on_probe_exception(monkeypatch):
async def _boom(**_kw):
raise RuntimeError("probe exploded")
monkeypatch.setattr(preflight_mod, "probe_llm_provider", _boom)
bundle = _bundle({"llm": {"provider": "deepseek", "model": "deepseek-chat", "api_key": "k"}})
ok, _reason = provider_preflight(bundle)
assert ok is True
def test_provider_block_reason_matches_credential_codes():
# The engine emits code="no_provider" and str(status_code) for HTTP errors.
assert provider_block_reason([{"code": "no_provider", "message": "none"}]) is not None
assert provider_block_reason([{"code": "402", "message": "no credits"}]) is not None
assert provider_block_reason([{"code": "401"}]) is not None
assert provider_block_reason([{"code": "403"}]) is not None
def test_provider_block_reason_ignores_non_credential_codes():
assert provider_block_reason([{"code": "429", "message": "rate limited"}]) is None
assert provider_block_reason([{"code": "tool_error"}]) is None
assert provider_block_reason([]) is None
# Non-dict items are tolerated (defensive) rather than crashing.
assert provider_block_reason(["oops", None]) is None