Download tests/test_registration_outlook_selection.py from webhook/codex: direct link, hf CLI and curl.
- Browser
- Download file 17.4 kB
-
https://huggingface.co/spaces/webhook/codex/resolve/main/tests/test_registration_outlook_selection.py
- Command line
-
hf download hf://spaces/webhook/codex/tests/test_registration_outlook_selection.py
-
curl -L -o test_registration_outlook_selection.py https://huggingface.co/spaces/webhook/codex/resolve/main/tests/test_registration_outlook_selection.py
17.4 kB
| from contextlib import contextmanager | |
| from pathlib import Path | |
| import threading | |
| import asyncio | |
| from types import SimpleNamespace | |
| from src.config.constants import EmailServiceType | |
| from src.core.register import RegistrationResult | |
| from src.database.models import Account, Base, EmailService, RegistrationTask | |
| from src.database.session import DatabaseSessionManager | |
| from src.web.routes import registration as registration_routes | |
| def _make_session_manager(name): | |
| runtime_dir = Path("tests_runtime") | |
| runtime_dir.mkdir(exist_ok=True) | |
| db_path = runtime_dir / name | |
| if db_path.exists(): | |
| db_path.unlink() | |
| manager = DatabaseSessionManager(f"sqlite:///{db_path}") | |
| Base.metadata.create_all(bind=manager.engine) | |
| return manager | |
| def _patched_get_db(manager): | |
| session = manager.SessionLocal() | |
| try: | |
| yield session | |
| finally: | |
| session.close() | |
| def _seed_outlook_services(session): | |
| first = EmailService( | |
| service_type="outlook", | |
| name="Outlook A", | |
| config={ | |
| "email": "reserved-a@example.com", | |
| "client_id": "client-a", | |
| "refresh_token": "refresh-a", | |
| }, | |
| enabled=True, | |
| priority=0, | |
| ) | |
| second = EmailService( | |
| service_type="outlook", | |
| name="Outlook B", | |
| config={ | |
| "email": "available-b@example.com", | |
| "client_id": "client-b", | |
| "refresh_token": "refresh-b", | |
| }, | |
| enabled=True, | |
| priority=1, | |
| ) | |
| session.add_all([first, second]) | |
| session.commit() | |
| session.refresh(first) | |
| session.refresh(second) | |
| return first, second | |
| def _seed_failed_outlook_placeholder(session, email, service_name="placeholder"): | |
| account = Account( | |
| email=email, | |
| password="", | |
| email_service="outlook", | |
| email_service_id=str(service_name), | |
| status="failed", | |
| source="register", | |
| extra_data={ | |
| "register_failed_reason": "username_rejected_or_existing_account", | |
| }, | |
| ) | |
| session.add(account) | |
| session.commit() | |
| session.refresh(account) | |
| return account | |
| def _seed_recoverable_outlook_account(session, email, service_name="placeholder"): | |
| account = Account( | |
| email=email, | |
| password="saved-password", | |
| email_service="outlook", | |
| email_service_id=str(service_name), | |
| status="failed", | |
| source="register", | |
| extra_data={ | |
| "register_failed_reason": "token_recovery_pending", | |
| "recovery_ready": True, | |
| "account_created": True, | |
| "token_acquired": False, | |
| }, | |
| ) | |
| session.add(account) | |
| session.commit() | |
| session.refresh(account) | |
| return account | |
| def _patch_registration_selection(monkeypatch, selected): | |
| def fake_create(service_type, config, name=None): | |
| selected["service_type"] = service_type | |
| selected["config"] = dict(config) | |
| selected["name"] = name | |
| return SimpleNamespace(service_type=service_type, config=config, name=name) | |
| def fake_run(self): | |
| return RegistrationResult(success=False, error_message="stop after selection") | |
| monkeypatch.setattr(registration_routes.EmailServiceFactory, "create", fake_create) | |
| monkeypatch.setattr(registration_routes.RegistrationEngine, "run", fake_run) | |
| def test_outlook_execution_uses_task_bound_email_service_id_when_runtime_arg_is_none(monkeypatch): | |
| manager = _make_session_manager("outlook-selection-bound.db") | |
| selected = {} | |
| with manager.session_scope() as session: | |
| first, second = _seed_outlook_services(session) | |
| session.add( | |
| RegistrationTask( | |
| task_uuid="task-bound-email-service", | |
| status="pending", | |
| email_service_id=second.id, | |
| ) | |
| ) | |
| session.commit() | |
| def fake_get_db(): | |
| with _patched_get_db(manager) as session: | |
| yield session | |
| monkeypatch.setattr(registration_routes, "get_db", fake_get_db) | |
| _patch_registration_selection(monkeypatch, selected) | |
| registration_routes._run_sync_registration_task( | |
| task_uuid="task-bound-email-service", | |
| email_service_type="outlook", | |
| proxy=None, | |
| email_service_config=None, | |
| email_service_id=None, | |
| ) | |
| assert selected["service_type"] == EmailServiceType.OUTLOOK | |
| assert selected["config"]["email"] == "available-b@example.com" | |
| assert selected["config"]["client_id"] == "client-b" | |
| def test_outlook_auto_selection_skips_failed_placeholder_account(monkeypatch): | |
| manager = _make_session_manager("outlook-selection-failed-placeholder.db") | |
| selected = {} | |
| with manager.session_scope() as session: | |
| first, second = _seed_outlook_services(session) | |
| _seed_failed_outlook_placeholder(session, "reserved-a@example.com") | |
| session.add( | |
| RegistrationTask( | |
| task_uuid="task-skip-failed-placeholder", | |
| status="pending", | |
| ) | |
| ) | |
| session.commit() | |
| def fake_get_db(): | |
| with _patched_get_db(manager) as session: | |
| yield session | |
| monkeypatch.setattr(registration_routes, "get_db", fake_get_db) | |
| _patch_registration_selection(monkeypatch, selected) | |
| registration_routes._run_sync_registration_task( | |
| task_uuid="task-skip-failed-placeholder", | |
| email_service_type="outlook", | |
| proxy=None, | |
| email_service_config=None, | |
| email_service_id=None, | |
| ) | |
| assert selected["service_type"] == EmailServiceType.OUTLOOK | |
| assert selected["config"]["email"] == "available-b@example.com" | |
| assert selected["config"]["client_id"] == "client-b" | |
| def test_outlook_auto_selection_reports_consumed_accounts_with_clear_error(monkeypatch): | |
| manager = _make_session_manager("outlook-selection-all-consumed.db") | |
| with manager.session_scope() as session: | |
| first, second = _seed_outlook_services(session) | |
| _seed_failed_outlook_placeholder(session, "reserved-a@example.com") | |
| _seed_failed_outlook_placeholder(session, "available-b@example.com") | |
| session.add( | |
| RegistrationTask( | |
| task_uuid="task-all-consumed", | |
| status="pending", | |
| ) | |
| ) | |
| session.commit() | |
| def fake_get_db(): | |
| with _patched_get_db(manager) as session: | |
| yield session | |
| monkeypatch.setattr(registration_routes, "get_db", fake_get_db) | |
| registration_routes._run_sync_registration_task( | |
| task_uuid="task-all-consumed", | |
| email_service_type="outlook", | |
| proxy=None, | |
| email_service_config=None, | |
| email_service_id=None, | |
| ) | |
| with _patched_get_db(manager) as session: | |
| task = session.query(RegistrationTask).filter_by(task_uuid="task-all-consumed").first() | |
| assert task.status == "failed" | |
| assert "已消耗" in (task.error_message or "") | |
| assert "Outlook" in (task.error_message or "") | |
| def test_outlook_bound_consumed_service_is_rejected_before_engine_runs(monkeypatch): | |
| manager = _make_session_manager("outlook-selection-bound-consumed.db") | |
| selected = {} | |
| with manager.session_scope() as session: | |
| first, second = _seed_outlook_services(session) | |
| _seed_failed_outlook_placeholder(session, "available-b@example.com") | |
| session.add( | |
| RegistrationTask( | |
| task_uuid="task-bound-consumed", | |
| status="pending", | |
| email_service_id=second.id, | |
| ) | |
| ) | |
| session.commit() | |
| def fake_get_db(): | |
| with _patched_get_db(manager) as session: | |
| yield session | |
| monkeypatch.setattr(registration_routes, "get_db", fake_get_db) | |
| def fake_create(service_type, config, name=None): | |
| selected["service_type"] = service_type | |
| selected["config"] = dict(config) | |
| selected["name"] = name | |
| return SimpleNamespace(service_type=service_type, config=config, name=name) | |
| def fake_run(self): | |
| selected["engine_ran"] = True | |
| return RegistrationResult(success=False, error_message="should not be reached") | |
| monkeypatch.setattr(registration_routes.EmailServiceFactory, "create", fake_create) | |
| monkeypatch.setattr(registration_routes.RegistrationEngine, "run", fake_run) | |
| registration_routes._run_sync_registration_task( | |
| task_uuid="task-bound-consumed", | |
| email_service_type="outlook", | |
| proxy=None, | |
| email_service_config=None, | |
| email_service_id=None, | |
| ) | |
| with _patched_get_db(manager) as session: | |
| task = session.query(RegistrationTask).filter_by(task_uuid="task-bound-consumed").first() | |
| assert selected == {} | |
| assert task.status == "failed" | |
| assert "已消耗" in (task.error_message or "") | |
| assert "Outlook" in (task.error_message or "") | |
| def test_outlook_auto_selection_skips_reserved_pending_task_and_uses_next_available_service(monkeypatch): | |
| manager = _make_session_manager("outlook-selection-reserved.db") | |
| selected = {} | |
| with manager.session_scope() as session: | |
| first, second = _seed_outlook_services(session) | |
| session.add_all( | |
| [ | |
| RegistrationTask( | |
| task_uuid="reserved-task", | |
| status="running", | |
| email_service_id=first.id, | |
| ), | |
| RegistrationTask( | |
| task_uuid="selected-task", | |
| status="pending", | |
| ), | |
| ] | |
| ) | |
| session.commit() | |
| def fake_get_db(): | |
| with _patched_get_db(manager) as session: | |
| yield session | |
| monkeypatch.setattr(registration_routes, "get_db", fake_get_db) | |
| _patch_registration_selection(monkeypatch, selected) | |
| registration_routes._run_sync_registration_task( | |
| task_uuid="selected-task", | |
| email_service_type="outlook", | |
| proxy=None, | |
| email_service_config=None, | |
| email_service_id=None, | |
| ) | |
| assert selected["service_type"] == EmailServiceType.OUTLOOK | |
| assert selected["config"]["email"] == "available-b@example.com" | |
| assert selected["config"]["client_id"] == "client-b" | |
| def test_outlook_auto_selection_reserves_account_atomically_across_concurrent_tasks(monkeypatch): | |
| manager = _make_session_manager("outlook-selection-concurrent.db") | |
| selections = {} | |
| with manager.session_scope() as session: | |
| first, second = _seed_outlook_services(session) | |
| session.add_all( | |
| [ | |
| RegistrationTask(task_uuid="task-1", status="pending"), | |
| RegistrationTask(task_uuid="task-2", status="pending"), | |
| ] | |
| ) | |
| session.commit() | |
| def fake_get_db(): | |
| with _patched_get_db(manager) as session: | |
| yield session | |
| real_update = registration_routes.crud.update_registration_task | |
| task_1_reached_reservation = threading.Event() | |
| allow_task_1_reservation = threading.Event() | |
| def fake_update_registration_task(db, task_uuid, **kwargs): | |
| if "email_service_id" in kwargs and task_uuid == "task-1": | |
| task_1_reached_reservation.set() | |
| allow_task_1_reservation.wait(timeout=5) | |
| return real_update(db, task_uuid, **kwargs) | |
| def fake_create(service_type, config, name=None): | |
| selections[threading.current_thread().name] = dict(config) | |
| return SimpleNamespace(service_type=service_type, config=config, name=name) | |
| def fake_run(self): | |
| return RegistrationResult(success=False, error_message="stop after selection") | |
| monkeypatch.setattr(registration_routes, "get_db", fake_get_db) | |
| monkeypatch.setattr(registration_routes.crud, "update_registration_task", fake_update_registration_task) | |
| monkeypatch.setattr(registration_routes.EmailServiceFactory, "create", fake_create) | |
| monkeypatch.setattr(registration_routes.RegistrationEngine, "run", fake_run) | |
| errors = [] | |
| def run_task(task_uuid, release_after_entry=False): | |
| try: | |
| if release_after_entry: | |
| task_1_reached_reservation.wait(timeout=5) | |
| registration_routes._run_sync_registration_task( | |
| task_uuid=task_uuid, | |
| email_service_type="outlook", | |
| proxy=None, | |
| email_service_config=None, | |
| email_service_id=None, | |
| ) | |
| except Exception as exc: # pragma: no cover - test diagnostics | |
| errors.append(exc) | |
| finally: | |
| if release_after_entry: | |
| allow_task_1_reservation.set() | |
| thread_1 = threading.Thread(target=run_task, args=("task-1", False), name="task-1-thread") | |
| thread_2 = threading.Thread(target=run_task, args=("task-2", True), name="task-2-thread") | |
| thread_1.start() | |
| thread_2.start() | |
| thread_1.join(timeout=10) | |
| thread_2.join(timeout=10) | |
| assert not errors | |
| assert selections["task-1-thread"]["email"] == "reserved-a@example.com" | |
| assert selections["task-2-thread"]["email"] == "available-b@example.com" | |
| def test_outlook_auto_selection_keeps_recoverable_account_available(monkeypatch): | |
| manager = _make_session_manager("outlook-selection-recoverable.db") | |
| selected = {} | |
| with manager.session_scope() as session: | |
| _seed_outlook_services(session) | |
| _seed_recoverable_outlook_account(session, "reserved-a@example.com") | |
| session.add( | |
| RegistrationTask( | |
| task_uuid="task-recoverable-selection", | |
| status="pending", | |
| ) | |
| ) | |
| session.commit() | |
| def fake_get_db(): | |
| with _patched_get_db(manager) as session: | |
| yield session | |
| monkeypatch.setattr(registration_routes, "get_db", fake_get_db) | |
| _patch_registration_selection(monkeypatch, selected) | |
| registration_routes._run_sync_registration_task( | |
| task_uuid="task-recoverable-selection", | |
| email_service_type="outlook", | |
| proxy=None, | |
| email_service_config=None, | |
| email_service_id=None, | |
| ) | |
| assert selected["service_type"] == EmailServiceType.OUTLOOK | |
| assert selected["config"]["email"] == "reserved-a@example.com" | |
| def test_outlook_bound_recoverable_service_runs_instead_of_being_rejected(monkeypatch): | |
| manager = _make_session_manager("outlook-selection-bound-recoverable.db") | |
| selected = {} | |
| with manager.session_scope() as session: | |
| first, second = _seed_outlook_services(session) | |
| _seed_recoverable_outlook_account(session, "available-b@example.com") | |
| session.add( | |
| RegistrationTask( | |
| task_uuid="task-bound-recoverable", | |
| status="pending", | |
| email_service_id=second.id, | |
| ) | |
| ) | |
| session.commit() | |
| def fake_get_db(): | |
| with _patched_get_db(manager) as session: | |
| yield session | |
| monkeypatch.setattr(registration_routes, "get_db", fake_get_db) | |
| def fake_create(service_type, config, name=None): | |
| selected["service_type"] = service_type | |
| selected["config"] = dict(config) | |
| selected["name"] = name | |
| return SimpleNamespace(service_type=service_type, config=config, name=name) | |
| def fake_run(self): | |
| selected["engine_ran"] = True | |
| return RegistrationResult(success=False, error_message="recovery still failed") | |
| monkeypatch.setattr(registration_routes.EmailServiceFactory, "create", fake_create) | |
| monkeypatch.setattr(registration_routes.RegistrationEngine, "run", fake_run) | |
| registration_routes._run_sync_registration_task( | |
| task_uuid="task-bound-recoverable", | |
| email_service_type="outlook", | |
| proxy=None, | |
| email_service_config=None, | |
| email_service_id=None, | |
| ) | |
| with _patched_get_db(manager) as session: | |
| task = session.query(RegistrationTask).filter_by(task_uuid="task-bound-recoverable").first() | |
| assert selected["engine_ran"] is True | |
| assert selected["config"]["email"] == "available-b@example.com" | |
| assert task.status == "failed" | |
| assert task.error_message == "recovery still failed" | |
| def test_outlook_batch_skip_registered_keeps_recoverable_accounts(monkeypatch): | |
| manager = _make_session_manager("outlook-batch-recoverable.db") | |
| with manager.session_scope() as session: | |
| first, _second = _seed_outlook_services(session) | |
| first_id = first.id | |
| _seed_recoverable_outlook_account(session, "reserved-a@example.com") | |
| def fake_get_db(): | |
| with _patched_get_db(manager) as session: | |
| yield session | |
| monkeypatch.setattr(registration_routes, "get_db", fake_get_db) | |
| async def fake_batch_runner(*args, **kwargs): | |
| return None | |
| monkeypatch.setattr(registration_routes, "run_outlook_batch_registration", fake_batch_runner) | |
| async def invoke(): | |
| from fastapi import BackgroundTasks | |
| return await registration_routes.start_outlook_batch_registration( | |
| registration_routes.OutlookBatchRegistrationRequest( | |
| service_ids=[first_id], | |
| skip_registered=True, | |
| ), | |
| BackgroundTasks(), | |
| ) | |
| response = asyncio.run(invoke()) | |
| assert response.to_register == 1 | |
| assert response.service_ids == [first_id] | |