Files
workphone-sdk/sdk/tests/test_rate_limiter_antiban.py

127 lines
4.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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}s10-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)