test_cli_image_command.py
1 from pathlib import Path 2 from unittest.mock import patch 3 4 from cli import ( 5 HermesCLI, 6 _collect_query_images, 7 _format_image_attachment_badges, 8 _termux_example_image_path, 9 ) 10 11 12 def _make_cli(): 13 cli_obj = HermesCLI.__new__(HermesCLI) 14 cli_obj._attached_images = [] 15 return cli_obj 16 17 18 def _make_image(path: Path) -> Path: 19 path.parent.mkdir(parents=True, exist_ok=True) 20 path.write_bytes(b"\x89PNG\r\n\x1a\n") 21 return path 22 23 24 class TestImageCommand: 25 def test_handle_image_command_attaches_local_image(self, tmp_path): 26 img = _make_image(tmp_path / "photo.png") 27 cli_obj = _make_cli() 28 29 with patch("cli._cprint"): 30 cli_obj._handle_image_command(f"/image {img}") 31 32 assert cli_obj._attached_images == [img] 33 34 def test_handle_image_command_supports_quoted_path_with_spaces(self, tmp_path): 35 img = _make_image(tmp_path / "my photo.png") 36 cli_obj = _make_cli() 37 38 with patch("cli._cprint"): 39 cli_obj._handle_image_command(f'/image "{img}"') 40 41 assert cli_obj._attached_images == [img] 42 43 def test_handle_image_command_rejects_non_image_file(self, tmp_path): 44 file_path = tmp_path / "notes.txt" 45 file_path.write_text("hello\n", encoding="utf-8") 46 cli_obj = _make_cli() 47 48 with patch("cli._cprint") as mock_print: 49 cli_obj._handle_image_command(f"/image {file_path}") 50 51 assert cli_obj._attached_images == [] 52 rendered = " ".join(str(arg) for call in mock_print.call_args_list for arg in call.args) 53 assert "Not a supported image file" in rendered 54 55 56 class TestCollectQueryImages: 57 def test_collect_query_images_accepts_explicit_image_arg(self, tmp_path): 58 img = _make_image(tmp_path / "diagram.png") 59 60 message, images = _collect_query_images("describe this", str(img)) 61 62 assert message == "describe this" 63 assert images == [img] 64 65 def test_collect_query_images_extracts_leading_path(self, tmp_path): 66 img = _make_image(tmp_path / "camera.png") 67 68 message, images = _collect_query_images(f"{img} what do you see?") 69 70 assert message == "what do you see?" 71 assert images == [img] 72 73 def test_collect_query_images_supports_tilde_paths(self, tmp_path, monkeypatch): 74 home = tmp_path / "home" 75 img = _make_image(home / "storage" / "shared" / "Pictures" / "cat.png") 76 monkeypatch.setenv("HOME", str(home)) 77 78 message, images = _collect_query_images("describe this", "~/storage/shared/Pictures/cat.png") 79 80 assert message == "describe this" 81 assert images == [img] 82 83 84 class TestTermuxImageHints: 85 def test_termux_example_image_path_prefers_real_shared_storage_root(self, monkeypatch): 86 existing = {"/sdcard", "/storage/emulated/0"} 87 monkeypatch.setattr("cli.os.path.isdir", lambda path: path in existing) 88 89 hint = _termux_example_image_path() 90 91 assert hint == "/sdcard/Pictures/cat.png" 92 93 94 class TestImageBadgeFormatting: 95 def test_compact_badges_use_filename_on_narrow_terminals(self, tmp_path): 96 img = _make_image(tmp_path / "Screenshot 2026-04-09 at 11.22.33 AM.png") 97 98 badges = _format_image_attachment_badges([img], image_counter=1, width=40) 99 100 assert badges.startswith("[📎 ") 101 assert "Image #1" not in badges 102 103 def test_compact_badges_summarize_multiple_images(self, tmp_path): 104 img1 = _make_image(tmp_path / "one.png") 105 img2 = _make_image(tmp_path / "two.png") 106 107 badges = _format_image_attachment_badges([img1, img2], image_counter=2, width=45) 108 109 assert badges == "[📎 2 images attached]"