"""Auto-routing must queue to subscribed targets WITHOUT replacing the admin review 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_A = -1009999000020 SRC_B = -1009999000021 TRG = -1009999000022 class FakeQueue: def __init__(self): self.pushed = [] async def push_target_post(self, target_id, payload): self.pushed.append((target_id, payload)) async def get_target_queue_size(self, target_id): return 0 class FakeAI: async def rewrite_for_target(self, raw_text, target, has_media=False, *args, **kwargs): return f"[{target.title}] {raw_text}" async def _cleanup(repo): pool = await repo._get_pool() async with pool.acquire() as conn: for cid in (SRC_A, SRC_B): await conn.execute("DELETE FROM posts WHERE source_channel_id = $1;", cid) await conn.execute("DELETE FROM sources WHERE channel_id = $1;", cid) await conn.execute("DELETE FROM targets WHERE channel_id = $1;", TRG) async def run_tests(): await init_db() repo = Repository() await _cleanup(repo) await repo.add_source(SRC_A, "Source A", None) await repo.add_source(SRC_B, "Source B", None) target_id = await repo.add_target(TRG, "Auto Target", None) # A brand new target routes nothing. assert await repo.get_targets_auto_routed_from(SRC_A) == [] # Toggle is a switch, not a one-way door. assert await repo.toggle_target_auto_source(target_id, SRC_A) is True routed = await repo.get_targets_auto_routed_from(SRC_A) assert len(routed) == 1 and routed[0].id == target_id assert routed[0].auto_source_ids == [SRC_A] assert await repo.toggle_target_auto_source(target_id, SRC_A) is False assert await repo.get_targets_auto_routed_from(SRC_A) == [] # Subscribe to A only; B must stay manual. await repo.set_target_auto_sources(target_id, [SRC_A]) queue = FakeQueue() bot = AdminBotService(repo=repo, ai_processor=FakeAI(), queue=queue, review_channel_id=0, admin_user_ids=[1]) post_a = await repo.create_raw_post(SRC_A, 1, "post from A", content_hash="auto_a") post_b = await repo.create_raw_post(SRC_B, 2, "post from B", content_hash="auto_b") assert await bot.auto_route_post(post_a) == 1, "subscribed source must route" assert await bot.auto_route_post(post_b) == 0, "unsubscribed source must not route" assert len(queue.pushed) == 1, f"expected 1 enqueue, got {queue.pushed}" tid, payload = queue.pushed[0] assert tid == target_id assert payload["text"] == "[Auto Target] post from A", f"AI rewrite not applied: {payload['text']}" assert payload["post_id"] == post_a # The post is recorded against the target but must NOT be marked published yet. stored = await repo.get_post_by_id(post_a) assert stored.status == "pending_review", f"auto-routing must not publish, got {stored.status}" assert len(stored.published_to) == 1 assert stored.published_to[0]["target_id"] == target_id assert stored.published_to[0]["published_at"] is None # A soft-deleted post is never auto-routed. await repo.soft_delete_post(post_b) await repo.set_target_auto_sources(target_id, [SRC_A, SRC_B]) assert await bot.auto_route_post(post_b) == 0, "deleted posts must not route" await _cleanup(repo) await close_db_pool() print("All auto-routing tests passed successfully!") if __name__ == "__main__": asyncio.run(run_tests())