""" Test Milestone 16: Meet Telegram commands and cross-reference agent. Tests command logic and cross-reference analysis without needing a live Telegram bot. """ import asyncio import os import sys import json sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..')) # ─── Seed data ──────────────────────────────────────────────────────────────── async def seed_meet_signals(meeting_id: str, group_id: str): """Seed a meeting with realistic signals.""" from backend.agents.meet_ingestor import process_meet_chunk transcript = """ Alex: We decided to go with PostgreSQL. Final decision, no more debate. Priya: I'll set up the schema by Thursday and own the migration. Sam: The checkout timeout bug is still blocking us. Third time this sprint. Lisa: Dashboard is still waiting on design specs. That's a two-week blocker. Alex: The OAuth refactor is risky — touches everything. High risk if we rush it. Sam: Should we delay the deploy to next week? Nobody answered. """ await process_meet_chunk( meeting_id=meeting_id, group_id=group_id, chunk_index=0, text=transcript.strip(), speaker="Alex", timestamp="2026-03-21T10:00:00Z", is_final=True, # triggers summary generation ) async def seed_chat_signals(chat_group_id: str): """Seed Telegram chat signals to cross-reference against.""" from backend.pipeline import process_message_batch, set_lens set_lens(chat_group_id, "dev") messages = [ {"sender": "Alex", "text": "We should switch to MySQL instead of PostgreSQL. It's simpler.", "timestamp": "2026-03-15T09:00:00Z"}, {"sender": "Sam", "text": "The checkout timeout is back again. Still not fixed.", "timestamp": "2026-03-18T10:00:00Z"}, {"sender": "Priya", "text": "OAuth integration is going into the main branch directly, no feature branch.", "timestamp": "2026-03-19T11:00:00Z"}, ] await process_message_batch(chat_group_id, messages) # ─── Tests ──────────────────────────────────────────────────────────────────── async def test_meetsum_logic(): """Test that meetsum can find and return a meeting summary.""" from backend.db.chroma import query_signals import chromadb from backend.config import CHROMA_DB_PATH meeting_id = "test-meet-m16-sum" group_id = "test_meet_m16_sum" print("Testing /meetsum logic...") await seed_meet_signals(meeting_id, group_id) # Simulate what /meetsum does signals = query_signals(group_id, meeting_id, n_results=20) assert len(signals) > 0, "Expected signals for the seeded meeting" print(f" ✅ {len(signals)} signals found for meeting {meeting_id}") types = {s["metadata"]["type"] for s in signals if "metadata" in s} print(f" Signal types: {types}") has_summary = "meet_summary" in types has_structured = any(t in types for t in ("meet_decision", "meet_action_item", "meet_blocker", "meet_risk")) assert has_summary or has_structured, "Expected summary or structured signals" print(f" ✅ Has summary: {has_summary} | Has structured signals: {has_structured}") # Cleanup client = chromadb.PersistentClient(path=CHROMA_DB_PATH) try: client.delete_collection(f"ll_{group_id}") except Exception: pass async def test_meetask_logic(): """Test that meetask can answer questions about a meeting.""" from backend.pipeline import query_knowledge import chromadb from backend.config import CHROMA_DB_PATH meeting_id = "test-meet-m16-ask" group_id = "test_meet_m16_ask" print("\nTesting /meetask logic...") await seed_meet_signals(meeting_id, group_id) # Test 1: Question about decisions answer = await query_knowledge(group_id, f"From meeting {meeting_id}: What database did we decide on?") assert len(answer) > 10, "Expected a substantive answer" postgres_mentioned = "postgres" in answer.lower() or "database" in answer.lower() or "sql" in answer.lower() assert postgres_mentioned, f"Expected PostgreSQL to be mentioned. Got: {answer[:200]}" print(f" ✅ Decision query answered: {answer[:120]}...") # Test 2: Question about action items answer2 = await query_knowledge(group_id, f"From meeting {meeting_id}: Who is setting up the schema?") assert len(answer2) > 10, "Expected a substantive answer about schema ownership" print(f" ✅ Action item query answered: {answer2[:120]}...") # Cleanup client = chromadb.PersistentClient(path=CHROMA_DB_PATH) try: client.delete_collection(f"ll_{group_id}") except Exception: pass async def test_meetmatch_cross_reference(): """Test cross-reference finds contradictions and confirmations.""" from backend.agents.meet_cross_ref import find_cross_references, format_cross_ref_for_telegram import chromadb from backend.config import CHROMA_DB_PATH meeting_id = "test-meet-m16-match" meet_group = "test_meet_m16_match" chat_group = "test_chat_m16_match" print("\nTesting /meetmatch cross-reference...") await seed_meet_signals(meeting_id, meet_group) await seed_chat_signals(chat_group) # Run cross-reference analysis = await find_cross_references( meeting_id=meeting_id, group_id=meet_group, cross_ref_group_ids=[chat_group], ) if analysis.get("error"): print(f" ⚠️ Cross-reference returned error: {analysis['error']}") # This is OK if the groups are empty — test passes but notes the condition else: contradictions = analysis.get("contradictions", []) confirmations = analysis.get("confirmations", []) blind_spots = analysis.get("blind_spots", []) print(f" Found: {len(contradictions)} contradiction(s), {len(confirmations)} confirmation(s), {len(blind_spots)} blind spot(s)") # We seeded a clear contradiction: meeting says PostgreSQL, chat says MySQL # And a confirmation: checkout timeout mentioned in both # At least one of these categories should have results total = len(contradictions) + len(confirmations) + len(blind_spots) assert total > 0, "Expected at least one cross-reference finding (contradiction: PostgreSQL vs MySQL)" print(f" ✅ {total} total cross-reference findings") if contradictions: print(f" ✅ Contradiction found: {contradictions[0]['meeting_signal'][:80]}") if confirmations: print(f" ✅ Confirmation found: {confirmations[0]['meeting_signal'][:80]}") # Test formatter formatted = format_cross_ref_for_telegram(analysis, meeting_id) assert len(formatted) > 20, "Expected a non-empty formatted message" assert meeting_id in formatted, "Expected meeting ID in formatted output" print(f" ✅ Telegram formatter produced {len(formatted)} char message") print(f" Preview: {formatted[:200]}...") # Cleanup client = chromadb.PersistentClient(path=CHROMA_DB_PATH) for gid in [meet_group, chat_group]: try: client.delete_collection(f"ll_{gid}") except Exception: pass async def test_command_handlers_importable(): """Test that all three command handlers can be imported without errors.""" try: from backend.bot.bot import cmd_meetsum, cmd_meetask, cmd_meetmatch print("\n ✅ cmd_meetsum importable") print(" ✅ cmd_meetask importable") print(" ✅ cmd_meetmatch importable") except ImportError as e: print(f"\n ❌ Command import failed: {e}") raise async def main(): print("Running Milestone 16 tests...\n") await test_meetsum_logic() await test_meetask_logic() await test_meetmatch_cross_reference() await test_command_handlers_importable() print("\n🎉 MILESTONE 16 PASSED — Meet commands working, cross-reference finding connections") asyncio.run(main())