Files
agentic-mobile-control/tests/test_semantic_enricher.py
T

142 lines
3.6 KiB
Python

from __future__ import annotations
from typing import Any
from core.models import Bounds, Scene, SceneElement
from semantic.config import SemanticConfig, load_config
from semantic.enricher import enrich_scene
from semantic.llm_client import EnrichmentUnavailable
class FakeClient:
def __init__(
self,
*,
response: dict[str, Any] | None = None,
error: Exception | None = None,
) -> None:
self.response = response
self.error = error
self.calls: list[tuple[dict[str, Any], float]] = []
def enrich(self, scene_json: dict[str, Any], *, timeout: float) -> dict[str, Any]:
self.calls.append((scene_json, timeout))
if self.error:
raise self.error
assert self.response is not None
return self.response
def _scene() -> Scene:
return Scene(
width=10,
height=20,
elements=[
SceneElement(
id="input",
type="input",
bounds=Bounds(1, 2, 3, 4),
text="Message",
),
SceneElement(
id="send",
type="button",
bounds=Bounds(5, 2, 3, 4),
text="Send",
),
],
)
def _semantic_payload() -> dict[str, Any]:
return {
"page": "Chat",
"intents": ["send a message"],
"widgets": [
{"element_id": "input", "purpose": "message text input"},
{"element_id": "send", "purpose": "send message"},
],
}
def test_enrichment_config_is_disabled_by_default() -> None:
assert load_config({}).enabled is False
def test_enrich_scene_returns_semantic_scene_when_enabled() -> None:
client = FakeClient(response=_semantic_payload())
result = enrich_scene(
_scene(),
client=client,
config=SemanticConfig(enabled=True, timeout=2.5),
)
assert result is not None
assert result.to_dict() == _semantic_payload()
assert len(client.calls) == 1
assert client.calls[0][1] == 2.5
def test_enrich_scene_disabled_short_circuits_without_client_call() -> None:
client = FakeClient(response=_semantic_payload())
result = enrich_scene(
_scene(),
client=client,
config=SemanticConfig(enabled=False),
)
assert result is None
assert client.calls == []
def test_enrich_scene_degrades_to_none_on_client_failure() -> None:
client = FakeClient(error=EnrichmentUnavailable("rate limited"))
result = enrich_scene(
_scene(),
client=client,
config=SemanticConfig(enabled=True),
)
assert result is None
def test_enrich_scene_filters_dangling_element_ids() -> None:
payload = _semantic_payload()
payload["widgets"] = [
{"element_id": "send", "purpose": "send message"},
{"element_id": "missing", "purpose": "unknown control"},
]
client = FakeClient(response=payload)
result = enrich_scene(
_scene(),
client=client,
config=SemanticConfig(enabled=True),
)
assert result is not None
assert result.to_dict()["widgets"] == [
{"element_id": "send", "purpose": "send message"}
]
def test_enrich_scene_returns_none_when_all_widgets_are_dangling() -> None:
client = FakeClient(
response={
"page": "Chat",
"intents": ["send a message"],
"widgets": [{"element_id": "missing", "purpose": "unknown control"}],
}
)
result = enrich_scene(
_scene(),
client=client,
config=SemanticConfig(enabled=True),
)
assert result is None