Files
EvoScientist/tests/test_cli_completion.py
T
Ziheng Zhang 6593ac9b5b fix(cli): submit slash command on Enter when name prefixes another (#293) (#300)
Typing `/model` and pressing Enter did nothing in the TUI; the picker
only opened via `/model --save` or `/model <name>`. The completion popup
matched both `/model` and `/model-fallback` by prefix, so the
exact-match-hide guard (which required a single match) never fired. With
the popup still visible, the TUI's Enter handler completed the text
instead of submitting the command, so it never executed.

Treat the typed prefix as an exact match whenever it equals any matched
command name, not only when it is the sole match. This hides the popup
on a complete command name so Enter submits it, even when a longer
command shares the prefix.
2026-06-18 14:07:43 +01:00

87 lines
3.5 KiB
Python

"""Tests for CLI SlashCommandCompleter (prompt_toolkit adapter)."""
from unittest.mock import MagicMock
from EvoScientist.cli.interactive import SlashCommandCompleter
def _doc(text: str):
"""Create a minimal prompt_toolkit Document stub."""
doc = MagicMock()
doc.text_before_cursor = text
return doc
class TestSlashCommandCompleter:
"""Verify that ``SlashCommandCompleter.get_completions`` correctly
delegates to the shared ``compute_completions`` engine and translates
candidates into prompt_toolkit ``Completion`` objects.
"""
def test_top_level_slash_shows_commands(self):
completer = SlashCommandCompleter()
completions = list(completer.get_completions(_doc("/he"), None))
texts = {c.text for c in completions}
assert "/help" in texts
def test_exact_command_no_space_hides(self):
completer = SlashCommandCompleter()
completions = list(completer.get_completions(_doc("/help"), None))
assert completions == []
def test_exact_command_hides_even_when_prefix_of_another(self):
"""Regression for #293: ``/model`` must hide on exact match so Enter
submits it, even though ``/model-fallback`` shares the prefix. Before
the fix the popup stayed visible (two prefix matches) and the TUI's
Enter handler completed the text instead of executing the command.
"""
completer = SlashCommandCompleter()
# Sanity: both commands share the prefix, so a partial prefix lists both.
partial = {c.text for c in completer.get_completions(_doc("/mode"), None)}
assert {"/model", "/model-fallback"} <= partial
# Exact ``/model`` with no trailing space → hide.
completions = list(completer.get_completions(_doc("/model"), None))
assert completions == []
def test_non_slash_returns_empty(self):
completer = SlashCommandCompleter()
completions = list(completer.get_completions(_doc("hello"), None))
assert completions == []
def test_trailing_space_shows_subcommands(self):
completer = SlashCommandCompleter()
completions = list(completer.get_completions(_doc("/mcp "), None))
texts = {c.text for c in completions}
assert "list" in texts
assert "add" in texts
def test_subcommand_prefix_filters(self):
completer = SlashCommandCompleter()
completions = list(completer.get_completions(_doc("/mcp lis"), None))
texts = {c.text for c in completions}
assert texts == {"list"}
def test_exact_subcommand_hides(self):
completer = SlashCommandCompleter()
completions = list(completer.get_completions(_doc("/mcp list"), None))
assert completions == []
def test_results_sorted_alphabetically(self):
completer = SlashCommandCompleter()
completions = list(completer.get_completions(_doc("/"), None))
texts = [c.text for c in completions]
assert texts == sorted(texts)
def test_display_meta_is_description(self):
completer = SlashCommandCompleter()
completions = list(completer.get_completions(_doc("/he"), None))
for c in completions:
if c.text == "/help":
assert c.display_meta is not None
def test_subcommand_completions_sorted(self):
completer = SlashCommandCompleter()
completions = list(completer.get_completions(_doc("/mcp "), None))
texts = [c.text for c in completions]
assert texts == sorted(texts)