"""任务间互发消息单测:标记块提取 + 投递分支。 不跑真实模型、不联网:extract 是纯函数;deliver 用内存 SQLite + 最小表结构(tasks 只含投递读到的列,followups 表由 peer_messages 自己建)。 """ import sqlite3 import sys import unittest from pathlib import Path try: from cloud.peer_messages import ( deliver_peer_message, extract_peer_messages, peer_message_prefix, ) except ImportError: # discover -s cloud/tests 时把项目根目录补进 sys.path sys.path.insert(0, str(Path(__file__).resolve().parents[2])) from cloud.peer_messages import ( deliver_peer_message, extract_peer_messages, peer_message_prefix, ) def _conn(): conn = sqlite3.connect(":memory:") conn.row_factory = sqlite3.Row conn.execute( "CREATE TABLE tasks(id TEXT PRIMARY KEY, project_id TEXT," " created_by TEXT, status TEXT, verify_command TEXT," " goal_mode INTEGER, resume_message TEXT, updated_at REAL)") return conn def _task(conn, task_id, project_id="p1", status="queued", created_by="tester", verify_command=None, goal_mode=None, resume_message=None): conn.execute( "INSERT INTO tasks(id, project_id, created_by, status," " verify_command, goal_mode, resume_message, updated_at)" " VALUES(?, ?, ?, ?, ?, ?, ?, 0)", (task_id, project_id, created_by, status, verify_command, goal_mode, resume_message)) conn.commit() def _followups(conn, task_id=None, sender=None): sql = "SELECT * FROM task_followups" conds = [] args = [] if task_id is not None: conds.append("task_id = ?") args.append(task_id) if sender is not None: conds.append("sender_task_id = ?") args.append(sender) if conds: sql += " WHERE " + " AND ".join(conds) try: return conn.execute(sql, args).fetchall() except Exception: return [] def _task_row(conn, task_id): return conn.execute( "SELECT * FROM tasks WHERE id = ?", (task_id,)).fetchone() class ExtractTestCase(unittest.TestCase): def test_single_block_default_queued(self): text = ("做完了。\n```task-message\n" "{\"to\": \"T2\", \"body\": \"请评审\"}\n```\n") msgs = extract_peer_messages(text) self.assertEqual(len(msgs), 1) self.assertEqual(msgs[0]["to_task_id"], "T2") self.assertEqual(msgs[0]["body"], "请评审") self.assertEqual(msgs[0]["mode"], "queued") def test_direct_mode_and_alias_key(self): text = ("```task-message\n{\"to_task_id\": \"T2\"," " \"body\": \"急\", \"mode\": \"direct\"}\n```") msgs = extract_peer_messages(text) self.assertEqual(msgs[0]["mode"], "direct") def test_bad_mode_falls_back_to_queued(self): text = ("```task-message\n{\"to\": \"T2\", \"body\": \"x\"," " \"mode\": \"smoke-signal\"}\n```") self.assertEqual(extract_peer_messages(text)[0]["mode"], "queued") def test_invalid_blocks_skipped(self): text = ( "```task-message\nnot json\n```\n" "```task-message\n{\"body\": \"no to\"}\n```\n" "```task-message\n{\"to\": \"T2\"}\n```\n" "```task-message\n{\"to\": \"T2\", \"body\": \" \"}\n```\n" "```task-message\n{\"to\": \"T2\", \"body\": \"ok\"}\n```\n") msgs = extract_peer_messages(text) self.assertEqual(len(msgs), 1) self.assertEqual(msgs[0]["body"], "ok") def test_body_too_long_skipped(self): text = ("```task-message\n{\"to\": \"T2\", \"body\": \"" + "x" * 20001 + "\"}\n```") self.assertEqual(extract_peer_messages(text), []) def test_max_five_per_turn(self): blocks = "".join( "```task-message\n{\"to\": \"T%d\", \"body\": \"m%d\"}\n```\n" % (i, i) for i in range(7)) self.assertEqual(len(extract_peer_messages(blocks)), 5) def test_no_fence_or_empty(self): self.assertEqual(extract_peer_messages("plain text"), []) self.assertEqual(extract_peer_messages(""), []) self.assertEqual(extract_peer_messages(None), []) class DeliverTestCase(unittest.TestCase): def test_running_target_becomes_followup(self): conn = _conn() _task(conn, "T1") _task(conn, "T2", status="running") status, detail = deliver_peer_message(conn, "T1", "T2", "请评审") self.assertEqual(status, "delivered") rows = _followups(conn, task_id="T2", sender="T1") self.assertEqual(len(rows), 1) self.assertEqual(rows[0]["body"], "请评审") self.assertEqual(rows[0]["mode"], "queued") self.assertEqual(rows[0]["status"], "pending") self.assertEqual(rows[0]["created_by"], "tester") self.assertEqual(rows[0]["id"], detail) def test_queued_target_direct_mode(self): conn = _conn() _task(conn, "T1") _task(conn, "T2", status="queued") status, _ = deliver_peer_message(conn, "T1", "T2", "急", "direct") self.assertEqual(status, "delivered") self.assertEqual(_followups(conn, task_id="T2")[0]["mode"], "direct") def test_completed_target_revives(self): conn = _conn() _task(conn, "T1") _task(conn, "T2", status="completed") status, detail = deliver_peer_message(conn, "T1", "T2", "回来一下") self.assertEqual(status, "revived") self.assertEqual(detail, "") row = _task_row(conn, "T2") self.assertEqual(row["status"], "queued") self.assertEqual(row["resume_message"], peer_message_prefix("T1") + "回来一下") self.assertEqual(_followups(conn, task_id="T2"), []) def test_revive_blocked_when_resume_occupied(self): conn = _conn() _task(conn, "T1") _task(conn, "T2", status="completed", resume_message="旧消息") status, detail = deliver_peer_message(conn, "T1", "T2", "新消息") self.assertEqual(status, "rejected") self.assertIn("pending resume", detail) self.assertEqual(_task_row(conn, "T2")["resume_message"], "旧消息") def test_failed_and_interrupted_revive(self): for st in ("failed", "interrupted"): conn = _conn() _task(conn, "T1") _task(conn, "T2", status=st) status, _ = deliver_peer_message(conn, "T1", "T2", "回来") self.assertEqual(status, "revived") self.assertEqual(_task_row(conn, "T2")["status"], "queued") def test_goal_targets_rejected(self): conn = _conn() _task(conn, "T1") _task(conn, "T2", status="running", verify_command="true") status, _ = deliver_peer_message(conn, "T1", "T2", "x") self.assertEqual(status, "rejected") conn = _conn() _task(conn, "T1") _task(conn, "T2", status="running", goal_mode=1) status, _ = deliver_peer_message(conn, "T1", "T2", "x") self.assertEqual(status, "rejected") def test_self_cross_project_merged_missing_rejected(self): conn = _conn() _task(conn, "T1") _task(conn, "T2", project_id="p2") _task(conn, "T3", status="merged") self.assertEqual( deliver_peer_message(conn, "T1", "T1", "x")[0], "rejected") self.assertEqual( deliver_peer_message(conn, "T1", "T2", "x")[0], "rejected") self.assertEqual( deliver_peer_message(conn, "T1", "T3", "x")[0], "rejected") self.assertEqual( deliver_peer_message(conn, "T1", "NOPE", "x")[0], "rejected") self.assertEqual( deliver_peer_message(conn, "NOPE", "T1", "x")[0], "rejected") # 拒绝不写任何东西、不改状态。 self.assertEqual(_followups(conn), []) self.assertEqual(_task_row(conn, "T3")["status"], "merged") def test_body_validation(self): conn = _conn() _task(conn, "T1") _task(conn, "T2", status="running") self.assertEqual( deliver_peer_message(conn, "T1", "T2", " ")[0], "rejected") self.assertEqual( deliver_peer_message(conn, "T1", "T2", "x" * 20001)[0], "rejected") if __name__ == "__main__": unittest.main()