"""Rendering what a tool did. The block is written from four places and read from stored rows written by earlier versions, so it has to render anything shaped roughly like an event -- and everything in it is third-party text. """ from __future__ import annotations from lembas.web.templating import templates def _render(*events, live: bool = False) -> str: return templates.get_template("chat/_tool_activity.html").render( {"tool_events": list(events), "live": live} ) def test_a_search_still_says_it_searched_the_web(): html = _render( { "name": "web_search", "kind": "search", "query": "mallorn", "status": "ok", "results": [ { "title": "Mallorn", "url": "https://a.test/m", "host": "a.test", "snippet": "A tree.", } ], } ) assert "Searched the web for “mallorn”" in html assert '".""" html = _render({"name": "notes_search", "query": "shopping", "status": "ok", "results": []}) assert "Searched the web" not in html assert "notes_search" in html def test_a_custom_tool_is_named_and_its_host_shown(): html = _render( { "name": "weather", "kind": "custom", "label": "Weather", "query": "city='Minas Tirith'", "detail": "GET api.test", "status": "ok", "results": [], "text": "Sunny.", } ) assert "Weather" in html assert "GET api.test" in html assert "Sunny." in html def test_a_tools_own_text_is_escaped_and_never_rendered_as_markdown(): """Hard rule 6. A tool's reply is exactly as untrusted as a search result, and markdown is the one path allowed to emit HTML.""" html = _render( { "name": "weather", "kind": "custom", "label": "Weather", "status": "ok", "results": [], "text": " [click](javascript:alert(1))", } ) assert "Bad' in html def test_a_result_with_no_url_at_all_does_not_explode(): html = _render( { "name": "notes_search", "status": "ok", "results": [{"title": "A note", "id": "abc"}], } ) assert "A note" in html def test_a_failure_shows_its_reason(): html = _render( { "name": "weather", "kind": "custom", "label": "Weather", "status": "error", "error": "HTTP 503", "results": [], } ) assert "tool-activity--error" in html assert "Weather failed" in html assert "HTTP 503" in html