128 lines
4.5 KiB
Python
128 lines
4.5 KiB
Python
"""端到端验证任务池闭环:投递 → 发送完成移除 → 新消息重新投递。
|
||
|
||
覆盖用户核心诉求:
|
||
1. AI 回复成功(sent)后任务从队列移除
|
||
2. 对方发新消息 = 新 dedup_key → 重新投递新任务(不被 _recently 防抖挡住)
|
||
3. 同一消息不会被重复投递(防抖生效)
|
||
4. has_active_pending 对已移除任务返回 False → 允许新投递
|
||
"""
|
||
import os
|
||
import sys
|
||
import time
|
||
|
||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)) + "/..")
|
||
|
||
from engine_b import DataEngine # noqa: E402
|
||
|
||
|
||
class FakeBot:
|
||
"""最小化 bot 桩:记录投递、模拟任务完成移除。"""
|
||
|
||
def __init__(self):
|
||
self.pending = {} # fp_hex -> state
|
||
self.enqueued = []
|
||
self.removed = []
|
||
|
||
def has_active_pending(self, fp_hex):
|
||
state = self.pending.get(fp_hex)
|
||
if state is None:
|
||
return False
|
||
return str(state.get("stage") or "") not in {
|
||
"sent", "cancelled", "expired", "cleared",
|
||
}
|
||
|
||
def enqueue_detected(self, fp_hex, dedup_key="", detected_by="engine_b",
|
||
chat_text="", display_name="", last_lines=None):
|
||
if fp_hex in self.pending and self.has_active_pending(fp_hex):
|
||
return False, "已有活跃任务"
|
||
self.pending[fp_hex] = {
|
||
"stage": "queued",
|
||
"dedup_key": dedup_key,
|
||
"chat_text": chat_text,
|
||
"display_name": display_name,
|
||
}
|
||
self.enqueued.append({
|
||
"fp_hex": fp_hex, "dedup_key": dedup_key,
|
||
"chat_text": chat_text, "display_name": display_name,
|
||
})
|
||
return True, "ok"
|
||
|
||
def mark_sent_and_remove(self, fp_hex):
|
||
"""模拟发送成功 → 任务移除。"""
|
||
self.pending.pop(fp_hex, None)
|
||
self.removed.append(fp_hex)
|
||
|
||
|
||
def run():
|
||
passed = 0
|
||
|
||
def check(name, cond):
|
||
nonlocal passed
|
||
if cond:
|
||
passed += 1
|
||
print(f" [PASS] {name}")
|
||
else:
|
||
print(f" [FAIL] {name}")
|
||
|
||
bot = FakeBot()
|
||
eng = DataEngine.__new__(DataEngine)
|
||
eng.bot = bot
|
||
eng._recently = {}
|
||
eng.processed_count = 0
|
||
eng.enqueued_count = 0
|
||
eng.last_error = ""
|
||
|
||
fp = "ab" * 40 # 80 hex chars
|
||
now = int(time.time())
|
||
|
||
print("== 场景1:新消息投递 ==")
|
||
entry1 = {
|
||
"display_name": "测试客户",
|
||
"history": [{"content": "第一条", "ts": now - 300}],
|
||
}
|
||
last_user = entry1["history"][-1]
|
||
eng._maybe_enqueue(fp, entry1, last_user)
|
||
check("消息1 投递成功", len(bot.enqueued) == 1 and bot.enqueued[0]["chat_text"] == "第一条")
|
||
check("任务已入队", bot.pending.get(fp, {}).get("stage") == "queued")
|
||
|
||
print("== 场景2:同一消息重复轮询不重复投递(防抖) ==")
|
||
eng._maybe_enqueue(fp, entry1, last_user)
|
||
check("同 dedup_key 300s 内不重复投递", len(bot.enqueued) == 1)
|
||
|
||
print("== 场景3:AI 回复成功 → 任务移除 ==")
|
||
bot.mark_sent_and_remove(fp)
|
||
check("发送成功任务被移除", fp not in bot.pending and fp in bot.removed)
|
||
check("has_active_pending 返回 False", bot.has_active_pending(fp) is False)
|
||
|
||
print("== 场景4:对方发新消息 → 重新投递新任务 ==")
|
||
entry2 = {
|
||
"display_name": "测试客户",
|
||
"history": [
|
||
{"content": "第一条", "ts": now - 300},
|
||
{"content": "在吗", "ts": now - 60},
|
||
],
|
||
}
|
||
last_user2 = entry2["history"][-1]
|
||
eng._maybe_enqueue(fp, entry2, last_user2)
|
||
check("新消息(新时间戳)重新投递", len(bot.enqueued) == 2)
|
||
check("新任务入队", bot.pending.get(fp, {}).get("chat_text") == "在吗")
|
||
check("新 dedup_key 不同", bot.enqueued[0]["dedup_key"] != bot.enqueued[1]["dedup_key"])
|
||
|
||
print("== 场景5:消息B在防抖窗口内第二次轮询不重复 ==")
|
||
eng._maybe_enqueue(fp, entry2, last_user2)
|
||
check("消息2 防抖生效", len(bot.enqueued) == 2)
|
||
|
||
print("== 场景6:队列已有活跃任务时引擎B只补标记不重复投递 ==")
|
||
# 场景4 后任务已入队(活跃),此时再检测到同一条消息不应重复投递
|
||
bot.enqueued_count_before = len(bot.enqueued)
|
||
eng._maybe_enqueue(fp, entry2, last_user2)
|
||
check("活跃任务存在时不重复投递", len(bot.enqueued) == 2)
|
||
|
||
print(f"\n结果: {passed}/10 通过")
|
||
return passed == 10
|
||
|
||
|
||
if __name__ == "__main__":
|
||
ok = run()
|
||
sys.exit(0 if ok else 1)
|