c1762ff11c
The rename sweep in the base commit missed the sibling-test blast radius (18 red files on CI). Three classes, all fixed: 1. Stale old names in tests (todo/cronjob/process/tour/tip) — updated to todo_list/cronjob_manage/process_manage/gui_tour/show_tip at every registry.get_entry/dispatch/coerce/preview/allowlist call site, plus the coding-brief sentence in agent/coding_context.py now names todo_list (and its gating test). 2. Missed rename in production: AGENT_RUNTIME_POST_HOOK_TOOL_NAMES still held 'tour' — post-hook ownership would have double-emitted for gui_tour via the bridge path. 3. Tests pinning pre-deferral assembly (blank-slate surface, modal sandbox resolution, desktop diet, HUD note) now pin their ACTUAL contract under the legacy defer:[] override, or assert on granted tool names instead of visible schemas. Also fixes a pre-existing ordering flake surfaced by the sweep: test_holds_exactly_the_gui_affordances depended on whether an earlier test had imported apply_layout_tool (registry-registered, not in the static desktop_ui list) — now forces discovery and pins the full set. 649 tests green locally across all touched files, both orderings.
88 lines
2.6 KiB
Python
88 lines
2.6 KiB
Python
"""Tests for the GUI-surface ``tip`` tool."""
|
|
|
|
import json
|
|
|
|
import pytest
|
|
|
|
from tools import tip_tool as tt
|
|
from tools.registry import registry
|
|
|
|
|
|
@pytest.fixture
|
|
def emitted(monkeypatch):
|
|
"""Capture what the desktop bridge is asked to send, and say it landed."""
|
|
sent = []
|
|
|
|
def _emit(event, payload):
|
|
sent.append((event, payload))
|
|
return True
|
|
|
|
monkeypatch.setattr(tt.desktop_ui, "emit", _emit)
|
|
return sent
|
|
|
|
|
|
def test_lives_in_the_gui_surface_toolset(monkeypatch):
|
|
"""Scoped by toolset, not by the backend's env — see AGENTS.md."""
|
|
monkeypatch.delenv("HERMES_DESKTOP", raising=False)
|
|
entry = registry.get_entry("show_tip")
|
|
|
|
assert entry is not None
|
|
assert entry.toolset == "desktop_ui"
|
|
|
|
|
|
def test_is_ungated_like_tour():
|
|
"""The Appearance switch governs the app's idle rotation, not this."""
|
|
entry = registry.get_entry("show_tip")
|
|
|
|
assert entry is not None
|
|
assert entry.check_fn is None
|
|
|
|
|
|
def test_requires_the_desktop_bridge(monkeypatch):
|
|
"""Outside the desktop GUI there is no emitter — a clear error, no crash."""
|
|
monkeypatch.setattr(tt.desktop_ui, "emit", lambda _event, _payload: False)
|
|
|
|
result = json.loads(tt.tip_tool(text="Over here", selector="#composer"))
|
|
|
|
assert "desktop" in result["error"]
|
|
|
|
|
|
def test_needs_both_something_to_say_and_somewhere_to_point(emitted):
|
|
assert "tip needs text" in json.loads(tt.tip_tool(text="", selector="#a"))["error"]
|
|
assert "tip needs a selector" in json.loads(tt.tip_tool(text="Hi", selector=" "))["error"]
|
|
assert not emitted
|
|
|
|
|
|
def test_rejects_an_unknown_side(emitted):
|
|
result = json.loads(tt.tip_tool(text="Hi", selector="#a", side="diagonal"))
|
|
|
|
assert "side must be one of" in result["error"]
|
|
assert not emitted
|
|
|
|
|
|
def test_emits_the_renderer_event_omitting_unset_fields(emitted):
|
|
result = json.loads(tt.tip_tool(text=" The model name is a button ", selector=" #model "))
|
|
|
|
assert result == {"success": True, "selector": "#model"}
|
|
assert emitted == [("tip.show", {"selector": "#model", "text": "The model name is a button"})]
|
|
|
|
|
|
def test_carries_title_and_side_when_given(emitted):
|
|
tt.tip_tool(text="Type here", selector="#composer", title="Composer", side="top")
|
|
|
|
assert emitted[0][1] == {
|
|
"selector": "#composer",
|
|
"text": "Type here",
|
|
"title": "Composer",
|
|
"side": "top",
|
|
}
|
|
|
|
|
|
def test_bridge_failure_is_reported(monkeypatch):
|
|
def _boom(_event, _payload):
|
|
raise RuntimeError("renderer went away")
|
|
|
|
monkeypatch.setattr(tt.desktop_ui, "emit", _boom)
|
|
|
|
assert "renderer went away" in json.loads(tt.tip_tool(text="Hi", selector="#a"))["error"]
|