"""Unit tests for slash.py — parse_slash + SlashRegistry.""" from __future__ import annotations import pytest from my_deepagent.slash import SlashParsed, SlashRegistry, parse_slash # --------------------------------------------------------------------------- # parse_slash # --------------------------------------------------------------------------- def test_parse_quit() -> None: result = parse_slash("/quit") assert result is not None assert result.name == "quit" assert result.args == () assert result.raw == "quit" def test_parse_agent_with_arg() -> None: result = parse_slash("/agent code-reviewer") assert result is not None assert result.name == "agent" assert result.args == ("code-reviewer",) def test_parse_model_with_slash_in_arg() -> None: result = parse_slash("/model anthropic/claude") assert result is not None assert result.name == "model" assert result.args == ("anthropic/claude",) def test_parse_plain_text_returns_none() -> None: assert parse_slash("hello world") is None def test_parse_empty_string_returns_none() -> None: assert parse_slash("") is None def test_parse_bare_slash_gives_empty_name() -> None: result = parse_slash("/") assert result is not None assert result.name == "" assert result.args == () assert result.raw == "" def test_parse_uppercase_normalized_to_lower() -> None: result = parse_slash("/QUIT") assert result is not None assert result.name == "quit" def test_parse_spaced_slash_command() -> None: result = parse_slash("/ spaced ") # body after strip of "/ spaced " → body = "spaced" (strip on body) assert result is not None assert result.name == "spaced" assert result.args == () # --------------------------------------------------------------------------- # SlashRegistry # --------------------------------------------------------------------------- @pytest.mark.asyncio async def test_registry_register_and_dispatch_returns_handler_value() -> None: reg = SlashRegistry() calls: list[str] = [] async def handler(cmd: SlashParsed) -> bool: calls.append(cmd.name) return False reg.register("foo", handler, help="test help") result = await reg.dispatch(SlashParsed(name="foo", args=(), raw="foo")) assert result is False assert calls == ["foo"] @pytest.mark.asyncio async def test_registry_unknown_name_returns_false() -> None: reg = SlashRegistry() result = await reg.dispatch(SlashParsed(name="nonexistent", args=(), raw="nonexistent")) assert result is False @pytest.mark.asyncio async def test_registry_handler_returning_true_propagates() -> None: reg = SlashRegistry() async def quit_handler(cmd: SlashParsed) -> bool: return True reg.register("quit", quit_handler, help="exit") result = await reg.dispatch(SlashParsed(name="quit", args=(), raw="quit")) assert result is True def test_registry_names_sorted() -> None: reg = SlashRegistry() async def noop(cmd: SlashParsed) -> bool: return False reg.register("zebra", noop) reg.register("apple", noop) reg.register("mango", noop) assert reg.names == ["apple", "mango", "zebra"] def test_registry_help_for_and_all_help() -> None: reg = SlashRegistry() async def noop(cmd: SlashParsed) -> bool: return False reg.register("quit", noop, help="exit the REPL") reg.register("help", noop, help="show commands") assert reg.help_for("quit") == "exit the REPL" assert reg.help_for("unknown") == "" pairs = dict(reg.all_help()) assert pairs["quit"] == "exit the REPL" assert pairs["help"] == "show commands"