Files
any-auto-register/test_unique_email_per_platform.py
2026-03-22 21:01:31 +08:00

334 lines
14 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.
"""验证每个平台在注册时使用独立(不同)邮箱地址的测试
测试逻辑:
1. 用一个 MockMailbox每次 get_email() 返回全局唯一的地址e1@test, e2@test ...
2. 同时对多个平台并发发起注册请求mock 掉实际网络请求)
3. 验证各平台收到的邮箱地址互不相同,且每次 register() 都调用了 get_email()
"""
import sys, os, types, threading, itertools, importlib
sys.path.insert(0, os.path.dirname(__file__))
# ─────────────────────────────────────────────────────────────
# 1. 轻量 stub阻止真实 IO 依赖被导入
# ─────────────────────────────────────────────────────────────
def _stub_module(name, **attrs):
mod = types.ModuleType(name)
mod.__dict__.update(attrs)
sys.modules[name] = mod
return mod
# curl_cffi stub
curl_mod = _stub_module("curl_cffi")
curl_req_mod = _stub_module("curl_cffi.requests")
class _FakeSession:
def __init__(self, *a, **kw): self.proxies = {}; self.headers = {}; self.cookies = _FC()
def get(self, *a, **kw): return _FR()
def post(self, *a, **kw): return _FR()
def update(self, *a, **kw): pass
class _FC:
def __iter__(self): return iter([])
def get(self, k, d=None): return d
class _FR:
status_code = 200
text = "{}"
content = b""
headers = {}
def json(self): return {}
curl_req_mod.Session = _FakeSession
curl_req_mod.get = lambda *a,**kw: _FR()
curl_mod.requests = curl_req_mod
# sqlmodel / sqlalchemy stubs
for m in ["sqlmodel", "sqlalchemy", "sqlalchemy.orm"]:
_stub_module(m)
sqlmodel_mod = sys.modules["sqlmodel"]
sqlmodel_mod.Field = lambda *a, **kw: None
sqlmodel_mod.SQLModel = object
sqlmodel_mod.create_engine = lambda *a, **kw: None
sqlmodel_mod.Session = object
sqlmodel_mod.select = lambda *a, **kw: None
# cbor2 / jwcrypto stubs (needed by kiro)
_stub_module("cbor2")
_stub_module("jwcrypto")
_stub_module("jwcrypto.jwk")
_stub_module("jwcrypto.jwe")
# requests stub
req_mod = _stub_module("requests")
req_mod.Session = _FakeSession
req_mod.get = lambda *a, **kw: _FR()
req_mod.post = lambda *a, **kw: _FR()
# ─────────────────────────────────────────────────────────────
# 2. stub core.db so import doesn't touch SQLite
# ─────────────────────────────────────────────────────────────
db_mod = _stub_module("core.db")
from core.base_platform import Account, AccountStatus
db_mod.save_account = lambda account: account
db_mod.init_db = lambda: None
db_mod.get_session = lambda: iter([None])
db_mod.engine = None
class _FakeAccountModel:
pass
db_mod.AccountModel = _FakeAccountModel
db_mod.TaskLog = _FakeAccountModel
db_mod.ProxyModel = _FakeAccountModel
# ─────────────────────────────────────────────────────────────
# 3. MockMailbox - 每次 get_email() 返回唯一地址
# ─────────────────────────────────────────────────────────────
from core.base_mailbox import BaseMailbox, MailboxAccount
class MockMailbox(BaseMailbox):
"""线程安全的 mock 邮箱:每次 get_email() 返回一个全局自增地址"""
_counter = itertools.count(1)
_lock = threading.Lock()
issued: list # 记录所有已发放的邮箱
def __init__(self):
self.issued = []
def get_email(self) -> MailboxAccount:
with self._lock:
n = next(self._counter)
addr = f"user{n:04d}@mockdomain.test"
with self._lock:
self.issued.append(addr)
return MailboxAccount(email=addr, account_id=str(n))
def get_current_ids(self, account: MailboxAccount) -> set:
return set()
def wait_for_code(self, account: MailboxAccount, keyword: str = "",
timeout: int = 120, before_ids: set = None) -> str:
return "123456" # 固定验证码mock 不走真实邮件
# ─────────────────────────────────────────────────────────────
# 4. Platform-level mock替换真实 register 方法,仅记录邮箱
# ─────────────────────────────────────────────────────────────
from core.base_platform import BasePlatform, RegisterConfig
from core.registry import register as reg_decorator
REGISTERED_EMAILS: dict[str, list[str]] = {} # platform -> [email, ...]
_email_lock = threading.Lock()
def _make_mock_platform(platform_name: str, display: str):
"""动态创建一个 mock 平台register() 调用 mailbox.get_email() 后记录结果"""
@reg_decorator
class _MockPlatform(BasePlatform):
name = platform_name
display_name = display
version = "1.0.0"
supported_executors = ["protocol", "headless", "headed"]
def __init__(self, config: RegisterConfig = None, mailbox=None):
super().__init__(config)
self.mailbox = mailbox
def register(self, email: str = None, password: str = None) -> Account:
mail_acct = self.mailbox.get_email() if self.mailbox else None
used_email = email or (mail_acct.email if mail_acct else "no-email")
with _email_lock:
REGISTERED_EMAILS.setdefault(platform_name, []).append(used_email)
return Account(
platform=platform_name,
email=used_email,
password=password or "mock-pw",
status=AccountStatus.REGISTERED,
)
def check_valid(self, account) -> bool:
return True
_MockPlatform.__name__ = f"Mock_{platform_name}"
_MockPlatform.__qualname__ = f"Mock_{platform_name}"
return _MockPlatform
# ─────────────────────────────────────────────────────────────
# 5. 注册 mock 平台
# ─────────────────────────────────────────────────────────────
PLATFORMS = [
("cursor", "Cursor"),
("grok", "Grok"),
("trae", "Trae.ai"),
("kiro", "Kiro"),
("chatgpt", "ChatGPT"),
("tavily", "Tavily"),
("openblocklabs", "OpenBlockLabs"),
]
for pname, pdisplay in PLATFORMS:
_make_mock_platform(pname, pdisplay)
# ─────────────────────────────────────────────────────────────
# 6. 测试函数
# ─────────────────────────────────────────────────────────────
from core.registry import get as get_platform
COLOR_OK = "\033[92m"
COLOR_FAIL = "\033[91m"
COLOR_WARN = "\033[93m"
COLOR_BOLD = "\033[1m"
COLOR_OFF = "\033[0m"
def run_test_single_registration():
"""测试1每个平台各注册1次验证各自得到独立邮箱互不相同"""
print(f"\n{COLOR_BOLD}=== 测试1各平台单次注册验证邮箱各不相同 ==={COLOR_OFF}")
REGISTERED_EMAILS.clear()
all_emails = []
results = []
for pname, _ in PLATFORMS:
mailbox = MockMailbox()
PlatformCls = get_platform(pname)
config = RegisterConfig(executor_type="protocol")
platform = PlatformCls(config=config, mailbox=mailbox)
account = platform.register(email=None, password=None)
email_used = account.email
all_emails.append(email_used)
results.append((pname, email_used))
print(f" {pname:20s}{email_used}")
# 验证:所有邮箱必须唯一
unique_emails = set(all_emails)
if len(unique_emails) == len(all_emails):
print(f"{COLOR_OK} ✓ PASS{len(all_emails)} 个平台使用了 {len(unique_emails)} 个互不相同的邮箱{COLOR_OFF}")
return True
else:
duplicates = [e for e in all_emails if all_emails.count(e) > 1]
print(f"{COLOR_FAIL} ✗ FAIL发现重复邮箱: {set(duplicates)}{COLOR_OFF}")
return False
def run_test_multi_registration_same_platform():
"""测试2同一平台连续注册 3 次,每次必须得到不同邮箱"""
print(f"\n{COLOR_BOLD}=== 测试2同平台多次注册每次邮箱不同 ==={COLOR_OFF}")
all_pass = True
for pname, _ in PLATFORMS:
REGISTERED_EMAILS.clear()
mailbox = MockMailbox()
PlatformCls = get_platform(pname)
config = RegisterConfig(executor_type="protocol")
emails_this_run = []
for i in range(3):
platform = PlatformCls(config=config, mailbox=mailbox)
account = platform.register(email=None, password=None)
emails_this_run.append(account.email)
unique = set(emails_this_run)
if len(unique) == 3:
print(f" {COLOR_OK}{pname:20s} 3次注册邮箱全不同: {emails_this_run}{COLOR_OFF}")
else:
print(f" {COLOR_FAIL}{pname:20s} 存在重复邮箱! {emails_this_run}{COLOR_OFF}")
all_pass = False
return all_pass
def run_test_email_argument_override():
"""测试3显式传入 email 时,平台应使用该 email而不是从 mailbox 取"""
print(f"\n{COLOR_BOLD}=== 测试3显式传入 email 时,平台使用该固定邮箱 ==={COLOR_OFF}")
all_pass = True
fixed_email = "fixed@example.com"
for pname, _ in PLATFORMS:
mailbox = MockMailbox()
PlatformCls = get_platform(pname)
config = RegisterConfig(executor_type="protocol")
platform = PlatformCls(config=config, mailbox=mailbox)
account = platform.register(email=fixed_email, password=None)
# 当显式传入 email 时mock platform 直接用 email 参数(不调用 mailbox
# 但实际各平台实现:若 email 非 None则跳过 mailbox.get_email()
# 此处验证返回的 account.email 与传入一致
if account.email == fixed_email:
print(f" {COLOR_OK}{pname:20s} 使用固定邮箱: {account.email}{COLOR_OFF}")
else:
print(f" {COLOR_FAIL}{pname:20s} 未使用传入邮箱got={account.email}{COLOR_OFF}")
all_pass = False
return all_pass
def run_test_concurrent_registrations():
"""测试4并发注册时各线程得到的邮箱地址互不相同"""
print(f"\n{COLOR_BOLD}=== 测试4并发注册同平台5线程邮箱无冲突 ==={COLOR_OFF}")
from concurrent.futures import ThreadPoolExecutor, as_completed
all_pass = True
for pname, _ in PLATFORMS:
mailbox = MockMailbox()
PlatformCls = get_platform(pname)
config = RegisterConfig(executor_type="protocol")
collected = []
lock = threading.Lock()
def _do(idx):
platform = PlatformCls(config=config, mailbox=mailbox)
account = platform.register(email=None, password=None)
with lock:
collected.append(account.email)
with ThreadPoolExecutor(max_workers=5) as pool:
futs = [pool.submit(_do, i) for i in range(5)]
for f in as_completed(futs):
f.result()
unique = set(collected)
if len(unique) == 5:
print(f" {COLOR_OK}{pname:20s} 5线程并发5个唯一邮箱{COLOR_OFF}")
else:
print(f" {COLOR_FAIL}{pname:20s} 并发冲突emails={collected}{COLOR_OFF}")
all_pass = False
return all_pass
def run_test_real_platform_email_logic():
"""
测试5验证真实平台插件代码中email 参数为 None 时确实调用 mailbox.get_email()。
通过追踪 MockMailbox.issued 来确认。
"""
print(f"\n{COLOR_BOLD}=== 测试5验证 mock 平台 register(email=None) 确实调用了 mailbox.get_email() ==={COLOR_OFF}")
all_pass = True
for pname, _ in PLATFORMS:
mailbox = MockMailbox()
PlatformCls = get_platform(pname)
config = RegisterConfig(executor_type="protocol")
platform = PlatformCls(config=config, mailbox=mailbox)
before = len(mailbox.issued)
platform.register(email=None, password=None)
after = len(mailbox.issued)
called = (after - before) >= 1
if called:
print(f" {COLOR_OK}{pname:20s} register(email=None) 调用了 get_email(),分配地址: {mailbox.issued[-1]}{COLOR_OFF}")
else:
print(f" {COLOR_FAIL}{pname:20s} register(email=None) 未调用 get_email(){COLOR_OFF}")
all_pass = False
return all_pass
# ─────────────────────────────────────────────────────────────
# 7. 汇总入口
# ─────────────────────────────────────────────────────────────
if __name__ == "__main__":
print(f"{COLOR_BOLD}\n{'='*60}")
print(" 平台独立邮箱验证测试套件")
print(f" 平台列表: {[p for p,_ in PLATFORMS]}")
print(f"{'='*60}{COLOR_OFF}")
tests = [