"""Unit tests for bar_commands: drink-name parsing and menu persistence. The unit CI job installs only pytest, so bar_commands' heavy imports (discord, ai_functions -> openai/tiktoken, communication_subroutine -> flask) are stubbed only when genuinely absent - locally the real packages are used. """ import sys import types def _stub_if_missing(name, build): if name in sys.modules: return try: __import__(name) except ImportError: sys.modules[name] = build() def _build_discord(): mod = types.ModuleType("discord") class Object: def __init__(self, id=None): # noqa: A002 - mirrors discord.Object self.id = id mod.Object = Object ext = types.ModuleType("discord.ext") commands = types.ModuleType("discord.ext.commands") class Cog: pass def hybrid_command(**_kwargs): return lambda fn: fn commands.Cog = Cog commands.hybrid_command = hybrid_command ext.commands = commands mod.ext = ext sys.modules["discord.ext"] = ext sys.modules["discord.ext.commands"] = commands return mod def _build_ai_functions(): mod = types.ModuleType("ai_functions") async def handle_response(*_a, **_k): return ("stub", []) mod.handle_response = handle_response return mod def _build_comm(): mod = types.ModuleType("communication_subroutine") mod.PREPPED_TRACKS = {"now_playing": ""} return mod _stub_if_missing("discord", _build_discord) _stub_if_missing("ai_functions", _build_ai_functions) _stub_if_missing("communication_subroutine", _build_comm) import bar_commands # noqa: E402 (after stubbing) def test_extract_name_strips_markers_and_markdown(): assert bar_commands._extract_name("🍸 Krew Wilka\nskładniki...") == "Krew Wilka" assert bar_commands._extract_name("**Miód Odyna**\nfoo") == "Miód Odyna" assert bar_commands._extract_name("1. Cichy Grom\nbar") == "Cichy Grom" assert bar_commands._extract_name("\n\n Ostatni Skald \nx") == "Ostatni Skald" def test_extract_name_fallback_on_empty(): assert bar_commands._extract_name("") == "Bezimienny drink" assert bar_commands._extract_name("\n \n") == "Bezimienny drink" def test_menu_round_trip(tmp_path, monkeypatch): menu_file = tmp_path / "menu.json" monkeypatch.setattr(bar_commands, "MENU_FILE", str(menu_file)) assert bar_commands._load_menu() == [] # missing file -> empty bar_commands._append_menu({"nazwa": "Krew Wilka", "autor": "Hammer"}) bar_commands._append_menu({"nazwa": "Miód Odyna", "autor": "Siara"}) menu = bar_commands._load_menu() assert [d["nazwa"] for d in menu] == ["Krew Wilka", "Miód Odyna"] def test_load_menu_survives_corrupt_file(tmp_path, monkeypatch): menu_file = tmp_path / "menu.json" menu_file.write_text("{ not json", encoding="utf-8") monkeypatch.setattr(bar_commands, "MENU_FILE", str(menu_file)) assert bar_commands._load_menu() == []