#!/usr/bin/env python3 # -*- coding: utf-8 -*- """WP-PMAX-01 / WP-WX-02 离线回归:防封限流内存态逻辑 真机铁律口径:本测试**仅离线回归**内存态频控逻辑(不依赖 Redis/真机), 验证 AB-02 静默限流熔断 / AB-03 长暂停 + 高危动作互斥 / AB-06 风险分级的纯函数行为; **不替代**真机批量发送/加友的频控 E2E。 运行:python3 sdk/tests/test_rate_limiter_antiban.py """ import sys import time from pathlib import Path ROOT = Path(__file__).resolve().parents[2] sys.path.insert(0, str(ROOT / "sdk" / "app")) from services import rate_limiter as rl # noqa: E402 fails = 0 def check(cond, msg): global fails if cond: print(f" ✓ {msg}") else: print(f" ✗ FAIL: {msg}") fails += 1 print("[WP-PMAX-01] 防封限流离线回归") lim = rl.AntiDetectRateLimiter() DEV = "test_dev_offline" # ---- AB-02 静默限流熔断 ---- print("· AB-02 静默限流熔断") lim.reset_silent_throttle(DEV) # 未触发时不应熔断 try: lim._check_silent_throttle(DEV) check(True, "初始无熔断,写类放行") except rl.SilentThrottleCooldown: check(False, "初始不应熔断") cd1 = lim.trip_silent_throttle(DEV) check(rl.SILENT_THROTTLE_COOLDOWN[0] <= cd1 <= rl.SILENT_THROTTLE_COOLDOWN[1] * 1.01, f"首次熔断冷却在 30-60min 区间({cd1:.0f}s)") raised = False try: lim._check_silent_throttle(DEV) except rl.SilentThrottleCooldown as e: raised = True check(e.hits == 1, "熔断期写类被拒,hits=1") check(raised, "熔断期 _check_silent_throttle 抛 SilentThrottleCooldown") cd2 = lim.trip_silent_throttle(DEV) check(cd2 > cd1, f"二次触发退避升级({cd2:.0f}s > {cd1:.0f}s)") # 真 msg_id 回来 → reset 清零 lim.reset_silent_throttle(DEV) try: lim._check_silent_throttle(DEV) check(True, "reset 后熔断解除,写类恢复") except rl.SilentThrottleCooldown: check(False, "reset 后不应再熔断") # ---- AB-03 长暂停节拍 ---- print("· AB-03 长暂停 + 高危互斥") rl._action_seq.pop(DEV, None) rl._long_pause_until.pop(DEV, None) rl._next_pause_at[DEV] = 3 # 强制第 3 个动作触发长暂停 pause_raised = False for i in range(3): try: lim._check_long_pause(DEV) except rl.LongPauseRequired as e: pause_raised = True check(rl.LONG_PAUSE_RANGE[0] <= e.wait_seconds <= rl.LONG_PAUSE_RANGE[1], f"第{i+1}动作触发长暂停 {e.wait_seconds:.0f}s(10-30min)") check(pause_raised, "达节拍阈值触发 LongPauseRequired") # 暂停期内再检查应继续被拦 try: lim._check_long_pause(DEV) check(False, "长暂停期内应继续拦截") except rl.LongPauseRequired: check(True, "长暂停期内持续 fast-fail") # 高危动作互斥 rl._last_highrisk.pop(DEV, None) lim._check_highrisk_combo(DEV, "add_friend") # 记录首个高危 combo_raised = False try: lim._check_highrisk_combo(DEV, "mass_send") # 不同类高危,窗口内 except rl.HighRiskComboBlocked as e: combo_raised = True check(e.wait_seconds > 0, f"高危组合互斥拦截 {e.wait_seconds:.0f}s") check(combo_raised, "短窗口内混跑不同类高危被拦") # 同类高危不拦 try: lim._check_highrisk_combo(DEV, "add_friend") check(True, "同类高危动作不互斥") except rl.HighRiskComboBlocked: check(False, "同类高危不应互斥") # ---- AB-06 风险分级 ---- print("· AB-06 风险分级") check(rl.classify_risk("静默限流熔断中") == rl.RISK_NEEDS_HUMAN, "熔断→needs_human") check(rl.classify_risk("高危动作组合互斥:add_friend") == rl.RISK_BLOCK, "高危互斥→block") check(rl.classify_risk("当前不在操作时段") == rl.RISK_LOG, "时段→log") check(rl.classify_risk("长会话中断,需再等 600s") == rl.RISK_LOG, "长暂停→log") # ---- runtime 看板字段 ---- print("· AB-05 runtime 看板") st = lim.get_runtime_state(DEV) check("silent_throttle" in st and "long_pause" in st and "last_highrisk" in st, "get_runtime_state 含三段运行态") # 清理 lim.reset_silent_throttle(DEV) rl._action_seq.pop(DEV, None) rl._long_pause_until.pop(DEV, None) rl._last_highrisk.pop(DEV, None) print(f"\n[done] 失败 {fails} 条") sys.exit(1 if fails else 0)