Something went wrong. Try again.
The agent that grows with you
Something went wrong. Try again.
6.6 kB · 184 lines
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185"""Tests for file path autocomplete in the CLI completer."""
import osfrom unittest.mock import MagicMock
import pytestfrom prompt_toolkit.document import Documentfrom prompt_toolkit.formatted_text import to_plain_text
from hermes_cli.commands import SlashCommandCompleter, _file_size_label
def _display_names(completions): """Extract plain-text display names from a list of Completion objects.""" return [to_plain_text(c.display) for c in completions]
def _display_metas(completions): """Extract plain-text display_meta from a list of Completion objects.""" return [to_plain_text(c.display_meta) if c.display_meta else "" for c in completions]
@pytest.fixturedef completer(): return SlashCommandCompleter()
class TestExtractPathWord: def test_relative_path(self): assert SlashCommandCompleter._extract_path_word("look at ./src/main.py") == "./src/main.py"
def test_home_path(self): assert SlashCommandCompleter._extract_path_word("edit ~/docs/") == "~/docs/"
def test_absolute_path(self): assert SlashCommandCompleter._extract_path_word("read /etc/hosts") == "/etc/hosts"
def test_parent_path(self): assert SlashCommandCompleter._extract_path_word("check ../config.yaml") == "../config.yaml"
def test_path_with_slash_in_middle(self): assert SlashCommandCompleter._extract_path_word("open src/utils/helpers.py") == "src/utils/helpers.py"
def test_plain_word_not_path(self): assert SlashCommandCompleter._extract_path_word("hello world") is None
def test_empty_string(self): assert SlashCommandCompleter._extract_path_word("") is None
def test_single_word_no_slash(self): assert SlashCommandCompleter._extract_path_word("README.md") is None
def test_word_after_space(self): assert SlashCommandCompleter._extract_path_word("fix the bug in ./tools/") == "./tools/"
def test_just_dot_slash(self): assert SlashCommandCompleter._extract_path_word("./") == "./"
def test_just_tilde_slash(self): assert SlashCommandCompleter._extract_path_word("~/") == "~/"
class TestPathCompletions: def test_lists_current_directory(self, tmp_path): (tmp_path / "file_a.py").touch() (tmp_path / "file_b.txt").touch() (tmp_path / "subdir").mkdir()
old_cwd = os.getcwd() os.chdir(tmp_path) try: completions = list(SlashCommandCompleter._path_completions("./")) names = _display_names(completions) assert "file_a.py" in names assert "file_b.txt" in names assert "subdir/" in names finally: os.chdir(old_cwd)
def test_filters_by_prefix(self, tmp_path): (tmp_path / "alpha.py").touch() (tmp_path / "beta.py").touch() (tmp_path / "alpha_test.py").touch()
completions = list(SlashCommandCompleter._path_completions(f"{tmp_path}/alpha")) names = _display_names(completions) assert "alpha.py" in names assert "alpha_test.py" in names assert "beta.py" not in names
def test_directories_have_trailing_slash(self, tmp_path): (tmp_path / "mydir").mkdir() (tmp_path / "myfile.txt").touch()
completions = list(SlashCommandCompleter._path_completions(f"{tmp_path}/")) names = _display_names(completions) metas = _display_metas(completions) assert "mydir/" in names idx = names.index("mydir/") assert metas[idx] == "dir"
def test_home_expansion(self, tmp_path, monkeypatch): monkeypatch.setenv("HOME", str(tmp_path)) (tmp_path / "testfile.md").touch()
completions = list(SlashCommandCompleter._path_completions("~/test")) names = _display_names(completions) assert "testfile.md" in names
def test_nonexistent_dir_returns_empty(self): completions = list(SlashCommandCompleter._path_completions("/nonexistent_dir_xyz/")) assert completions == []
def test_respects_limit(self, tmp_path): for i in range(50): (tmp_path / f"file_{i:03d}.txt").touch()
completions = list(SlashCommandCompleter._path_completions(f"{tmp_path}/", limit=10)) assert len(completions) == 10
def test_case_insensitive_prefix(self, tmp_path): (tmp_path / "README.md").touch()
completions = list(SlashCommandCompleter._path_completions(f"{tmp_path}/read")) names = _display_names(completions) assert "README.md" in names
class TestIntegration: """Test the completer produces path completions via the prompt_toolkit API."""
def test_slash_commands_still_work(self, completer): doc = Document("/hel", cursor_position=4) event = MagicMock() completions = list(completer.get_completions(doc, event)) names = _display_names(completions) assert "/help" in names
def test_path_completion_triggers_on_dot_slash(self, completer, tmp_path): (tmp_path / "test.py").touch() old_cwd = os.getcwd() os.chdir(tmp_path) try: doc = Document("edit ./te", cursor_position=9) event = MagicMock() completions = list(completer.get_completions(doc, event)) names = _display_names(completions) assert "test.py" in names finally: os.chdir(old_cwd)
def test_no_completion_for_plain_words(self, completer): doc = Document("hello world", cursor_position=11) event = MagicMock() completions = list(completer.get_completions(doc, event)) assert completions == []
def test_absolute_path_triggers_completion(self, completer): doc = Document("check /etc/hos", cursor_position=14) event = MagicMock() completions = list(completer.get_completions(doc, event)) names = _display_names(completions) # /etc/hosts should exist on Linux assert any("host" in n.lower() for n in names)
class TestFileSizeLabel: def test_bytes(self, tmp_path): f = tmp_path / "small.txt" f.write_text("hi") assert _file_size_label(str(f)) == "2B"
def test_kilobytes(self, tmp_path): f = tmp_path / "medium.txt" f.write_bytes(b"x" * 2048) assert _file_size_label(str(f)) == "2K"
def test_megabytes(self, tmp_path): f = tmp_path / "large.bin" f.write_bytes(b"x" * (2 * 1024 * 1024)) assert _file_size_label(str(f)) == "2.0M"
def test_nonexistent(self): assert _file_size_label("/nonexistent_xyz") == ""