"""Tests for the X Content Pipeline — output gate, tweet queue, approval stages.""" import sys import os import sqlite3 import json # Add telegram/ to path for imports sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "telegram")) # ─── Output Gate Tests ─────────────────────────────────────────────── from output_gate import classify, gate_for_tweet_queue, GateResult def test_system_content_blocked(): """Pipeline messages are classified as system.""" system_messages = [ "PR #2274 merged to main via cherry-pick", "Pipeline extracted 5 claims from batch-extract-50.sh run", "Schema v14 migration applied, approval_queue table created", "Qdrant has 1004 vectors, embed-on-merge running", "VPS deploy: scp merge.py to /opt/teleo-eval/pipeline/", "Ganymede reviewed and approved the code changes", "PIPELINE_OWNED_PREFIXES filter working correctly", ] for msg in system_messages: result = classify(msg) assert not result.is_public, f"System message passed gate: {msg!r}" assert len(result.blocked_reasons) > 0 def test_public_content_passes(): """Legitimate public content passes the gate.""" public_messages = [ "Futarchy governance is the most underrated coordination mechanism in crypto right now. Here's why the market is wrong about MetaDAO.", "The GLP-1 revolution isn't just about weight loss. New kidney data from FLOW trial shows 24% reduction in disease progression.", "Thread: Why I think AI alignment needs more empirical evidence and less theoretical speculation", "Interesting take from Shapiro on how AI will transform creative industries. The talent embrace angle is underappreciated.", ] for msg in public_messages: result = classify(msg) assert result.is_public, f"Public message blocked: {msg!r} — reasons: {result.blocked_reasons}" def test_mixed_content_blocked(): """Content with system signals defaults to blocking (safety).""" mixed = "Great PR #2274 about futarchy mechanisms merged today" result = classify(mixed) assert not result.is_public, "Mixed system+public content should be blocked" def test_empty_content_blocked(): """Empty content is blocked.""" assert not classify("") assert not classify(" ") assert not classify(None) def test_internal_urls_blocked(): """Internal URLs are blocked by tweet gate.""" result = gate_for_tweet_queue("Check out http://localhost:3000/teleo/repo") assert not result.is_public assert any("internal URL" in r for r in result.blocked_reasons) def test_short_content_blocked(): """Very short content is blocked by tweet gate.""" result = gate_for_tweet_queue("ok") assert not result.is_public assert any("too short" in r for r in result.blocked_reasons) def test_gate_result_bool(): """GateResult is truthy when public, falsy when blocked.""" assert GateResult(True, [], 0.9) assert not GateResult(False, ["test"], 0.9) # ─── Tweet Submission Tests ────────────────────────────────────────── def _make_db(): """Create an in-memory DB with approval_queue table.""" conn = sqlite3.connect(":memory:") conn.row_factory = sqlite3.Row conn.execute("""CREATE TABLE approval_queue ( id INTEGER PRIMARY KEY AUTOINCREMENT, type TEXT NOT NULL, content TEXT NOT NULL, originating_agent TEXT NOT NULL, context TEXT, leo_review_status TEXT DEFAULT 'pending_leo', leo_review_note TEXT, status TEXT DEFAULT 'pending', decision_by TEXT, rejection_reason TEXT, telegram_message_id INTEGER, submitted_at TEXT DEFAULT (datetime('now')), decided_at TEXT, expires_at TEXT )""") return conn def test_submit_tweet_draft(): """Valid tweet draft enters the approval queue.""" try: from x_publisher import submit_tweet_draft except ImportError: print(" (skipped — aiohttp not installed)") return conn = _make_db() request_id, error = submit_tweet_draft( conn, content="Futarchy governance is the most underrated mechanism in crypto. The market is systematically mispricing coordination infrastructure.", agent="hermes", post_type="original", ) assert error is None, f"Submit failed: {error}" assert request_id > 0 row = conn.execute("SELECT * FROM approval_queue WHERE id = ?", (request_id,)).fetchone() assert row["type"] == "tweet" assert row["originating_agent"] == "hermes" assert row["leo_review_status"] == "pending_leo" ctx = json.loads(row["context"]) assert ctx["post_type"] == "original" assert ctx["target_account"] == "TeleoHumanity" def test_submit_blocks_system_content(): """System content is rejected before entering the queue.""" try: from x_publisher import submit_tweet_draft except ImportError: print(" (skipped — aiohttp not installed)") return conn = _make_db() request_id, error = submit_tweet_draft( conn, content="PR #2274 merged via cherry-pick. Pipeline schema v14 deployed to VPS.", agent="hermes", ) assert request_id == -1 assert "Output gate blocked" in error # Verify nothing was inserted count = conn.execute("SELECT COUNT(*) FROM approval_queue").fetchone()[0] assert count == 0 def test_submit_blocks_opsec(): """OPSEC-violating content is rejected.""" try: from x_publisher import submit_tweet_draft except ImportError: print(" (skipped — aiohttp not installed)") return conn = _make_db() request_id, error = submit_tweet_draft( conn, content="Excited to announce our $5M Series A raise! The deal terms are amazing.", agent="hermes", ) assert request_id == -1 assert "OPSEC" in error def test_submit_reply_tweet(): """Reply tweets include reply_to_url in context.""" try: from x_publisher import submit_tweet_draft except ImportError: print(" (skipped — aiohttp not installed)") return conn = _make_db() request_id, error = submit_tweet_draft( conn, content="Great point about futarchy adoption friction. The real bottleneck is UX, not mechanism design.", agent="rio", post_type="reply", reply_to_url="https://x.com/user/status/12345", ) assert error is None ctx = json.loads(conn.execute( "SELECT context FROM approval_queue WHERE id = ?", (request_id,) ).fetchone()["context"]) assert ctx["reply_to_url"] == "https://x.com/user/status/12345" assert ctx["post_type"] == "reply" # ─── Approval Stages Tests ────────────────────────────────────────── from approval_stages import ( OutputGateStage, OpsecStage, Vote, StageResult, run_sync_stages, register_stage, ApprovalStage, ) def test_output_gate_stage_passes_public(): """Output gate stage approves public content.""" stage = OutputGateStage() result = stage.check({ "content": "AI alignment needs empirical evidence, not just theory. Here's what the data shows.", "originating_agent": "theseus", }) assert result.vote == Vote.APPROVE def test_output_gate_stage_blocks_system(): """Output gate stage rejects system content.""" stage = OutputGateStage() result = stage.check({ "content": "Pipeline merge.py deployed to VPS via scp, systemd restarted", "originating_agent": "epimetheus", }) assert result.vote == Vote.REJECT def test_opsec_stage_blocks_financials(): """OPSEC stage rejects financial content.""" try: stage = OpsecStage() result = stage.check({ "content": "Our $10M fund is performing well with 23.5% returns", }) assert result.vote == Vote.REJECT except ImportError: # approvals.py needs telegram module — test OPSEC patterns directly import re pattern = re.compile(r"\$[\d,.]+[KMBkmb]?\b", re.IGNORECASE) assert pattern.search("Our $10M fund") def test_opsec_stage_passes_clean(): """OPSEC stage passes clean content.""" try: stage = OpsecStage() result = stage.check({ "content": "Futarchy governance shows promise for decentralized coordination", }) assert result.vote == Vote.APPROVE except ImportError: # approvals.py needs telegram — just verify no OPSEC patterns match import re patterns = [ re.compile(r"\$[\d,.]+[KMBkmb]?\b", re.IGNORECASE), re.compile(r"\b(deal terms?|valuation|cap table)\b", re.IGNORECASE), ] content = "Futarchy governance shows promise for decentralized coordination" assert not any(p.search(content) for p in patterns) def test_sync_stages_block_system(): """Full pipeline blocks system content at output gate (stage 0).""" result = run_sync_stages({ "content": "PR #2274 cherry-pick merged via pipeline, schema v14 deployed", "originating_agent": "epimetheus", "type": "tweet", }) assert not result.approved assert any(sr.stage_name == "output_gate" and sr.vote == Vote.REJECT for sr in result.stage_results) def test_sync_stages_pass_public(): """Full pipeline passes public content through to human stage (abstain).""" result = run_sync_stages({ "content": "The convergence of futarchy governance and AI alignment is the most important intersection nobody is talking about.", "originating_agent": "hermes", "type": "tweet", }) # Not "approved" yet because human stage abstains (async) # But no rejections rejects = [sr for sr in result.stage_results if sr.vote == Vote.REJECT] assert len(rejects) == 0 def test_custom_stage_registration(): """Custom stages can be registered and run.""" class TestStage(ApprovalStage): name = "test_custom" priority = 5 # between opsec (1) and human (10) weight = 0.5 def check(self, request): if "test_block" in request.get("content", ""): return StageResult(self.name, Vote.REJECT, self.weight, "Test blocked") return StageResult(self.name, Vote.APPROVE, self.weight, "Test passed") register_stage(TestStage()) # Verify it runs in the pipeline result = run_sync_stages({ "content": "test_block this content", "originating_agent": "test", "type": "tweet", }) assert any(sr.stage_name == "test_custom" for sr in result.stage_results) # ─── Run all tests ─────────────────────────────────────────────────── if __name__ == "__main__": tests = [v for k, v in sorted(globals().items()) if k.startswith("test_")] passed = 0 failed = 0 for test in tests: try: test() print(f" PASS {test.__name__}") passed += 1 except Exception as e: print(f" FAIL {test.__name__}: {e}") failed += 1 print(f"\n{passed} passed, {failed} failed out of {passed + failed}") sys.exit(1 if failed else 0)