57176b359a
Replace config.yaml-driven model selection with registry snapshot resolution across the runtime chain: - ConfigurableModelMiddleware reads configurable["runtime_snapshot_id"] only; model/model_provider overrides are rejected with MODEL_CONFIG_OUTSIDE_SNAPSHOT - MessageBudgetMiddleware derives budgets from snapshot reserves (system/tools/attachments) and re-resolves the summarizer per snapshot - Agent factory and subagent factory resolve models via SnapshotRuntime (auxiliary/tool_selector/scheduler -> defaults.auxiliary ?? defaults.primary) - Remove ModelFallbackMiddleware, /model-fallback command, and fallback chain - Add model_registry/runtime.py SnapshotRuntime glue layer Legacy config.yaml LLM fields, /model command, and llm/models.py remain for Task 7. Report: .superpowers/sdd/briefs/task-6-report.md
114 lines
4.5 KiB
Python
114 lines
4.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 when another command 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.
|
|
|
|
The original prefix sibling (``/model-fallback``) was removed with
|
|
the fallback chain; a temporary stub command recreates the scenario.
|
|
"""
|
|
from EvoScientist.commands.base import Command
|
|
from EvoScientist.commands.manager import manager
|
|
|
|
class _StubCommand(Command):
|
|
name = "/model-extra"
|
|
description = "stub"
|
|
|
|
async def execute(self, ctx, args):
|
|
return None
|
|
|
|
manager.register(_StubCommand())
|
|
try:
|
|
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-extra"} <= partial
|
|
# Exact ``/model`` with no trailing space → hide.
|
|
completions = list(completer.get_completions(_doc("/model"), None))
|
|
assert completions == []
|
|
finally:
|
|
manager._commands.pop("/model-extra", None)
|
|
|
|
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_match_engine_order(self):
|
|
"""Completer preserves the category-based order from compute_completions."""
|
|
from EvoScientist.commands._completion_engine import compute_completions
|
|
|
|
completer = SlashCommandCompleter()
|
|
completions = list(completer.get_completions(_doc("/"), None))
|
|
engine_result = compute_completions("/", 1)
|
|
assert [c.text for c in completions] == [
|
|
c.text for c in engine_result.candidates
|
|
]
|
|
|
|
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_match_engine_order(self):
|
|
"""Subcommand completions preserve the order from compute_completions."""
|
|
from EvoScientist.commands._completion_engine import compute_completions
|
|
|
|
completer = SlashCommandCompleter()
|
|
completions = list(completer.get_completions(_doc("/mcp "), None))
|
|
engine_result = compute_completions("/mcp ", 5)
|
|
assert [c.text for c in completions] == [
|
|
c.text for c in engine_result.candidates
|
|
]
|