# Copyright (c) 2026 Krzysztof Rudnicki. MIT licence. """The cat and dog commands, and their data-driven aliases.""" from __future__ import annotations import json from typing import TYPE_CHECKING import httpx import pytest from signal_bot.animals import ( ALIAS_FILE, load_aliases, register_animal_commands, ) from signal_bot.decorators import cooldown from signal_bot.http_client import ImageFetcher from signal_bot.models import parse_envelope from signal_bot.registry import CommandContext, CommandRegistry from tests.conftest import REPLY_GROUP, envelope if TYPE_CHECKING: from pathlib import Path from signal_bot.config import Config from signal_bot.store import Store from signal_bot.transport_fake import FakeTransport IMAGE = b"png-bytes" def _fetcher() -> ImageFetcher: def handler(request: httpx.Request) -> httpx.Response: if "thecatapi" in str(request.url): return httpx.Response(200, json=[{"url": "https://cdn/cat.png"}]) if "dog.ceo" in str(request.url): return httpx.Response(200, json={"message": "https://cdn/dog.png"}) return httpx.Response(200, content=IMAGE) return ImageFetcher(httpx.AsyncClient(transport=httpx.MockTransport(handler))) def _ctx( transport: FakeTransport, store: Store, config: Config, name: str, text: str ) -> CommandContext: message = parse_envelope(envelope(text=text)) assert message is not None return CommandContext( message=message, recipient=REPLY_GROUP, transport=transport, store=store, config=config, command_name=name, now=100.0, day="2026-08-29", ) def test_alias_file_ships_with_the_package() -> None: assert ALIAS_FILE.is_file() aliases = load_aliases() assert "!cat" in aliases["cat"] assert "!pies" in aliases["dog"] def test_comment_keys_are_skipped(tmp_path: Path) -> None: path = tmp_path / "a.json" path.write_text(json.dumps({"_comment": "x", "cat": ["!cat"]}), encoding="utf-8") assert load_aliases(path) == {"cat": ["!cat"]} def test_styled_unicode_aliases_resolve() -> None: registry = CommandRegistry() register_animal_commands(registry, _fetcher()) assert registry.resolve("!\U0001d5f8\U0001d5fc\U0001d601") is not None assert registry.resolve("!miau").name == "cat" assert registry.resolve("!szczek").name == "dog" @pytest.mark.parametrize(("trigger", "name"), [("!cat", "cat"), ("!dog", "dog")]) async def test_commands_post_an_image( transport: FakeTransport, store: Store, config: Config, trigger: str, name: str ) -> None: registry = CommandRegistry() register_animal_commands(registry, _fetcher()) command = registry.resolve(trigger) assert command is not None await command.handler(_ctx(transport, store, config, name, trigger)) assert transport.sent[0].image_base64 is not None assert transport.sent[0].recipient == REPLY_GROUP async def test_decorator_is_applied_when_given( transport: FakeTransport, store: Store, config: Config ) -> None: registry = CommandRegistry() register_animal_commands(registry, _fetcher(), decorate=cooldown(10, "wait {seconds}")) store.record_use("uuid-alice", "cat", 100.0, "2026-08-29") command = registry.resolve("!cat") assert command is not None await command.handler(_ctx(transport, store, config, "cat", "!cat")) assert transport.texts == ["wait 11"] def test_custom_alias_table_is_honoured() -> None: registry = CommandRegistry() register_animal_commands(registry, _fetcher(), aliases={"cat": ["!c"], "dog": ["!d"]}) assert registry.resolve("!c").name == "cat" assert registry.resolve("!cat") is None