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
131 lines
4.4 KiB
Python
131 lines
4.4 KiB
Python
"""Tests for multi-stage command completions, categories, and dynamic completions."""
|
|
|
|
from unittest.mock import patch
|
|
|
|
from EvoScientist.commands._completion_engine import compute_completions
|
|
|
|
|
|
class TestTopLevelCompletions:
|
|
def test_slash_returns_all_commands(self):
|
|
r = compute_completions("/", 1)
|
|
names = [c.text for c in r.candidates]
|
|
assert "/mcp" in names
|
|
assert "/help" in names
|
|
assert "/new" in names
|
|
|
|
def test_prefix_filters(self):
|
|
r = compute_completions("/mc", 3)
|
|
names = [c.text for c in r.candidates]
|
|
assert "/mcp" in names
|
|
assert "/help" not in names
|
|
|
|
def test_exact_leaf_hides(self):
|
|
r = compute_completions("/new", 4)
|
|
assert r.kind == "empty"
|
|
|
|
def test_exact_with_subcommands_hides_without_space(self):
|
|
r = compute_completions("/mcp", 4)
|
|
assert r.kind == "empty"
|
|
|
|
def test_results_have_categories(self):
|
|
r = compute_completions("/", 1)
|
|
cats = {c.category for c in r.candidates}
|
|
assert "Session" in cats
|
|
assert "MCP" in cats
|
|
assert "General" in cats
|
|
|
|
def test_category_ordering(self):
|
|
r = compute_completions("/", 1)
|
|
cats = []
|
|
for c in r.candidates:
|
|
if not cats or cats[-1] != c.category:
|
|
cats.append(c.category)
|
|
assert cats.index("Session") < cats.index("General")
|
|
|
|
|
|
class TestAliasVisibility:
|
|
def test_alias_prefix_matches(self):
|
|
r = compute_completions("/skills-re", 10)
|
|
names = [c.text for c in r.candidates]
|
|
assert "/autoskills" in names
|
|
|
|
def test_alias_exact_hides_leaf(self):
|
|
r = compute_completions("/quit", 5)
|
|
assert r.kind == "empty"
|
|
|
|
def test_alias_exact_hides_without_space(self):
|
|
r = compute_completions("/q", 2)
|
|
assert r.kind == "empty"
|
|
|
|
def test_alias_space_shows_subcommands(self):
|
|
r = compute_completions("/skills-review ", 15)
|
|
names = [c.text for c in r.candidates]
|
|
assert "approve" in names
|
|
assert "list" in names
|
|
|
|
|
|
class TestSubcommandCompletions:
|
|
def test_space_shows_subcommands(self):
|
|
r = compute_completions("/mcp ", 5)
|
|
names = [c.text for c in r.candidates]
|
|
assert "list" in names
|
|
assert "config" in names
|
|
assert "install" in names
|
|
|
|
def test_prefix_filters_subcommands(self):
|
|
r = compute_completions("/mcp c", 6)
|
|
names = [c.text for c in r.candidates]
|
|
assert "config" in names
|
|
assert "list" not in names
|
|
|
|
def test_leaf_subcommand_stops(self):
|
|
r = compute_completions("/autoskills help ", 17)
|
|
assert r.kind == "empty"
|
|
|
|
def test_channel_all_types(self):
|
|
r = compute_completions("/channel ", 9)
|
|
names = [c.text for c in r.candidates]
|
|
assert "status" in names
|
|
assert "telegram" in names
|
|
assert "discord" in names
|
|
|
|
def test_autoskills_subcommands(self):
|
|
r = compute_completions("/autoskills ", 12)
|
|
names = [c.text for c in r.candidates]
|
|
assert "approve" in names
|
|
assert "reject" in names
|
|
|
|
|
|
class TestDynamicCompletions:
|
|
def _invalidate_mcp_cache(self):
|
|
from EvoScientist.commands.implementation.mcp import MCPCommand
|
|
from EvoScientist.commands.manager import manager
|
|
|
|
cmd = manager.get_command("/mcp")
|
|
if isinstance(cmd, MCPCommand):
|
|
cmd._invalidate_server_cache()
|
|
|
|
def test_mcp_config_server_names(self):
|
|
self._invalidate_mcp_cache()
|
|
fake_config = {"myserver": {}, "other": {}}
|
|
with patch("EvoScientist.mcp.load_mcp_config", return_value=fake_config):
|
|
r = compute_completions("/mcp config ", 12)
|
|
names = [c.text for c in r.candidates]
|
|
assert "myserver" in names
|
|
assert "other" in names
|
|
|
|
def test_mcp_config_prefix_filters(self):
|
|
self._invalidate_mcp_cache()
|
|
fake_config = {"myserver": {}, "other": {}}
|
|
with patch("EvoScientist.mcp.load_mcp_config", return_value=fake_config):
|
|
r = compute_completions("/mcp config my", 14)
|
|
names = [c.text for c in r.candidates]
|
|
assert names == ["myserver"]
|
|
|
|
def test_mcp_remove_shows_servers(self):
|
|
self._invalidate_mcp_cache()
|
|
fake_config = {"srv1": {}}
|
|
with patch("EvoScientist.mcp.load_mcp_config", return_value=fake_config):
|
|
r = compute_completions("/mcp remove ", 12)
|
|
assert [c.text for c in r.candidates] == ["srv1"]
|