test_banner.py
1 """Tests for banner toolset name normalization and skin color usage.""" 2 3 from unittest.mock import patch 4 5 from rich.console import Console 6 7 import hermes_cli.banner as banner 8 import model_tools 9 import tools.mcp_tool 10 11 12 def test_display_toolset_name_strips_legacy_suffix(): 13 assert banner._display_toolset_name("homeassistant_tools") == "homeassistant" 14 assert banner._display_toolset_name("honcho_tools") == "honcho" 15 assert banner._display_toolset_name("web_tools") == "web" 16 17 18 def test_display_toolset_name_preserves_clean_names(): 19 assert banner._display_toolset_name("browser") == "browser" 20 assert banner._display_toolset_name("file") == "file" 21 assert banner._display_toolset_name("terminal") == "terminal" 22 23 24 def test_display_toolset_name_handles_empty(): 25 assert banner._display_toolset_name("") == "unknown" 26 assert banner._display_toolset_name(None) == "unknown" 27 28 29 def test_build_welcome_banner_uses_normalized_toolset_names(): 30 """Unavailable toolsets should not have '_tools' appended in banner output.""" 31 with ( 32 patch.object( 33 model_tools, 34 "check_tool_availability", 35 return_value=( 36 ["web"], 37 [ 38 {"name": "homeassistant", "tools": ["ha_call_service"]}, 39 {"name": "honcho", "tools": ["honcho_conclude"]}, 40 ], 41 ), 42 ), 43 patch.object(banner, "get_available_skills", return_value={}), 44 patch.object(banner, "get_update_result", return_value=None), 45 patch.object(tools.mcp_tool, "get_mcp_status", return_value=[]), 46 ): 47 console = Console( 48 record=True, force_terminal=False, color_system=None, width=160 49 ) 50 banner.build_welcome_banner( 51 console=console, 52 model="anthropic/test-model", 53 cwd="/tmp/project", 54 tools=[ 55 {"function": {"name": "web_search"}}, 56 {"function": {"name": "read_file"}}, 57 ], 58 get_toolset_for_tool=lambda name: { 59 "web_search": "web_tools", 60 "read_file": "file", 61 }.get(name), 62 ) 63 64 output = console.export_text() 65 assert "homeassistant:" in output 66 assert "honcho:" in output 67 assert "web:" in output 68 assert "homeassistant_tools:" not in output 69 assert "honcho_tools:" not in output 70 assert "web_tools:" not in output 71 72 73 def test_build_welcome_banner_title_is_hyperlinked_to_release(): 74 """Panel title (version label) is wrapped in an OSC-8 hyperlink to the GitHub release.""" 75 import io 76 from unittest.mock import patch as _patch 77 import hermes_cli.banner as _banner 78 import model_tools as _mt 79 import tools.mcp_tool as _mcp 80 81 _banner._latest_release_cache = None 82 tag_url = ("v2026.4.23", "https://github.com/NousResearch/hermes-agent/releases/tag/v2026.4.23") 83 84 buf = io.StringIO() 85 with ( 86 _patch.object(_mt, "check_tool_availability", return_value=(["web"], [])), 87 _patch.object(_banner, "get_available_skills", return_value={}), 88 _patch.object(_banner, "get_update_result", return_value=None), 89 _patch.object(_mcp, "get_mcp_status", return_value=[]), 90 _patch.object(_banner, "get_latest_release_tag", return_value=tag_url), 91 ): 92 console = Console(file=buf, force_terminal=True, color_system="truecolor", width=160) 93 _banner.build_welcome_banner( 94 console=console, model="x", cwd="/tmp", 95 session_id="abc123", 96 tools=[{"function": {"name": "read_file"}}], 97 get_toolset_for_tool=lambda n: "file", 98 ) 99 100 raw = buf.getvalue() 101 # The existing version label must still be present in the title 102 assert "Hermes Agent v" in raw, "Version label missing from title" 103 # OSC-8 hyperlink escape sequence present with the release URL 104 assert "\x1b]8;" in raw, "OSC-8 hyperlink not emitted" 105 assert "releases/tag/v2026.4.23" in raw, "Release URL missing from banner output" 106 107 108 def test_build_welcome_banner_title_falls_back_when_no_tag(): 109 """Without a resolvable tag, the panel title renders as plain text (no hyperlink escape).""" 110 import io 111 from unittest.mock import patch as _patch 112 import hermes_cli.banner as _banner 113 import model_tools as _mt 114 import tools.mcp_tool as _mcp 115 116 _banner._latest_release_cache = None 117 buf = io.StringIO() 118 with ( 119 _patch.object(_mt, "check_tool_availability", return_value=(["web"], [])), 120 _patch.object(_banner, "get_available_skills", return_value={}), 121 _patch.object(_banner, "get_update_result", return_value=None), 122 _patch.object(_mcp, "get_mcp_status", return_value=[]), 123 _patch.object(_banner, "get_latest_release_tag", return_value=None), 124 ): 125 console = Console(file=buf, force_terminal=True, color_system="truecolor", width=160) 126 _banner.build_welcome_banner( 127 console=console, model="x", cwd="/tmp", 128 session_id="abc123", 129 tools=[{"function": {"name": "read_file"}}], 130 get_toolset_for_tool=lambda n: "file", 131 ) 132 133 raw = buf.getvalue() 134 assert "Hermes Agent v" in raw, "Version label missing from title" 135 assert "\x1b]8;" not in raw, "OSC-8 hyperlink should not be emitted without a tag"