Files

65 lines
2.6 KiB
Python

"""Hopper failures must not become successful negative lookups."""
import asyncio
from importlib import import_module
from types import SimpleNamespace
from unittest.mock import AsyncMock, Mock
import pytest
from artemis.agents.hopper.hopper import HopperError, HopperOutput
module = import_module("artemis.agents.hopper.hopper")
@pytest.mark.asyncio
@pytest.mark.parametrize("invalid", [None, {}, {"found": True, "output": " ", "reason": "Empty"}])
async def test_invalid_extraction_switches_to_fallback(monkeypatch, invalid):
good = HopperOutput(found=True, output="com.example.app", reason="Matched")
primary = SimpleNamespace(ainvoke=AsyncMock(return_value=invalid))
secondary = SimpleNamespace(ainvoke=AsyncMock(return_value=good))
factory = Mock(
side_effect=[
SimpleNamespace(with_structured_output=lambda _: primary),
SimpleNamespace(with_structured_output=lambda _: secondary),
]
)
monkeypatch.setattr(module, "get_llm", factory)
assert (
await module.hopper(SimpleNamespace(data_engine=None), "Find app", "com.example.app")
== good
)
assert primary.ainvoke.await_count == secondary.ainvoke.await_count == 1
@pytest.mark.asyncio
@pytest.mark.parametrize("use_fallback", [False, True])
async def test_exhausted_extraction_raises_a_distinct_error(monkeypatch, use_fallback):
model = SimpleNamespace(ainvoke=AsyncMock(return_value=None))
monkeypatch.setattr(
module, "get_llm", lambda **_: SimpleNamespace(with_structured_output=lambda _: model)
)
with pytest.raises(HopperError, match="模型未能完成数据提取"):
await module.hopper(
SimpleNamespace(data_engine=None), "Find app", "packages", use_fallback=use_fallback
)
assert model.ainvoke.await_count == (2 if use_fallback else 1)
@pytest.mark.asyncio
async def test_cancelled_extraction_never_uses_fallback(monkeypatch):
model = SimpleNamespace(ainvoke=AsyncMock(side_effect=asyncio.CancelledError))
monkeypatch.setattr(
module, "get_llm", lambda **_: SimpleNamespace(with_structured_output=lambda _: model)
)
with pytest.raises(asyncio.CancelledError):
await module.hopper(SimpleNamespace(data_engine=None), "Find app", "packages")
assert model.ainvoke.await_count == 1
@pytest.mark.asyncio
async def test_model_setup_failure_is_not_reported_as_missing_data(monkeypatch):
monkeypatch.setattr(module, "get_llm", Mock(side_effect=ValueError("invalid model settings")))
with pytest.raises(HopperError, match="模型未能完成数据提取"):
await module.hopper(SimpleNamespace(data_engine=None), "Find app", "packages")