import asyncio import threading import time from contextlib import contextmanager from pathlib import Path from types import SimpleNamespace from src.core.register import ERROR_TASK_CANCELLED, RegistrationResult from src.database.models import Base, RegistrationTask from src.database.session import DatabaseSessionManager from src.services import EmailServiceType from src.services.base import BaseEmailService, EmailServiceCancelledError from src.web.routes import registration as registration_routes class FakeTaskManager: def __init__(self): self.cancelled = set() self.status_updates = [] self.logs = {} self.batch_status = {} def is_cancelled(self, task_uuid): return task_uuid in self.cancelled def cancel_task(self, task_uuid): self.cancelled.add(task_uuid) def update_status(self, task_uuid, status, **kwargs): self.status_updates.append((task_uuid, status, kwargs)) def create_log_callback(self, task_uuid, prefix="", batch_id=""): def callback(message): full_message = f"{prefix} {message}" if prefix else message self.logs.setdefault(task_uuid, []).append(full_message) return callback def create_check_cancelled_callback(self, task_uuid): return lambda: self.is_cancelled(task_uuid) def get_batch_status(self, batch_id): snapshot = self.batch_status.get(batch_id) return dict(snapshot) if snapshot else None def cancel_batch(self, batch_id): snapshot = self.batch_status.setdefault(batch_id, {}) snapshot["cancelled"] = True snapshot["status"] = "cancelling" def update_batch_status(self, batch_id, **kwargs): snapshot = self.batch_status.setdefault(batch_id, {}) snapshot.update(kwargs) class DummySettings: proxy_dynamic_enabled = False proxy_dynamic_api_url = "" email_code_timeout = 10 email_code_poll_interval = 1 email_code_resend_max_retries = 0 email_code_non_openai_sender_resend_max_retries = 0 openai_client_id = "client-id" openai_auth_url = "https://auth.example.test" openai_token_url = "https://token.example.test" openai_redirect_uri = "https://callback.example.test" openai_scope = "openid profile email" def get_proxy_url(self): return None def _build_fake_get_db(manager): @contextmanager def fake_get_db(): with manager.session_scope() as session: yield session return fake_get_db class FakeRegistrationEngine: started_event = None def __init__(self, email_service, proxy_url=None, callback_logger=None, status_callback=None, task_uuid=None): self.email_service = email_service self.phase_history = [] self.check_cancelled = None self.callback_logger = callback_logger or (lambda _msg: None) self.task_uuid = task_uuid def run(self): if self.started_event is not None: self.started_event.set() while True: if callable(self.check_cancelled) and self.check_cancelled(): return RegistrationResult( success=False, error_message="任务已取消", error_code=ERROR_TASK_CANCELLED, logs=[], ) time.sleep(0.01) def save_to_database(self, result): return False def close(self): return None class FakePollingEmailService(BaseEmailService): def __init__(self, started_event=None): super().__init__(EmailServiceType.TEMPMAIL, "fake-polling-email") self.started_event = started_event def create_email(self, config=None): return {"email": "poll@example.test", "service_id": "poll-service"} def get_verification_code(self, email: str, email_id: str = None, timeout: int = 120, pattern: str = r"(?