# -*- coding: utf-8 -*- """tg-reply --followup: «сначала ответ — потом работа» (аудит 2026-10-16). Hermetic. Запуск: ./venv/bin/python tests/test_followup.py Проверяет: досылка уходит в ЧАТ входящего (группа → группа, личка → личка), не в личку по умолчанию; работает после ReplySent (обычный tg-reply уже запрещён); повтор с тем же текстом — SKIP (ledger), другой текст — уходит; FollowupSent в сторе; неизвестный id — ValueError; мандат мин-уровня содержит правило. """ import os import sys import tempfile sys.path.insert(0, "/root/simbiot-harness") os.environ["SIMBIOT_DB"] = os.path.join(tempfile.mkdtemp(prefix="simbiot_followup_"), "harness.db") from harness import commands, es, tg_inbox from harness.levels import LEVELS es.init_db() fails = [] def check(name, cond, extra=""): print(("PASS" if cond else "FAIL"), name, ("| " + str(extra)) if not cond else "") if not cond: fails.append(name) SENT = [] tg_inbox.send_message = lambda chat_id, text, reply_to=None, timeout=35: ( SENT.append((chat_id, text)) or {"ok": True}) tg_inbox._api = lambda *a, **k: {"ok": True} for mid, chat in (("m-1", 7569223131), ("m-2", -1004402779881)): es.append_event("inbox", mid, "MessageReceived", {"update_id": int(mid[2:]), "chat_id": chat, "text": "сделай", "kind": "text"}) tg_inbox.send_reply("m-2", "понял, делаю") check("первый ответ ушёл в группу", SENT[-1] == (-1004402779881, "понял, делаю"), SENT) try: tg_inbox.send_reply("m-2", "итог") check("повторный tg-reply запрещён", False) except ValueError: check("повторный tg-reply запрещён", True) def followup(mid, text): import hashlib aid = f"followup:{mid}:{hashlib.sha1(text.encode()).hexdigest()[:10]}" return commands.deliver_artifact(aid, kind="message", owner="test", caption=text, send=lambda: tg_inbox.send_followup(mid, text)) check("досылка после ответа — отправлена", followup("m-2", "готово: X") is True) check("досылка — в чат входящего (группа)", SENT[-1] == (-1004402779881, "готово: X"), SENT[-1]) n = len(SENT) check("повтор того же итога — SKIP", followup("m-2", "готово: X") is False and len(SENT) == n) check("другой итог — уходит", followup("m-2", "ещё: Y") is True and len(SENT) == n + 1) check("личка → личка", followup("m-1", "итог лички") and SENT[-1][0] == 7569223131) evs = [e for e in es.get_events(0, 1000) if e["event_type"] == "FollowupSent"] check("FollowupSent в сторе", len(evs) == 3, len(evs)) try: tg_inbox.send_followup("m-404", "x") check("неизвестный id — ValueError", False) except ValueError: check("неизвестный id — ValueError", True) check("мандат мин-уровня: правило «сначала ответ»", "СНАЧАЛА ОТВЕТ" in LEVELS["min"].mandate and "--followup" in LEVELS["min"].mandate) print(f"\n{'OK' if not fails else 'FAILED'}: {len(fails)} fail(s)") sys.exit(1 if fails else 0)