"""Channel lists render as one button menu, and each button opens that channel's card.""" import asyncio import sys sys.path.insert(0, "/app") from db.database import init_db, close_db_pool from db.repository import Repository from services.admin_bot import AdminBotService SRC = -1009999000030 TRG = -1009999000031 async def _cleanup(repo): pool = await repo._get_pool() async with pool.acquire() as conn: await conn.execute("DELETE FROM posts WHERE source_channel_id = $1;", SRC) await conn.execute("DELETE FROM sources WHERE channel_id = $1;", SRC) await conn.execute("DELETE FROM targets WHERE channel_id = $1;", TRG) await conn.execute("DELETE FROM channel_categories;") def button_data(rows): return [b.data.decode() for row in rows for b in row] async def run_tests(): await init_db() repo = Repository() await _cleanup(repo) try: await _run_assertions(repo) finally: await _cleanup(repo) await close_db_pool() print("All admin menu tests passed successfully!") async def _run_assertions(repo): bot = AdminBotService(repo=repo, review_channel_id=0, admin_user_ids=[1]) src_id = await repo.add_source(SRC, "Menu Source", "menusrc") target_id = await repo.add_target(TRG, "Menu Target", None) await repo.create_raw_post(SRC, 1, "a post", content_hash="menu_1") # Source list: one button per source, pointing at its detail view. text, buttons = await bot._render_source_list() assert "Menu Source" not in text, "the list message itself should stay a header, not a card dump" data = button_data(buttons) assert f"src_view:{src_id}" in data, f"source button missing: {data}" # Target list likewise, and it flags auto-routing. text, buttons = await bot._render_target_list() data = button_data(buttons) assert f"trg_view:{target_id}" in data, f"target button missing: {data}" def label_for(rows, want): for row in rows: for b in row: if b.data.decode() == want: return b.text raise AssertionError(f"button {want} not found") assert "🤖" not in label_for(buttons, f"trg_view:{target_id}"), "no auto-route configured yet" await repo.set_target_auto_sources(target_id, [SRC]) _, buttons = await bot._render_target_list() assert "🤖" in label_for(buttons, f"trg_view:{target_id}"), "auto-routed target should be flagged" # Source detail card carries fetch buttons and a way back. card, buttons = await bot._render_source_config(src_id) assert "Menu Source" in card and str(SRC) in card assert "1" in card, f"collected count missing from card: {card}" assert "Menu Target" in card, "auto-routed target should be listed on the source card" data = button_data(buttons) assert f"hist:{SRC}:20" in data and f"histn:{SRC}" in data, f"fetch buttons missing: {data}" assert "list_src" in data, f"back button missing: {data}" # Target detail card can get back to its list too. card, buttons = await bot._render_target_config(target_id) data = button_data(buttons) assert f"auto_src:{target_id}" in data, f"auto-route button missing: {data}" assert "list_trg" in data, f"back button missing: {data}" # Missing ids degrade gracefully instead of raising. card, buttons = await bot._render_source_config(999999) assert "یافت نشد" in card and buttons == [] if __name__ == "__main__": asyncio.run(run_tests())