Download tests/test_registration_engine.py from webhook/codex: direct link, hf CLI and curl.
- Browser
- Download file 150 kB
-
https://huggingface.co/spaces/webhook/codex/resolve/main/tests/test_registration_engine.py
- Command line
-
hf download hf://spaces/webhook/codex/tests/test_registration_engine.py
-
curl -L -o test_registration_engine.py https://huggingface.co/spaces/webhook/codex/resolve/main/tests/test_registration_engine.py
150 kB
| import base64 | |
| import json | |
| import urllib.parse | |
| from contextlib import contextmanager | |
| from datetime import datetime | |
| from pathlib import Path | |
| from src.config.constants import EmailServiceType, OPENAI_API_ENDPOINTS, OPENAI_PAGE_TYPES | |
| from src.core.http_client import OpenAIHTTPClient | |
| from src.core.openai.oauth import OAuthStart | |
| from src.core.register import RegistrationEngine | |
| from src.services.base import BaseEmailService | |
| from src.services.outlook.base import EmailMessage | |
| from src.services.outlook.service import OutlookService | |
| class DummyResponse: | |
| def __init__(self, status_code=200, payload=None, text="", headers=None, on_return=None, url="", history=None): | |
| self.status_code = status_code | |
| self._payload = payload | |
| self.text = text | |
| self.headers = headers or {} | |
| self.on_return = on_return | |
| self.url = url | |
| self.history = history or [] | |
| def json(self): | |
| if self._payload is None: | |
| raise ValueError("no json payload") | |
| return self._payload | |
| class QueueSession: | |
| def __init__(self, steps): | |
| self.steps = list(steps) | |
| self.calls = [] | |
| self.cookies = {} | |
| def get(self, url, **kwargs): | |
| return self._request("GET", url, **kwargs) | |
| def post(self, url, **kwargs): | |
| return self._request("POST", url, **kwargs) | |
| def request(self, method, url, **kwargs): | |
| return self._request(method.upper(), url, **kwargs) | |
| def close(self): | |
| return None | |
| def _request(self, method, url, **kwargs): | |
| self.calls.append({ | |
| "method": method, | |
| "url": url, | |
| "kwargs": kwargs, | |
| }) | |
| if not self.steps: | |
| raise AssertionError(f"unexpected request: {method} {url}") | |
| expected_method, expected_url, response = self.steps.pop(0) | |
| assert method == expected_method | |
| assert url == expected_url | |
| if callable(response): | |
| response = response(self) | |
| if response.on_return: | |
| response.on_return(self) | |
| return response | |
| class FakeEmailService(BaseEmailService): | |
| def __init__(self, codes): | |
| super().__init__(EmailServiceType.TEMPMAIL) | |
| self.codes = list(codes) | |
| self.otp_requests = [] | |
| def create_email(self, config=None): | |
| return { | |
| "email": "tester@example.com", | |
| "service_id": "mailbox-1", | |
| } | |
| def get_verification_code(self, email, email_id=None, timeout=120, pattern=r"(?<!\d)(\d{6})(?!\d)", otp_sent_at=None): | |
| self.otp_requests.append({ | |
| "email": email, | |
| "email_id": email_id, | |
| "timeout": timeout, | |
| "otp_sent_at": otp_sent_at, | |
| }) | |
| if not self.codes: | |
| raise AssertionError("no verification code queued") | |
| return self.codes.pop(0) | |
| def list_emails(self, **kwargs): | |
| return [] | |
| def delete_email(self, email_id): | |
| return True | |
| def check_health(self): | |
| return True | |
| class FakeOutlookEmailService(FakeEmailService): | |
| def __init__(self, codes): | |
| super().__init__(codes) | |
| self.service_type = EmailServiceType.OUTLOOK | |
| self.current_stage = None | |
| self.verification_stages = [] | |
| def set_verification_stage(self, email, stage): | |
| self.current_stage = stage | |
| self.verification_stages.append({ | |
| "email": email, | |
| "stage": stage, | |
| }) | |
| def get_verification_code(self, email, email_id=None, timeout=120, pattern=r"(?<!\d)(\d{6})(?!\d)", otp_sent_at=None): | |
| self.otp_requests.append({ | |
| "email": email, | |
| "email_id": email_id, | |
| "timeout": timeout, | |
| "otp_sent_at": otp_sent_at, | |
| "stage": self.current_stage, | |
| }) | |
| if not self.codes: | |
| raise AssertionError("no verification code queued") | |
| return self.codes.pop(0) | |
| class FakeStageAwareTempMailService(FakeEmailService): | |
| def __init__(self, codes): | |
| super().__init__(codes) | |
| self.service_type = EmailServiceType.TEMP_MAIL | |
| self.verification_stages = [] | |
| def set_verification_stage(self, email, stage): | |
| self.verification_stages.append({ | |
| "email": email, | |
| "stage": stage, | |
| }) | |
| class FakeOAuthManager: | |
| def __init__(self): | |
| self.start_calls = 0 | |
| self.callback_calls = [] | |
| def start_oauth(self): | |
| self.start_calls += 1 | |
| return OAuthStart( | |
| auth_url=f"https://auth.example.test/flow/{self.start_calls}", | |
| state=f"state-{self.start_calls}", | |
| code_verifier=f"verifier-{self.start_calls}", | |
| redirect_uri="http://localhost:1455/auth/callback", | |
| ) | |
| def handle_callback(self, callback_url, expected_state, code_verifier): | |
| self.callback_calls.append({ | |
| "callback_url": callback_url, | |
| "expected_state": expected_state, | |
| "code_verifier": code_verifier, | |
| }) | |
| return { | |
| "account_id": "acct-1", | |
| "access_token": "access-1", | |
| "refresh_token": "refresh-1", | |
| "id_token": "id-1", | |
| } | |
| class FakeOAuthManagerWithWorkspace(FakeOAuthManager): | |
| def __init__(self, workspace_id="ws-token"): | |
| super().__init__() | |
| self.workspace_id = workspace_id | |
| def handle_callback(self, callback_url, expected_state, code_verifier): | |
| result = super().handle_callback(callback_url, expected_state, code_verifier) | |
| result["id_token"] = _jwt_like_token({ | |
| "sub": "acct-1", | |
| "workspaces": [{"id": self.workspace_id}], | |
| }) | |
| return result | |
| class FakeOpenAIClient: | |
| def __init__(self, sessions, sentinel_tokens): | |
| self._sessions = list(sessions) | |
| self._session_index = 0 | |
| self._session = self._sessions[0] | |
| self._sentinel_tokens = list(sentinel_tokens) | |
| def session(self): | |
| return self._session | |
| def check_ip_location(self): | |
| return True, "US" | |
| def check_sentinel(self, did): | |
| if not self._sentinel_tokens: | |
| raise AssertionError("no sentinel token queued") | |
| return self._sentinel_tokens.pop(0) | |
| def close(self): | |
| if self._session_index + 1 < len(self._sessions): | |
| self._session_index += 1 | |
| self._session = self._sessions[self._session_index] | |
| def _workspace_cookie(workspace_id): | |
| header = base64.urlsafe_b64encode( | |
| json.dumps({"alg": "HS256", "typ": "JWT"}).encode("utf-8") | |
| ).decode("ascii").rstrip("=") | |
| payload = base64.urlsafe_b64encode( | |
| json.dumps({"workspaces": [{"id": workspace_id}]}).encode("utf-8") | |
| ).decode("ascii").rstrip("=") | |
| return f"{header}.{payload}.sig" | |
| def _workspace_cookie_without_workspace(): | |
| header = base64.urlsafe_b64encode( | |
| json.dumps({"alg": "HS256", "typ": "JWT"}).encode("utf-8") | |
| ).decode("ascii").rstrip("=") | |
| payload = base64.urlsafe_b64encode( | |
| json.dumps({"sub": "session-only"}).encode("utf-8") | |
| ).decode("ascii").rstrip("=") | |
| return f"{header}.{payload}.sig" | |
| def _quoted_first_segment_workspace_cookie(workspace_id): | |
| first_segment = base64.urlsafe_b64encode( | |
| json.dumps({"workspaces": [{"id": workspace_id}]}).encode("utf-8") | |
| ).decode("ascii").rstrip("=") | |
| return urllib.parse.quote(f'"{first_segment}.sig"') | |
| def _jwt_like_token(payload): | |
| header = base64.urlsafe_b64encode( | |
| json.dumps({"alg": "HS256", "typ": "JWT"}).encode("utf-8") | |
| ).decode("ascii").rstrip("=") | |
| payload_segment = base64.urlsafe_b64encode( | |
| json.dumps(payload).encode("utf-8") | |
| ).decode("ascii").rstrip("=") | |
| return f"{header}.{payload_segment}.sig" | |
| def _next_auth_session_token(workspace_id): | |
| return _jwt_like_token({"workspaces": [{"id": workspace_id}]}) | |
| def _response_with_did(did): | |
| return DummyResponse( | |
| status_code=200, | |
| text="ok", | |
| on_return=lambda session: session.cookies.__setitem__("oai-did", did), | |
| ) | |
| def _response_with_login_cookies(workspace_id="ws-1", session_token="session-1"): | |
| def setter(session): | |
| session.cookies["oai-client-auth-session"] = _workspace_cookie(workspace_id) | |
| session.cookies["__Secure-next-auth.session-token"] = session_token | |
| return DummyResponse(status_code=200, payload={}, on_return=setter) | |
| def _response_with_login_cookies_without_workspace(session_token="session-1"): | |
| def setter(session): | |
| session.cookies["oai-client-auth-session"] = _workspace_cookie_without_workspace() | |
| session.cookies["__Secure-next-auth.session-token"] = session_token | |
| return DummyResponse(status_code=200, payload={}, on_return=setter) | |
| def _response_with_payload_workspace_only(workspace_id="ws-payload", session_token="session-1"): | |
| def setter(session): | |
| session.cookies["oai-client-auth-session"] = _workspace_cookie_without_workspace() | |
| session.cookies["__Secure-next-auth.session-token"] = session_token | |
| return DummyResponse( | |
| status_code=200, | |
| payload={"session": {"workspaces": [{"id": workspace_id}]}}, | |
| on_return=setter, | |
| ) | |
| def _response_with_callback_before_workspace( | |
| callback_url="http://localhost:1455/auth/callback?code=code-2&state=state-2", | |
| session_token="session-1", | |
| ): | |
| def setter(session): | |
| session.cookies["oai-client-auth-session"] = _workspace_cookie_without_workspace() | |
| session.cookies["__Secure-next-auth.session-token"] = session_token | |
| return DummyResponse( | |
| status_code=200, | |
| payload={"callback_url": callback_url}, | |
| on_return=setter, | |
| ) | |
| def _response_with_resume_history( | |
| resume_url="https://auth.example.test/login-challenge?login_challenge=challenge-1", | |
| session_token="session-1", | |
| ): | |
| def setter(session): | |
| session.cookies["oai-client-auth-session"] = _workspace_cookie_without_workspace() | |
| session.cookies["__Secure-next-auth.session-token"] = session_token | |
| history = [ | |
| DummyResponse( | |
| status_code=302, | |
| headers={"Location": resume_url}, | |
| url=OPENAI_API_ENDPOINTS["validate_otp"], | |
| ) | |
| ] | |
| return DummyResponse( | |
| status_code=200, | |
| payload={}, | |
| url=resume_url, | |
| history=history, | |
| on_return=setter, | |
| ) | |
| def _response_with_email_verification_resume_text( | |
| resume_url="https://auth.example.test/api/oauth/oauth2/auth?client_id=client-1&state=resume-1", | |
| session_token="session-1", | |
| ): | |
| def setter(session): | |
| session.cookies["oai-client-auth-session"] = _workspace_cookie_without_workspace() | |
| session.cookies["__Secure-next-auth.session-token"] = session_token | |
| text = f'<html><body><script>window.__STATE__={{"continue_url":"{resume_url}"}};</script></body></html>' | |
| return DummyResponse( | |
| status_code=200, | |
| payload={}, | |
| text=text, | |
| url="https://auth.example.test/email-verification", | |
| on_return=setter, | |
| ) | |
| def _response_with_about_you_next_data_callback( | |
| callback_url="http://localhost:1455/auth/callback?code=code-next-data&state=state-1", | |
| ): | |
| encoded_callback = callback_url.replace(":", "\\u003A").replace("/", "\\u002F") | |
| text = ( | |
| '<html><body><script id="__NEXT_DATA__" type="application/json">' | |
| '{"props":{"pageProps":{"callback_url":"' | |
| + encoded_callback | |
| + '"}}}</script></body></html>' | |
| ) | |
| return DummyResponse( | |
| status_code=200, | |
| text=text, | |
| url="https://auth.example.test/about-you", | |
| ) | |
| def _response_with_about_you_next_data_resume( | |
| resume_url="https://auth.example.test/api/oauth/oauth2/auth?client_id=client-1&state=resume-next", | |
| ): | |
| encoded_resume = resume_url.replace(":", "\\u003A").replace("/", "\\u002F") | |
| text = ( | |
| '<html><body><script id="__NEXT_DATA__" type="application/json">' | |
| '{"props":{"pageProps":{"continue_url":"' | |
| + encoded_resume | |
| + '"}}}</script></body></html>' | |
| ) | |
| return DummyResponse( | |
| status_code=200, | |
| text=text, | |
| url="https://auth.example.test/about-you", | |
| ) | |
| def _response_with_consent_next_data_workspace( | |
| workspace_id="ws-consent-next-data", | |
| session_token="session-consent-next-data", | |
| ): | |
| payload = { | |
| "props": { | |
| "pageProps": { | |
| "session": { | |
| "workspaces": [{"id": workspace_id}], | |
| }, | |
| "page": {"type": "sign_in_with_chatgpt_codex_consent"}, | |
| } | |
| } | |
| } | |
| def setter(session): | |
| session.cookies["oai-client-auth-session"] = _workspace_cookie_without_workspace() | |
| session.cookies["__Secure-next-auth.session-token"] = session_token | |
| text = ( | |
| '<html><body><script id="__NEXT_DATA__" type="application/json">' | |
| + json.dumps(payload) | |
| + "</script></body></html>" | |
| ) | |
| return DummyResponse( | |
| status_code=200, | |
| text=text, | |
| url="https://auth.openai.com/sign-in-with-chatgpt/codex/consent", | |
| on_return=setter, | |
| ) | |
| def _response_with_consent_app_router_workspace( | |
| workspace_id="ws-consent-app-router", | |
| session_token="session-consent-app-router", | |
| ): | |
| payload_fragment = json.dumps( | |
| { | |
| "session": { | |
| "workspaces": [{"id": workspace_id}], | |
| }, | |
| "page": {"type": "sign_in_with_chatgpt_codex_consent"}, | |
| } | |
| ) | |
| def setter(session): | |
| session.cookies["oai-client-auth-session"] = _workspace_cookie_without_workspace() | |
| session.cookies["__Secure-next-auth.session-token"] = session_token | |
| text = ( | |
| "<html><body><script>" | |
| "(self.__next_f=self.__next_f||[]).push(" | |
| + json.dumps([1, payload_fragment]) | |
| + ");</script></body></html>" | |
| ) | |
| return DummyResponse( | |
| status_code=200, | |
| text=text, | |
| url="https://auth.openai.com/sign-in-with-chatgpt/codex/consent", | |
| on_return=setter, | |
| ) | |
| def _manifest_cookie(payload): | |
| encoded = base64.urlsafe_b64encode( | |
| json.dumps(payload).encode("utf-8") | |
| ).decode("ascii").rstrip("=") | |
| return urllib.parse.quote(encoded) | |
| def _response_with_username_rejected(): | |
| payload = { | |
| "error": { | |
| "message": "Failed to register username. Please try again.", | |
| "code": "bad_request", | |
| } | |
| } | |
| return DummyResponse(status_code=400, payload=payload, text=json.dumps(payload)) | |
| def _fake_get_db_context(): | |
| yield object() | |
| def _make_engine_session_manager(name): | |
| runtime_dir = Path("tests_runtime") | |
| runtime_dir.mkdir(exist_ok=True) | |
| db_path = runtime_dir / name | |
| if db_path.exists(): | |
| try: | |
| db_path.unlink() | |
| except PermissionError: | |
| db_path = runtime_dir / f"{db_path.stem}-{datetime.now().timestamp():.0f}{db_path.suffix}" | |
| from src.database.models import Base | |
| from src.database.session import DatabaseSessionManager | |
| manager = DatabaseSessionManager(f"sqlite:///{db_path}") | |
| Base.metadata.create_all(bind=manager.engine) | |
| return manager | |
| def _build_outlook_username_rejection_engine(monkeypatch): | |
| created_accounts = [] | |
| monkeypatch.setattr("src.core.register.get_db", _fake_get_db_context) | |
| monkeypatch.setattr("src.core.register.crud.get_account_by_email", lambda db, email: None) | |
| monkeypatch.setattr( | |
| "src.core.register.crud.create_account", | |
| lambda db, **kwargs: created_accounts.append(kwargs), | |
| ) | |
| email_service = FakeOutlookEmailService([]) | |
| email_service.create_email = lambda config=None: { | |
| "email": "StephanieChavez3037@outlook.com", | |
| "service_id": "mailbox-1", | |
| } | |
| engine = RegistrationEngine(email_service) | |
| engine.oauth_manager = FakeOAuthManager() | |
| return engine, created_accounts | |
| def test_check_sentinel_sends_non_empty_pow(monkeypatch): | |
| session = QueueSession([ | |
| ("POST", OPENAI_API_ENDPOINTS["sentinel"], DummyResponse(payload={"token": "sentinel-token"})), | |
| ]) | |
| client = OpenAIHTTPClient() | |
| client._session = session | |
| monkeypatch.setattr( | |
| "src.core.http_client.build_sentinel_pow_token", | |
| lambda user_agent: "gAAAAACpow-token", | |
| ) | |
| token = client.check_sentinel("device-1") | |
| assert token == "sentinel-token" | |
| body = json.loads(session.calls[0]["kwargs"]["data"]) | |
| assert body["id"] == "device-1" | |
| assert body["flow"] == "authorize_continue" | |
| assert body["p"] == "gAAAAACpow-token" | |
| def test_run_registers_then_relogs_to_fetch_token(): | |
| session_one = QueueSession([ | |
| ("GET", "https://auth.example.test/flow/1", _response_with_did("did-1")), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["signup"], | |
| DummyResponse(payload={"page": {"type": OPENAI_PAGE_TYPES["PASSWORD_REGISTRATION"]}}), | |
| ), | |
| ("POST", OPENAI_API_ENDPOINTS["register"], DummyResponse(payload={})), | |
| ("GET", OPENAI_API_ENDPOINTS["send_otp"], DummyResponse(payload={})), | |
| ("POST", OPENAI_API_ENDPOINTS["validate_otp"], DummyResponse(payload={})), | |
| ("POST", OPENAI_API_ENDPOINTS["create_account"], DummyResponse(payload={})), | |
| ]) | |
| session_two = QueueSession([ | |
| ("GET", "https://auth.example.test/flow/2", _response_with_did("did-2")), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["signup"], | |
| DummyResponse(payload={"page": {"type": OPENAI_PAGE_TYPES["LOGIN_PASSWORD"]}}), | |
| ), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["password_verify"], | |
| DummyResponse(payload={"page": {"type": OPENAI_PAGE_TYPES["EMAIL_OTP_VERIFICATION"]}}), | |
| ), | |
| ("POST", OPENAI_API_ENDPOINTS["validate_otp"], _response_with_login_cookies()), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["select_workspace"], | |
| DummyResponse(payload={"continue_url": "https://auth.example.test/continue"}), | |
| ), | |
| ( | |
| "GET", | |
| "https://auth.example.test/continue", | |
| DummyResponse( | |
| status_code=302, | |
| headers={"Location": "http://localhost:1455/auth/callback?code=code-2&state=state-2"}, | |
| ), | |
| ), | |
| ]) | |
| email_service = FakeEmailService(["123456", "654321"]) | |
| engine = RegistrationEngine(email_service) | |
| fake_oauth = FakeOAuthManager() | |
| engine.http_client = FakeOpenAIClient([session_one, session_two], ["sentinel-1", "sentinel-2"]) | |
| engine.oauth_manager = fake_oauth | |
| result = engine.run() | |
| assert result.success is True | |
| assert result.source == "register" | |
| assert result.workspace_id == "ws-1" | |
| assert result.session_token == "session-1" | |
| assert fake_oauth.start_calls == 2 | |
| assert len(email_service.otp_requests) == 2 | |
| assert all(item["otp_sent_at"] is not None for item in email_service.otp_requests) | |
| assert sum(1 for call in session_one.calls if call["url"] == OPENAI_API_ENDPOINTS["send_otp"]) == 1 | |
| assert sum(1 for call in session_two.calls if call["url"] == OPENAI_API_ENDPOINTS["send_otp"]) == 0 | |
| assert sum(1 for call in session_one.calls if call["url"] == OPENAI_API_ENDPOINTS["select_workspace"]) == 0 | |
| assert sum(1 for call in session_two.calls if call["url"] == OPENAI_API_ENDPOINTS["select_workspace"]) == 1 | |
| relogin_start_body = json.loads(session_two.calls[1]["kwargs"]["data"]) | |
| assert relogin_start_body["screen_hint"] == "login" | |
| assert relogin_start_body["username"]["value"] == "tester@example.com" | |
| password_verify_body = json.loads(session_two.calls[2]["kwargs"]["data"]) | |
| assert password_verify_body == {"password": result.password} | |
| assert result.metadata["token_acquired_via_relogin"] is True | |
| def test_run_prefers_current_session_after_create_account_add_phone_consent_before_relogin(): | |
| def create_account_add_phone(session): | |
| session.cookies["oai-client-auth-session"] = _workspace_cookie_without_workspace() | |
| session.cookies["__Secure-next-auth.session-token"] = "session-add-phone" | |
| return DummyResponse( | |
| payload={ | |
| "page": {"type": "add_phone"}, | |
| "continue_url": "https://auth.example.test/add-phone", | |
| }, | |
| url=OPENAI_API_ENDPOINTS["create_account"], | |
| ) | |
| def consent_page_with_workspace(session): | |
| session.cookies["oai-client-auth-session"] = _workspace_cookie("ws-add-phone") | |
| session.cookies["__Secure-next-auth.session-token"] = "session-add-phone" | |
| return DummyResponse( | |
| status_code=200, | |
| url="https://auth.openai.com/sign-in-with-chatgpt/codex/consent", | |
| text="<html><body>consent</body></html>", | |
| ) | |
| session = QueueSession([ | |
| ("GET", "https://auth.example.test/flow/1", _response_with_did("did-1")), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["signup"], | |
| DummyResponse(payload={"page": {"type": OPENAI_PAGE_TYPES["PASSWORD_REGISTRATION"]}}), | |
| ), | |
| ("POST", OPENAI_API_ENDPOINTS["register"], DummyResponse(payload={})), | |
| ("GET", OPENAI_API_ENDPOINTS["send_otp"], DummyResponse(payload={})), | |
| ("POST", OPENAI_API_ENDPOINTS["validate_otp"], DummyResponse(payload={})), | |
| ("POST", OPENAI_API_ENDPOINTS["create_account"], create_account_add_phone), | |
| ( | |
| "GET", | |
| "https://auth.example.test/add-phone", | |
| DummyResponse( | |
| status_code=200, | |
| url="https://auth.example.test/add-phone", | |
| text=( | |
| "<html><body><script>" | |
| 'window.__STATE__={"continue_url":"https://auth.openai.com/sign-in-with-chatgpt/codex/consent"};' | |
| "</script></body></html>" | |
| ), | |
| ), | |
| ), | |
| ("GET", "https://auth.openai.com/sign-in-with-chatgpt/codex/consent", consent_page_with_workspace), | |
| ("GET", "https://auth.openai.com/sign-in-with-chatgpt/codex/consent", consent_page_with_workspace), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["select_workspace"], | |
| DummyResponse(payload={"continue_url": "https://auth.example.test/consent-continue"}), | |
| ), | |
| ( | |
| "GET", | |
| "https://auth.example.test/consent-continue", | |
| DummyResponse( | |
| status_code=302, | |
| headers={"Location": "http://localhost:1455/auth/callback?code=code-add-phone&state=state-1"}, | |
| ), | |
| ), | |
| ]) | |
| email_service = FakeEmailService(["123456"]) | |
| engine = RegistrationEngine(email_service) | |
| fake_oauth = FakeOAuthManagerWithWorkspace("ws-add-phone") | |
| engine.http_client = FakeOpenAIClient([session], ["sentinel-1"]) | |
| engine.oauth_manager = fake_oauth | |
| result = engine.run() | |
| assert result.success is True | |
| assert result.workspace_id == "ws-add-phone" | |
| assert result.session_token == "session-add-phone" | |
| assert fake_oauth.start_calls == 1 | |
| assert len(email_service.otp_requests) == 1 | |
| assert any(call["url"] == "https://auth.example.test/add-phone" for call in session.calls) | |
| assert any(call["url"] == "https://auth.openai.com/sign-in-with-chatgpt/codex/consent" for call in session.calls) | |
| assert sum(1 for call in session.calls if call["url"] == OPENAI_API_ENDPOINTS["select_workspace"]) == 1 | |
| assert result.metadata["token_acquired_via_relogin"] is False | |
| def test_get_workspace_id_reads_workspace_from_cookie_payload_segment(): | |
| engine = RegistrationEngine(FakeEmailService([])) | |
| engine.session = QueueSession([]) | |
| engine.session.cookies["oai-client-auth-session"] = _workspace_cookie("ws-cookie") | |
| workspace_id = engine._get_workspace_id() | |
| assert workspace_id == "ws-cookie" | |
| def test_get_workspace_id_returns_none_when_cookie_has_no_workspace(): | |
| engine = RegistrationEngine(FakeEmailService([])) | |
| engine.session = QueueSession([]) | |
| engine.session.cookies["oai-client-auth-session"] = _workspace_cookie_without_workspace() | |
| workspace_id = engine._get_workspace_id() | |
| assert workspace_id is None | |
| assert any("workspace" in log.lower() for log in engine.logs) | |
| def test_get_workspace_id_reads_workspace_from_quoted_first_segment_cookie(): | |
| engine = RegistrationEngine(FakeEmailService([])) | |
| engine.session = QueueSession([]) | |
| engine.session.cookies["oai-client-auth-session"] = _quoted_first_segment_workspace_cookie("ws-quoted") | |
| workspace_id = engine._get_workspace_id() | |
| assert workspace_id == "ws-quoted" | |
| def test_get_workspace_id_reads_workspace_from_next_auth_session_token(): | |
| engine = RegistrationEngine(FakeEmailService([])) | |
| engine.session = QueueSession([]) | |
| engine.session.cookies["oai-client-auth-session"] = _workspace_cookie_without_workspace() | |
| engine.session.cookies["__Secure-next-auth.session-token"] = _next_auth_session_token("ws-next-auth") | |
| workspace_id = engine._get_workspace_id() | |
| assert workspace_id == "ws-next-auth" | |
| def test_get_workspace_id_reads_workspace_from_unified_session_manifest_cookie(): | |
| engine = RegistrationEngine(FakeEmailService([])) | |
| engine.session = QueueSession([]) | |
| engine.session.cookies["oai-client-auth-session"] = _workspace_cookie_without_workspace() | |
| engine.session.cookies["unified_session_manifest"] = _manifest_cookie( | |
| {"workspaces": [{"id": "ws-manifest"}]} | |
| ) | |
| workspace_id = engine._get_workspace_id() | |
| assert workspace_id == "ws-manifest" | |
| def test_select_workspace_includes_oauth_context_headers(): | |
| engine = RegistrationEngine(FakeEmailService([])) | |
| engine.session = QueueSession([ | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["select_workspace"], | |
| DummyResponse(payload={"continue_url": "https://auth.example.test/next"}), | |
| ), | |
| ]) | |
| engine.session.headers = {"User-Agent": "UA-test/1.0"} | |
| engine.session.cookies["oai-did"] = "did-test-1" | |
| continue_url = engine._select_workspace("ws-header-check") | |
| assert continue_url == "https://auth.example.test/next" | |
| headers = engine.session.calls[0]["kwargs"]["headers"] | |
| assert headers["accept"] == "application/json" | |
| assert headers["content-type"] == "application/json" | |
| assert headers["origin"] == "https://auth.openai.com" | |
| assert headers["referer"] == "https://auth.openai.com/sign-in-with-chatgpt/codex/consent" | |
| assert headers["user-agent"] == "UA-test/1.0" | |
| assert headers["oai-device-id"] == "did-test-1" | |
| assert "traceparent" in headers | |
| assert "x-datadog-trace-id" in headers | |
| def test_select_organization_includes_oauth_context_headers(): | |
| organization_select_url = "https://auth.openai.com/api/accounts/organization/select" | |
| engine = RegistrationEngine(FakeEmailService([])) | |
| engine.session = QueueSession([ | |
| ( | |
| "POST", | |
| organization_select_url, | |
| DummyResponse(payload={"continue_url": "https://auth.example.test/org-next"}), | |
| ), | |
| ]) | |
| engine.session.headers = {"User-Agent": "UA-org/2.0"} | |
| engine.session.cookies["oai-did"] = "did-org-1" | |
| continue_url = engine._select_organization( | |
| "org-1", | |
| "proj-1", | |
| referer="https://auth.example.test/organization-step", | |
| ) | |
| assert continue_url == "https://auth.example.test/org-next" | |
| headers = engine.session.calls[0]["kwargs"]["headers"] | |
| assert headers["accept"] == "application/json" | |
| assert headers["content-type"] == "application/json" | |
| assert headers["origin"] == "https://auth.openai.com" | |
| assert headers["referer"] == "https://auth.example.test/organization-step" | |
| assert headers["user-agent"] == "UA-org/2.0" | |
| assert headers["oai-device-id"] == "did-org-1" | |
| assert "traceparent" in headers | |
| assert "x-datadog-parent-id" in headers | |
| def test_remember_navigation_from_consent_logs_script_assets_and_bundle_hints(): | |
| route_script_url = "https://auth.openai.com/assets/route-D83ftS1Y.js" | |
| main_script_url = "https://auth.openai.com/assets/main.js" | |
| engine = RegistrationEngine(FakeEmailService([])) | |
| engine.session = QueueSession([ | |
| ( | |
| "GET", | |
| route_script_url, | |
| DummyResponse( | |
| status_code=200, | |
| url=route_script_url, | |
| text='const clientAction="/api/accounts/workspace/select"; const org="/api/accounts/organization/select";', | |
| ), | |
| ), | |
| ( | |
| "GET", | |
| main_script_url, | |
| DummyResponse( | |
| status_code=200, | |
| url=main_script_url, | |
| text="console.log('noop')", | |
| ), | |
| ), | |
| ]) | |
| engine.session.headers = {"User-Agent": "UA-assets/1.0"} | |
| response = DummyResponse( | |
| status_code=200, | |
| url="https://auth.openai.com/sign-in-with-chatgpt/codex/consent", | |
| text=( | |
| "<html><head>" | |
| '<script src="/assets/route-D83ftS1Y.js"></script>' | |
| '<script src="/assets/main.js"></script>' | |
| "</head><body>consent</body></html>" | |
| ), | |
| ) | |
| engine._remember_navigation_from_response("redirect_1", response) | |
| assert any("consent_script_assets=2" in log for log in engine.logs) | |
| assert any("route-D83ftS1Y.js" in log for log in engine.logs) | |
| assert any("workspace/select" in log for log in engine.logs) | |
| assert any("organization/select" in log for log in engine.logs) | |
| def test_resolve_oauth_callback_runs_organization_select_when_workspace_select_requires_it(): | |
| organization_select_url = "https://auth.openai.com/api/accounts/organization/select" | |
| engine = RegistrationEngine(FakeEmailService([])) | |
| engine.session = QueueSession([ | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["select_workspace"], | |
| DummyResponse( | |
| payload={ | |
| "continue_url": "https://auth.example.test/organization", | |
| "page": {"type": "organization_selection"}, | |
| "data": { | |
| "orgs": [ | |
| { | |
| "id": "org-1", | |
| "projects": [{"id": "proj-1"}], | |
| } | |
| ] | |
| }, | |
| } | |
| ), | |
| ), | |
| ( | |
| "POST", | |
| organization_select_url, | |
| DummyResponse(payload={"continue_url": "https://auth.example.test/org-continue"}), | |
| ), | |
| ( | |
| "GET", | |
| "https://auth.example.test/org-continue", | |
| DummyResponse( | |
| status_code=302, | |
| headers={"Location": "http://localhost:1455/auth/callback?code=code-org&state=state-1"}, | |
| ), | |
| ), | |
| ]) | |
| engine.session.cookies["oai-client-auth-session"] = _workspace_cookie("ws-org") | |
| callback_url, workspace_id, resolution_error = engine._resolve_oauth_callback_url() | |
| assert callback_url == "http://localhost:1455/auth/callback?code=code-org&state=state-1" | |
| assert workspace_id == "ws-org" | |
| assert resolution_error is None | |
| assert any(call["url"] == organization_select_url for call in engine.session.calls) | |
| def test_register_password_marks_username_rejection_as_consumed(monkeypatch): | |
| engine, created_accounts = _build_outlook_username_rejection_engine(monkeypatch) | |
| engine.email = "StephanieChavez3037@outlook.com" | |
| engine.email_info = {"service_id": "mailbox-1"} | |
| engine.session = QueueSession([ | |
| ("POST", OPENAI_API_ENDPOINTS["register"], _response_with_username_rejected()), | |
| ]) | |
| ok, password = engine._register_password() | |
| assert ok is False | |
| assert password is None | |
| assert len(created_accounts) == 1 | |
| assert created_accounts[0]["email"] == "StephanieChavez3037@outlook.com" | |
| assert created_accounts[0]["status"] == "failed" | |
| def test_run_reports_specific_error_for_username_rejected_outlook(monkeypatch): | |
| engine, _created_accounts = _build_outlook_username_rejection_engine(monkeypatch) | |
| engine.http_client = FakeOpenAIClient([ | |
| QueueSession([ | |
| ("GET", "https://auth.example.test/flow/1", _response_with_did("did-1")), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["signup"], | |
| DummyResponse(payload={"page": {"type": OPENAI_PAGE_TYPES["PASSWORD_REGISTRATION"]}}), | |
| ), | |
| ("POST", OPENAI_API_ENDPOINTS["register"], _response_with_username_rejected()), | |
| ]) | |
| ], ["sentinel-1"]) | |
| result = engine.run() | |
| assert result.success is False | |
| assert "注册密码失败" not in result.error_message | |
| assert any(token in result.error_message for token in ("已被占用", "疑似已注册", "已注册")) | |
| def test_existing_account_login_uses_auto_sent_otp_without_manual_send(): | |
| session = QueueSession([ | |
| ("GET", "https://auth.example.test/flow/1", _response_with_did("did-1")), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["signup"], | |
| DummyResponse(payload={"page": {"type": OPENAI_PAGE_TYPES["EMAIL_OTP_VERIFICATION"]}}), | |
| ), | |
| ("POST", OPENAI_API_ENDPOINTS["validate_otp"], _response_with_login_cookies("ws-existing", "session-existing")), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["select_workspace"], | |
| DummyResponse(payload={"continue_url": "https://auth.example.test/continue-existing"}), | |
| ), | |
| ( | |
| "GET", | |
| "https://auth.example.test/continue-existing", | |
| DummyResponse( | |
| status_code=302, | |
| headers={"Location": "http://localhost:1455/auth/callback?code=code-1&state=state-1"}, | |
| ), | |
| ), | |
| ]) | |
| email_service = FakeEmailService(["246810"]) | |
| engine = RegistrationEngine(email_service) | |
| fake_oauth = FakeOAuthManager() | |
| engine.http_client = FakeOpenAIClient([session], ["sentinel-1"]) | |
| engine.oauth_manager = fake_oauth | |
| result = engine.run() | |
| assert result.success is True | |
| assert result.source == "login" | |
| assert fake_oauth.start_calls == 1 | |
| assert sum(1 for call in session.calls if call["url"] == OPENAI_API_ENDPOINTS["send_otp"]) == 0 | |
| assert len(email_service.otp_requests) == 1 | |
| assert email_service.otp_requests[0]["otp_sent_at"] is not None | |
| assert result.metadata["token_acquired_via_relogin"] is False | |
| def test_run_registers_then_relogs_outlook_with_manual_resend_and_longer_timeout(): | |
| session_one = QueueSession([ | |
| ("GET", "https://auth.example.test/flow/1", _response_with_did("did-1")), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["signup"], | |
| DummyResponse(payload={"page": {"type": OPENAI_PAGE_TYPES["PASSWORD_REGISTRATION"]}}), | |
| ), | |
| ("POST", OPENAI_API_ENDPOINTS["register"], DummyResponse(payload={})), | |
| ("GET", OPENAI_API_ENDPOINTS["send_otp"], DummyResponse(payload={})), | |
| ("POST", OPENAI_API_ENDPOINTS["validate_otp"], DummyResponse(payload={})), | |
| ("POST", OPENAI_API_ENDPOINTS["create_account"], DummyResponse(payload={})), | |
| ]) | |
| session_two = QueueSession([ | |
| ("GET", "https://auth.example.test/flow/2", _response_with_did("did-2")), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["signup"], | |
| DummyResponse(payload={"page": {"type": OPENAI_PAGE_TYPES["LOGIN_PASSWORD"]}}), | |
| ), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["password_verify"], | |
| DummyResponse(payload={"page": {"type": OPENAI_PAGE_TYPES["EMAIL_OTP_VERIFICATION"]}}), | |
| ), | |
| ("GET", OPENAI_API_ENDPOINTS["send_otp"], DummyResponse(payload={})), | |
| ("POST", OPENAI_API_ENDPOINTS["validate_otp"], _response_with_login_cookies()), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["select_workspace"], | |
| DummyResponse(payload={"continue_url": "https://auth.example.test/continue"}), | |
| ), | |
| ( | |
| "GET", | |
| "https://auth.example.test/continue", | |
| DummyResponse( | |
| status_code=302, | |
| headers={"Location": "http://localhost:1455/auth/callback?code=code-2&state=state-2"}, | |
| ), | |
| ), | |
| ]) | |
| email_service = FakeOutlookEmailService(["123456", "123456"]) | |
| engine = RegistrationEngine(email_service) | |
| fake_oauth = FakeOAuthManager() | |
| engine.http_client = FakeOpenAIClient([session_one, session_two], ["sentinel-1", "sentinel-2"]) | |
| engine.oauth_manager = fake_oauth | |
| result = engine.run() | |
| assert result.success is True | |
| assert email_service.verification_stages[0]["stage"] == "signup_otp" | |
| assert email_service.verification_stages[-1]["stage"] == "relogin_otp" | |
| assert {item["stage"] for item in email_service.verification_stages} == {"signup_otp", "relogin_otp"} | |
| assert [item["stage"] for item in email_service.otp_requests] == ["signup_otp", "relogin_otp"] | |
| assert email_service.otp_requests[0]["timeout"] == 120 | |
| assert email_service.otp_requests[1]["timeout"] == 180 | |
| assert sum(1 for call in session_two.calls if call["url"] == OPENAI_API_ENDPOINTS["send_otp"]) == 1 | |
| assert session_two.calls[3]["kwargs"]["headers"]["referer"] == "https://auth.openai.com/log-in/password" | |
| def test_run_registers_then_relogs_outlook_keeps_polling_when_manual_resend_fails(): | |
| session_one = QueueSession([ | |
| ("GET", "https://auth.example.test/flow/1", _response_with_did("did-1")), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["signup"], | |
| DummyResponse(payload={"page": {"type": OPENAI_PAGE_TYPES["PASSWORD_REGISTRATION"]}}), | |
| ), | |
| ("POST", OPENAI_API_ENDPOINTS["register"], DummyResponse(payload={})), | |
| ("GET", OPENAI_API_ENDPOINTS["send_otp"], DummyResponse(payload={})), | |
| ("POST", OPENAI_API_ENDPOINTS["validate_otp"], DummyResponse(payload={})), | |
| ("POST", OPENAI_API_ENDPOINTS["create_account"], DummyResponse(payload={})), | |
| ]) | |
| session_two = QueueSession([ | |
| ("GET", "https://auth.example.test/flow/2", _response_with_did("did-2")), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["signup"], | |
| DummyResponse(payload={"page": {"type": OPENAI_PAGE_TYPES["LOGIN_PASSWORD"]}}), | |
| ), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["password_verify"], | |
| DummyResponse(payload={"page": {"type": OPENAI_PAGE_TYPES["EMAIL_OTP_VERIFICATION"]}}), | |
| ), | |
| ("GET", OPENAI_API_ENDPOINTS["send_otp"], DummyResponse(status_code=500, payload={})), | |
| ("POST", OPENAI_API_ENDPOINTS["validate_otp"], _response_with_login_cookies("ws-fallback", "session-fallback")), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["select_workspace"], | |
| DummyResponse(payload={"continue_url": "https://auth.example.test/continue-fallback"}), | |
| ), | |
| ( | |
| "GET", | |
| "https://auth.example.test/continue-fallback", | |
| DummyResponse( | |
| status_code=302, | |
| headers={"Location": "http://localhost:1455/auth/callback?code=code-2&state=state-2"}, | |
| ), | |
| ), | |
| ]) | |
| email_service = FakeOutlookEmailService(["123456", "123456"]) | |
| engine = RegistrationEngine(email_service) | |
| fake_oauth = FakeOAuthManager() | |
| engine.http_client = FakeOpenAIClient([session_one, session_two], ["sentinel-1", "sentinel-2"]) | |
| engine.oauth_manager = fake_oauth | |
| result = engine.run() | |
| assert result.success is True | |
| assert result.workspace_id == "ws-fallback" | |
| assert sum(1 for call in session_two.calls if call["url"] == OPENAI_API_ENDPOINTS["send_otp"]) == 1 | |
| assert email_service.otp_requests[1]["timeout"] == 180 | |
| def test_run_persists_recoverable_outlook_account_when_workspace_lookup_fails(monkeypatch): | |
| from src.database.models import Account | |
| manager = _make_engine_session_manager("recoverable-workspace-fail.db") | |
| def fake_get_db(): | |
| session = manager.SessionLocal() | |
| try: | |
| yield session | |
| finally: | |
| session.close() | |
| monkeypatch.setattr("src.core.register.get_db", fake_get_db) | |
| session_one = QueueSession([ | |
| ("GET", "https://auth.example.test/flow/1", _response_with_did("did-1")), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["signup"], | |
| DummyResponse(payload={"page": {"type": OPENAI_PAGE_TYPES["PASSWORD_REGISTRATION"]}}), | |
| ), | |
| ("POST", OPENAI_API_ENDPOINTS["register"], DummyResponse(payload={})), | |
| ("GET", OPENAI_API_ENDPOINTS["send_otp"], DummyResponse(payload={})), | |
| ("POST", OPENAI_API_ENDPOINTS["validate_otp"], DummyResponse(payload={})), | |
| ("POST", OPENAI_API_ENDPOINTS["create_account"], DummyResponse(payload={})), | |
| ]) | |
| session_two = QueueSession([ | |
| ("GET", "https://auth.example.test/flow/2", _response_with_did("did-2")), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["signup"], | |
| DummyResponse(payload={"page": {"type": OPENAI_PAGE_TYPES["LOGIN_PASSWORD"]}}), | |
| ), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["password_verify"], | |
| DummyResponse(payload={"page": {"type": OPENAI_PAGE_TYPES["EMAIL_OTP_VERIFICATION"]}}), | |
| ), | |
| ("GET", OPENAI_API_ENDPOINTS["send_otp"], DummyResponse(payload={})), | |
| ("POST", OPENAI_API_ENDPOINTS["validate_otp"], _response_with_login_cookies_without_workspace()), | |
| ( | |
| "GET", | |
| "https://auth.example.test/flow/2", | |
| DummyResponse(status_code=200, payload={}, url="https://auth.example.test/flow/2"), | |
| ), | |
| ]) | |
| email_service = FakeOutlookEmailService(["123456", "654321", "777888"]) | |
| engine = RegistrationEngine(email_service) | |
| engine.http_client = FakeOpenAIClient([session_one, session_two], ["sentinel-1", "sentinel-2"]) | |
| engine.oauth_manager = FakeOAuthManager() | |
| result = engine.run() | |
| assert result.success is False | |
| assert result.error_message == "OAuth 续跑失败" | |
| with manager.session_scope() as session: | |
| account = session.query(Account).filter(Account.email == "tester@example.com").first() | |
| account_snapshot = { | |
| "password": account.password, | |
| "status": account.status, | |
| "email_service": account.email_service, | |
| "extra_data": dict(account.extra_data or {}), | |
| } if account else None | |
| assert account_snapshot is not None | |
| assert account_snapshot["password"] | |
| assert account_snapshot["status"] == "failed" | |
| assert account_snapshot["email_service"] == "outlook" | |
| assert account_snapshot["extra_data"]["recovery_ready"] is True | |
| assert account_snapshot["extra_data"]["account_created"] is True | |
| assert account_snapshot["extra_data"]["token_acquired"] is False | |
| assert account_snapshot["extra_data"]["register_failed_reason"] == "token_recovery_pending" | |
| assert account_snapshot["extra_data"]["last_recovery_error"] == "OAuth 续跑失败" | |
| assert account_snapshot["extra_data"]["last_oauth_resume_source"] == "authorize_replay_failed" | |
| def test_run_recovers_outlook_account_with_saved_password_and_updates_existing_record(monkeypatch): | |
| from src.database import crud | |
| from src.database.models import Account | |
| manager = _make_engine_session_manager("recoverable-success.db") | |
| with manager.session_scope() as session: | |
| crud.create_account( | |
| session, | |
| email="tester@example.com", | |
| password="saved-password", | |
| email_service="outlook", | |
| email_service_id="mailbox-1", | |
| status="failed", | |
| source="register", | |
| extra_data={ | |
| "recovery_ready": True, | |
| "account_created": True, | |
| "token_acquired": False, | |
| "register_failed_reason": "token_recovery_pending", | |
| "last_recovery_error": "获取 Workspace ID 失败", | |
| }, | |
| ) | |
| def fake_get_db(): | |
| session = manager.SessionLocal() | |
| try: | |
| yield session | |
| finally: | |
| session.close() | |
| monkeypatch.setattr("src.core.register.get_db", fake_get_db) | |
| session = QueueSession([ | |
| ("GET", "https://auth.example.test/flow/1", _response_with_did("did-1")), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["signup"], | |
| DummyResponse(payload={"page": {"type": OPENAI_PAGE_TYPES["LOGIN_PASSWORD"]}}), | |
| ), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["password_verify"], | |
| DummyResponse(payload={"page": {"type": OPENAI_PAGE_TYPES["EMAIL_OTP_VERIFICATION"]}}), | |
| ), | |
| ("GET", OPENAI_API_ENDPOINTS["send_otp"], DummyResponse(payload={})), | |
| ("POST", OPENAI_API_ENDPOINTS["validate_otp"], _response_with_login_cookies("ws-recovered", "session-recovered")), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["select_workspace"], | |
| DummyResponse(payload={"continue_url": "https://auth.example.test/recover"}), | |
| ), | |
| ( | |
| "GET", | |
| "https://auth.example.test/recover", | |
| DummyResponse( | |
| status_code=302, | |
| headers={"Location": "http://localhost:1455/auth/callback?code=code-1&state=state-1"}, | |
| ), | |
| ), | |
| ]) | |
| email_service = FakeOutlookEmailService(["789012"]) | |
| engine = RegistrationEngine(email_service) | |
| engine.http_client = FakeOpenAIClient([session], ["sentinel-1", "sentinel-2"]) | |
| engine.oauth_manager = FakeOAuthManager() | |
| result = engine.run() | |
| assert result.success is True | |
| assert result.metadata["database_saved"] is True | |
| assert not any(call["url"] == OPENAI_API_ENDPOINTS["register"] for call in session.calls) | |
| assert not any(call["url"] == OPENAI_API_ENDPOINTS["create_account"] for call in session.calls) | |
| assert [item["stage"] for item in email_service.otp_requests] == ["relogin_otp"] | |
| assert email_service.otp_requests[0]["timeout"] == 180 | |
| with manager.session_scope() as session_db: | |
| accounts = session_db.query(Account).filter(Account.email == "tester@example.com").all() | |
| assert len(accounts) == 1 | |
| account = accounts[0] | |
| assert account.status == "active" | |
| assert account.password == "saved-password" | |
| assert account.workspace_id == "ws-recovered" | |
| assert account.access_token == "access-1" | |
| assert account.extra_data["recovery_ready"] is False | |
| assert account.extra_data["token_acquired"] is True | |
| def test_run_recovers_outlook_account_uses_relogin_otp_and_manual_resend(monkeypatch): | |
| from src.database import crud | |
| manager = _make_engine_session_manager("recoverable-relogin-otp.db") | |
| with manager.session_scope() as session: | |
| crud.create_account( | |
| session, | |
| email="tester@example.com", | |
| password="saved-password", | |
| email_service="outlook", | |
| email_service_id="mailbox-1", | |
| status="failed", | |
| source="register", | |
| extra_data={ | |
| "recovery_ready": True, | |
| "account_created": True, | |
| "token_acquired": False, | |
| "register_failed_reason": "token_recovery_pending", | |
| "last_recovery_error": "获取 Workspace ID 失败", | |
| }, | |
| ) | |
| def fake_get_db(): | |
| session = manager.SessionLocal() | |
| try: | |
| yield session | |
| finally: | |
| session.close() | |
| monkeypatch.setattr("src.core.register.get_db", fake_get_db) | |
| session = QueueSession([ | |
| ("GET", "https://auth.example.test/flow/1", _response_with_did("did-1")), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["signup"], | |
| DummyResponse(payload={"page": {"type": OPENAI_PAGE_TYPES["LOGIN_PASSWORD"]}}), | |
| ), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["password_verify"], | |
| DummyResponse(payload={"page": {"type": OPENAI_PAGE_TYPES["EMAIL_OTP_VERIFICATION"]}}), | |
| ), | |
| ("GET", OPENAI_API_ENDPOINTS["send_otp"], DummyResponse(payload={})), | |
| ("POST", OPENAI_API_ENDPOINTS["validate_otp"], _response_with_login_cookies("ws-recovered", "session-recovered")), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["select_workspace"], | |
| DummyResponse(payload={"continue_url": "https://auth.example.test/recover"}), | |
| ), | |
| ( | |
| "GET", | |
| "https://auth.example.test/recover", | |
| DummyResponse( | |
| status_code=302, | |
| headers={"Location": "http://localhost:1455/auth/callback?code=code-1&state=state-1"}, | |
| ), | |
| ), | |
| ]) | |
| email_service = FakeOutlookEmailService(["789012"]) | |
| engine = RegistrationEngine(email_service) | |
| engine.http_client = FakeOpenAIClient([session], ["sentinel-1", "sentinel-2"]) | |
| engine.oauth_manager = FakeOAuthManager() | |
| result = engine.run() | |
| assert result.success is True | |
| assert result.metadata["database_saved"] is True | |
| assert email_service.verification_stages[-1]["stage"] == "relogin_otp" | |
| assert [item["stage"] for item in email_service.otp_requests] == ["relogin_otp"] | |
| assert email_service.otp_requests[0]["timeout"] == 180 | |
| send_otp_calls = [call for call in session.calls if call["url"] == OPENAI_API_ENDPOINTS["send_otp"]] | |
| assert len(send_otp_calls) == 1 | |
| assert send_otp_calls[0]["kwargs"]["headers"]["referer"] == "https://auth.openai.com/log-in/password" | |
| def test_run_persists_recoverable_outlook_account_on_relogin_otp_timeout(monkeypatch): | |
| from src.database.models import Account | |
| from src.database import crud | |
| manager = _make_engine_session_manager("recoverable-relogin-timeout.db") | |
| with manager.session_scope() as session: | |
| crud.create_account( | |
| session, | |
| email="tester@example.com", | |
| password="saved-password", | |
| email_service="outlook", | |
| email_service_id="mailbox-1", | |
| status="failed", | |
| source="register", | |
| extra_data={ | |
| "recovery_ready": True, | |
| "account_created": True, | |
| "token_acquired": False, | |
| "register_failed_reason": "token_recovery_pending", | |
| "last_recovery_error": "获取 Workspace ID 失败", | |
| }, | |
| ) | |
| def fake_get_db(): | |
| session = manager.SessionLocal() | |
| try: | |
| yield session | |
| finally: | |
| session.close() | |
| monkeypatch.setattr("src.core.register.get_db", fake_get_db) | |
| session = QueueSession([ | |
| ("GET", "https://auth.example.test/flow/1", _response_with_did("did-1")), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["signup"], | |
| DummyResponse(payload={"page": {"type": OPENAI_PAGE_TYPES["LOGIN_PASSWORD"]}}), | |
| ), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["password_verify"], | |
| DummyResponse(payload={"page": {"type": OPENAI_PAGE_TYPES["EMAIL_OTP_VERIFICATION"]}}), | |
| ), | |
| ("GET", OPENAI_API_ENDPOINTS["send_otp"], DummyResponse(payload={})), | |
| ]) | |
| email_service = FakeOutlookEmailService([None]) | |
| engine = RegistrationEngine(email_service) | |
| engine.http_client = FakeOpenAIClient([session], ["sentinel-1"]) | |
| engine.oauth_manager = FakeOAuthManager() | |
| result = engine.run() | |
| assert result.success is False | |
| assert result.error_message == "获取验证码失败" | |
| assert email_service.verification_stages[-1]["stage"] == "relogin_otp" | |
| assert [item["stage"] for item in email_service.otp_requests] == ["relogin_otp"] | |
| assert email_service.otp_requests[0]["timeout"] == 180 | |
| with manager.session_scope() as session_db: | |
| account = session_db.query(Account).filter(Account.email == "tester@example.com").first() | |
| extra_data = dict(account.extra_data or {}) | |
| assert extra_data["recovery_ready"] is True | |
| assert extra_data["token_acquired"] is False | |
| assert extra_data["last_recovery_error"] == "获取验证码失败" | |
| assert extra_data["last_otp_stage"] == "relogin_otp" | |
| def test_run_uses_workspace_from_validate_otp_response_payload(): | |
| session_one = QueueSession([ | |
| ("GET", "https://auth.example.test/flow/1", _response_with_did("did-1")), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["signup"], | |
| DummyResponse(payload={"page": {"type": OPENAI_PAGE_TYPES["PASSWORD_REGISTRATION"]}}), | |
| ), | |
| ("POST", OPENAI_API_ENDPOINTS["register"], DummyResponse(payload={})), | |
| ("GET", OPENAI_API_ENDPOINTS["send_otp"], DummyResponse(payload={})), | |
| ("POST", OPENAI_API_ENDPOINTS["validate_otp"], DummyResponse(payload={})), | |
| ("POST", OPENAI_API_ENDPOINTS["create_account"], DummyResponse(payload={})), | |
| ]) | |
| session_two = QueueSession([ | |
| ("GET", "https://auth.example.test/flow/2", _response_with_did("did-2")), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["signup"], | |
| DummyResponse(payload={"page": {"type": OPENAI_PAGE_TYPES["LOGIN_PASSWORD"]}}), | |
| ), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["password_verify"], | |
| DummyResponse(payload={"page": {"type": OPENAI_PAGE_TYPES["EMAIL_OTP_VERIFICATION"]}}), | |
| ), | |
| ("GET", OPENAI_API_ENDPOINTS["send_otp"], DummyResponse(payload={})), | |
| ("POST", OPENAI_API_ENDPOINTS["validate_otp"], _response_with_payload_workspace_only("ws-payload")), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["select_workspace"], | |
| DummyResponse(payload={"continue_url": "https://auth.example.test/continue-payload"}), | |
| ), | |
| ( | |
| "GET", | |
| "https://auth.example.test/continue-payload", | |
| DummyResponse( | |
| status_code=302, | |
| headers={"Location": "http://localhost:1455/auth/callback?code=code-2&state=state-2"}, | |
| ), | |
| ), | |
| ]) | |
| email_service = FakeOutlookEmailService(["123456", "654321"]) | |
| engine = RegistrationEngine(email_service) | |
| engine.http_client = FakeOpenAIClient( | |
| [session_one, session_two], | |
| ["sentinel-1", "sentinel-2", "sentinel-3"], | |
| ) | |
| engine.oauth_manager = FakeOAuthManager() | |
| result = engine.run() | |
| assert result.success is True | |
| assert result.workspace_id == "ws-payload" | |
| def test_set_verification_stage_syncs_to_stage_aware_temp_mail_service(): | |
| email_service = FakeStageAwareTempMailService([]) | |
| engine = RegistrationEngine(email_service) | |
| engine.email = "tester@example.com" | |
| engine._set_verification_stage("signup_otp") | |
| engine._set_verification_stage("relogin_otp") | |
| assert [item["stage"] for item in email_service.verification_stages] == ["signup_otp", "relogin_otp"] | |
| def test_run_can_complete_oauth_callback_before_workspace_is_resolved(): | |
| session_one = QueueSession([ | |
| ("GET", "https://auth.example.test/flow/1", _response_with_did("did-1")), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["signup"], | |
| DummyResponse(payload={"page": {"type": OPENAI_PAGE_TYPES["PASSWORD_REGISTRATION"]}}), | |
| ), | |
| ("POST", OPENAI_API_ENDPOINTS["register"], DummyResponse(payload={})), | |
| ("GET", OPENAI_API_ENDPOINTS["send_otp"], DummyResponse(payload={})), | |
| ("POST", OPENAI_API_ENDPOINTS["validate_otp"], DummyResponse(payload={})), | |
| ("POST", OPENAI_API_ENDPOINTS["create_account"], DummyResponse(payload={})), | |
| ]) | |
| session_two = QueueSession([ | |
| ("GET", "https://auth.example.test/flow/2", _response_with_did("did-2")), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["signup"], | |
| DummyResponse(payload={"page": {"type": OPENAI_PAGE_TYPES["LOGIN_PASSWORD"]}}), | |
| ), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["password_verify"], | |
| DummyResponse(payload={"page": {"type": OPENAI_PAGE_TYPES["EMAIL_OTP_VERIFICATION"]}}), | |
| ), | |
| ("GET", OPENAI_API_ENDPOINTS["send_otp"], DummyResponse(payload={})), | |
| ("POST", OPENAI_API_ENDPOINTS["validate_otp"], _response_with_callback_before_workspace()), | |
| ]) | |
| email_service = FakeOutlookEmailService(["123456", "654321"]) | |
| engine = RegistrationEngine(email_service) | |
| engine.http_client = FakeOpenAIClient( | |
| [session_one, session_two], | |
| ["sentinel-1", "sentinel-2", "sentinel-3"], | |
| ) | |
| engine.oauth_manager = FakeOAuthManagerWithWorkspace("ws-token-fallback") | |
| result = engine.run() | |
| assert result.success is True | |
| assert result.workspace_id == "ws-token-fallback" | |
| assert not any(call["url"] == OPENAI_API_ENDPOINTS["select_workspace"] for call in session_two.calls) | |
| def test_run_recovers_outlook_account_by_resuming_login_challenge_after_validate_otp(monkeypatch): | |
| from src.database import crud | |
| from src.database.models import Account | |
| manager = _make_engine_session_manager("recoverable-login-challenge-resume.db") | |
| with manager.session_scope() as session: | |
| crud.create_account( | |
| session, | |
| email="tester@example.com", | |
| password="saved-password", | |
| email_service="outlook", | |
| email_service_id="mailbox-1", | |
| status="failed", | |
| source="register", | |
| extra_data={ | |
| "recovery_ready": True, | |
| "account_created": True, | |
| "token_acquired": False, | |
| "register_failed_reason": "token_recovery_pending", | |
| "last_recovery_error": "获取 Workspace ID 失败", | |
| }, | |
| ) | |
| def fake_get_db(): | |
| session = manager.SessionLocal() | |
| try: | |
| yield session | |
| finally: | |
| session.close() | |
| monkeypatch.setattr("src.core.register.get_db", fake_get_db) | |
| session = QueueSession([ | |
| ("GET", "https://auth.example.test/flow/1", _response_with_did("did-1")), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["signup"], | |
| DummyResponse(payload={"page": {"type": OPENAI_PAGE_TYPES["LOGIN_PASSWORD"]}}), | |
| ), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["password_verify"], | |
| DummyResponse(payload={"page": {"type": OPENAI_PAGE_TYPES["EMAIL_OTP_VERIFICATION"]}}), | |
| ), | |
| ("GET", OPENAI_API_ENDPOINTS["send_otp"], DummyResponse(payload={})), | |
| ("POST", OPENAI_API_ENDPOINTS["validate_otp"], _response_with_resume_history()), | |
| ( | |
| "GET", | |
| "https://auth.example.test/login-challenge?login_challenge=challenge-1", | |
| DummyResponse( | |
| status_code=302, | |
| headers={"Location": "http://localhost:1455/auth/callback?code=code-1&state=state-1"}, | |
| ), | |
| ), | |
| ]) | |
| email_service = FakeOutlookEmailService(["789012"]) | |
| engine = RegistrationEngine(email_service) | |
| engine.http_client = FakeOpenAIClient([session], ["sentinel-1"]) | |
| engine.oauth_manager = FakeOAuthManagerWithWorkspace("ws-replayed") | |
| result = engine.run() | |
| assert result.success is True | |
| assert result.workspace_id == "ws-replayed" | |
| assert result.metadata["database_saved"] is True | |
| assert not any(call["url"] == OPENAI_API_ENDPOINTS["select_workspace"] for call in session.calls) | |
| assert session.calls[-1]["url"] == "https://auth.example.test/login-challenge?login_challenge=challenge-1" | |
| assert result.metadata["last_oauth_resume_source"] == "login_challenge_resume" | |
| with manager.session_scope() as session_db: | |
| account = session_db.query(Account).filter(Account.email == "tester@example.com").first() | |
| assert account.status == "active" | |
| assert account.workspace_id == "ws-replayed" | |
| def test_run_recovers_outlook_account_from_email_verification_page_text_resume(monkeypatch): | |
| from src.database import crud | |
| from src.database.models import Account | |
| manager = _make_engine_session_manager("recoverable-email-verification-resume.db") | |
| with manager.session_scope() as session: | |
| crud.create_account( | |
| session, | |
| email="tester@example.com", | |
| password="saved-password", | |
| email_service="outlook", | |
| email_service_id="mailbox-1", | |
| status="failed", | |
| source="register", | |
| extra_data={ | |
| "recovery_ready": True, | |
| "account_created": True, | |
| "token_acquired": False, | |
| "register_failed_reason": "token_recovery_pending", | |
| "last_recovery_error": "获取 Workspace ID 失败", | |
| }, | |
| ) | |
| def fake_get_db(): | |
| session = manager.SessionLocal() | |
| try: | |
| yield session | |
| finally: | |
| session.close() | |
| monkeypatch.setattr("src.core.register.get_db", fake_get_db) | |
| resume_url = "https://auth.example.test/api/oauth/oauth2/auth?client_id=client-1&state=resume-1" | |
| session = QueueSession([ | |
| ("GET", "https://auth.example.test/flow/1", _response_with_did("did-1")), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["signup"], | |
| DummyResponse(payload={"page": {"type": OPENAI_PAGE_TYPES["LOGIN_PASSWORD"]}}), | |
| ), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["password_verify"], | |
| DummyResponse(payload={"page": {"type": OPENAI_PAGE_TYPES["EMAIL_OTP_VERIFICATION"]}}), | |
| ), | |
| ("GET", OPENAI_API_ENDPOINTS["send_otp"], DummyResponse(payload={})), | |
| ("POST", OPENAI_API_ENDPOINTS["validate_otp"], _response_with_email_verification_resume_text(resume_url)), | |
| ( | |
| "GET", | |
| resume_url, | |
| DummyResponse( | |
| status_code=302, | |
| headers={"Location": "http://localhost:1455/auth/callback?code=code-1&state=state-1"}, | |
| ), | |
| ), | |
| ]) | |
| email_service = FakeOutlookEmailService(["789012"]) | |
| engine = RegistrationEngine(email_service) | |
| engine.http_client = FakeOpenAIClient([session], ["sentinel-1"]) | |
| engine.oauth_manager = FakeOAuthManagerWithWorkspace("ws-email-verification") | |
| result = engine.run() | |
| assert result.success is True | |
| assert result.workspace_id == "ws-email-verification" | |
| assert not any(call["url"] == OPENAI_API_ENDPOINTS["select_workspace"] for call in session.calls) | |
| assert any(call["url"] == resume_url for call in session.calls) | |
| assert result.metadata["last_oauth_resume_source"] in { | |
| "resume_url_found_after_validate_otp", | |
| "continue_url_resume", | |
| "resume_url_found_from_navigation", | |
| } | |
| with manager.session_scope() as session_db: | |
| account = session_db.query(Account).filter(Account.email == "tester@example.com").first() | |
| assert account.status == "active" | |
| assert account.workspace_id == "ws-email-verification" | |
| def test_run_recovers_outlook_account_by_completing_about_you_before_relogin(monkeypatch): | |
| from src.database import crud | |
| from src.database.models import Account | |
| manager = _make_engine_session_manager("recoverable-about-you-success.db") | |
| with manager.session_scope() as session: | |
| crud.create_account( | |
| session, | |
| email="tester@example.com", | |
| password="saved-password", | |
| email_service="outlook", | |
| email_service_id="mailbox-1", | |
| status="failed", | |
| source="register", | |
| extra_data={ | |
| "recovery_ready": True, | |
| "account_created": True, | |
| "token_acquired": False, | |
| "register_failed_reason": "token_recovery_pending", | |
| "last_recovery_error": "获取 Workspace ID 失败", | |
| }, | |
| ) | |
| def fake_get_db(): | |
| session = manager.SessionLocal() | |
| try: | |
| yield session | |
| finally: | |
| session.close() | |
| monkeypatch.setattr("src.core.register.get_db", fake_get_db) | |
| session = QueueSession([ | |
| ("GET", "https://auth.example.test/flow/1", _response_with_did("did-1")), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["signup"], | |
| DummyResponse( | |
| payload={ | |
| "page": {"type": OPENAI_PAGE_TYPES["LOGIN_PASSWORD"]}, | |
| "method": "POST", | |
| "continue_url": "https://auth.example.test/log-in/password", | |
| } | |
| ), | |
| ), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["password_verify"], | |
| DummyResponse( | |
| payload={ | |
| "page": {"type": OPENAI_PAGE_TYPES["EMAIL_OTP_VERIFICATION"]}, | |
| "method": "POST", | |
| "continue_url": "https://auth.example.test/email-verification", | |
| } | |
| ), | |
| ), | |
| ("GET", OPENAI_API_ENDPOINTS["send_otp"], DummyResponse(payload={})), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["validate_otp"], | |
| DummyResponse( | |
| payload={ | |
| "page": {"type": OPENAI_PAGE_TYPES["EMAIL_OTP_VERIFICATION"]}, | |
| "method": "POST", | |
| "continue_url": "https://auth.example.test/about-you", | |
| } | |
| ), | |
| ), | |
| ( | |
| "GET", | |
| "https://auth.example.test/about-you", | |
| DummyResponse(status_code=200, url="https://auth.example.test/about-you", text="<html>about you</html>"), | |
| ), | |
| ("POST", OPENAI_API_ENDPOINTS["create_account"], DummyResponse(payload={})), | |
| ("GET", "https://auth.example.test/flow/2", _response_with_did("did-2")), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["signup"], | |
| DummyResponse(payload={"page": {"type": OPENAI_PAGE_TYPES["LOGIN_PASSWORD"]}}), | |
| ), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["password_verify"], | |
| DummyResponse(payload={"page": {"type": OPENAI_PAGE_TYPES["EMAIL_OTP_VERIFICATION"]}}), | |
| ), | |
| ("GET", OPENAI_API_ENDPOINTS["send_otp"], DummyResponse(payload={})), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["validate_otp"], | |
| _response_with_callback_before_workspace( | |
| callback_url="http://localhost:1455/auth/callback?code=code-about-you&state=state-2", | |
| session_token="session-about-you", | |
| ), | |
| ), | |
| ]) | |
| email_service = FakeOutlookEmailService(["123456", "654321"]) | |
| engine = RegistrationEngine(email_service) | |
| engine.http_client = FakeOpenAIClient([session], ["sentinel-1", "sentinel-2"]) | |
| engine.oauth_manager = FakeOAuthManagerWithWorkspace("ws-about-you") | |
| result = engine.run() | |
| assert result.success is True | |
| assert result.workspace_id == "ws-about-you" | |
| assert result.session_token == "session-about-you" | |
| assert any(call["url"] == OPENAI_API_ENDPOINTS["create_account"] for call in session.calls) | |
| assert len([call for call in session.calls if call["url"] == OPENAI_API_ENDPOINTS["validate_otp"]]) == 2 | |
| with manager.session_scope() as session_db: | |
| account = session_db.query(Account).filter(Account.email == "tester@example.com").first() | |
| assert account.status == "active" | |
| assert account.workspace_id == "ws-about-you" | |
| def test_run_recovers_outlook_account_when_about_you_create_account_reports_user_exists(monkeypatch): | |
| from src.database import crud | |
| from src.database.models import Account | |
| manager = _make_engine_session_manager("recoverable-about-you-user-exists.db") | |
| with manager.session_scope() as session: | |
| crud.create_account( | |
| session, | |
| email="tester@example.com", | |
| password="saved-password", | |
| email_service="outlook", | |
| email_service_id="mailbox-1", | |
| status="failed", | |
| source="register", | |
| extra_data={ | |
| "recovery_ready": True, | |
| "account_created": True, | |
| "token_acquired": False, | |
| "register_failed_reason": "token_recovery_pending", | |
| "last_recovery_error": "鑾峰彇 Workspace ID 澶辫触", | |
| }, | |
| ) | |
| def fake_get_db(): | |
| session = manager.SessionLocal() | |
| try: | |
| yield session | |
| finally: | |
| session.close() | |
| monkeypatch.setattr("src.core.register.get_db", fake_get_db) | |
| session = QueueSession([ | |
| ("GET", "https://auth.example.test/flow/1", _response_with_did("did-1")), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["signup"], | |
| DummyResponse( | |
| payload={ | |
| "page": {"type": OPENAI_PAGE_TYPES["LOGIN_PASSWORD"]}, | |
| "method": "POST", | |
| "continue_url": "https://auth.example.test/log-in/password", | |
| } | |
| ), | |
| ), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["password_verify"], | |
| DummyResponse( | |
| payload={ | |
| "page": {"type": OPENAI_PAGE_TYPES["EMAIL_OTP_VERIFICATION"]}, | |
| "method": "POST", | |
| "continue_url": "https://auth.example.test/email-verification", | |
| } | |
| ), | |
| ), | |
| ("GET", OPENAI_API_ENDPOINTS["send_otp"], DummyResponse(payload={})), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["validate_otp"], | |
| DummyResponse( | |
| payload={ | |
| "page": {"type": OPENAI_PAGE_TYPES["EMAIL_OTP_VERIFICATION"]}, | |
| "method": "POST", | |
| "continue_url": "https://auth.example.test/about-you", | |
| } | |
| ), | |
| ), | |
| ( | |
| "GET", | |
| "https://auth.example.test/about-you", | |
| DummyResponse(status_code=200, url="https://auth.example.test/about-you", text="<html>about you</html>"), | |
| ), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["create_account"], | |
| DummyResponse( | |
| status_code=400, | |
| payload={ | |
| "error": { | |
| "message": "An account already exists for this email address.", | |
| "code": "user_already_exists", | |
| } | |
| }, | |
| text='{"error":{"message":"An account already exists for this email address.","code":"user_already_exists"}}', | |
| ), | |
| ), | |
| ( | |
| "GET", | |
| "https://auth.example.test/flow/1", | |
| DummyResponse( | |
| status_code=302, | |
| headers={"Location": "https://auth.example.test/log-in"}, | |
| ), | |
| ), | |
| ( | |
| "GET", | |
| "https://auth.example.test/log-in", | |
| DummyResponse(status_code=200, payload={}, url="https://auth.example.test/log-in"), | |
| ), | |
| ("GET", "https://auth.example.test/flow/2", _response_with_did("did-2")), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["signup"], | |
| DummyResponse(payload={"page": {"type": OPENAI_PAGE_TYPES["LOGIN_PASSWORD"]}}), | |
| ), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["password_verify"], | |
| DummyResponse(payload={"page": {"type": OPENAI_PAGE_TYPES["EMAIL_OTP_VERIFICATION"]}}), | |
| ), | |
| ("GET", OPENAI_API_ENDPOINTS["send_otp"], DummyResponse(payload={})), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["validate_otp"], | |
| _response_with_callback_before_workspace( | |
| callback_url="http://localhost:1455/auth/callback?code=code-about-you-exists&state=state-2", | |
| session_token="session-about-you-exists", | |
| ), | |
| ), | |
| ]) | |
| email_service = FakeOutlookEmailService(["123456", "654321"]) | |
| engine = RegistrationEngine(email_service) | |
| engine.http_client = FakeOpenAIClient([session], ["sentinel-1", "sentinel-2"]) | |
| engine.oauth_manager = FakeOAuthManagerWithWorkspace("ws-about-you-exists") | |
| result = engine.run() | |
| assert result.success is True | |
| assert result.workspace_id == "ws-about-you-exists" | |
| assert result.session_token == "session-about-you-exists" | |
| assert any(call["url"] == OPENAI_API_ENDPOINTS["create_account"] for call in session.calls) | |
| assert len([call for call in session.calls if call["url"] == OPENAI_API_ENDPOINTS["validate_otp"]]) == 2 | |
| with manager.session_scope() as session_db: | |
| account = session_db.query(Account).filter(Account.email == "tester@example.com").first() | |
| assert account.status == "active" | |
| assert account.workspace_id == "ws-about-you-exists" | |
| def test_run_recovers_outlook_account_from_same_session_after_about_you_user_exists(monkeypatch): | |
| from src.database import crud | |
| from src.database.models import Account | |
| manager = _make_engine_session_manager("recoverable-about-you-same-session.db") | |
| with manager.session_scope() as session: | |
| crud.create_account( | |
| session, | |
| email="tester@example.com", | |
| password="saved-password", | |
| email_service="outlook", | |
| email_service_id="mailbox-1", | |
| status="failed", | |
| source="register", | |
| extra_data={ | |
| "recovery_ready": True, | |
| "account_created": True, | |
| "token_acquired": False, | |
| "register_failed_reason": "token_recovery_pending", | |
| "last_recovery_error": "鑾峰彇 Workspace ID 澶辫触", | |
| }, | |
| ) | |
| def fake_get_db(): | |
| session = manager.SessionLocal() | |
| try: | |
| yield session | |
| finally: | |
| session.close() | |
| monkeypatch.setattr("src.core.register.get_db", fake_get_db) | |
| def create_account_user_exists_with_callback(): | |
| def setter(session): | |
| session.cookies["oai-client-auth-session"] = _workspace_cookie_without_workspace() | |
| session.cookies["__Secure-next-auth.session-token"] = "session-about-you-same-session" | |
| return DummyResponse( | |
| status_code=400, | |
| payload={ | |
| "error": { | |
| "message": "An account already exists for this email address.", | |
| "code": "user_already_exists", | |
| }, | |
| "callback_url": "http://localhost:1455/auth/callback?code=code-about-you-same-session&state=state-1", | |
| }, | |
| text='{"error":{"message":"An account already exists for this email address.","code":"user_already_exists"}}', | |
| on_return=setter, | |
| ) | |
| session = QueueSession([ | |
| ("GET", "https://auth.example.test/flow/1", _response_with_did("did-1")), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["signup"], | |
| DummyResponse( | |
| payload={ | |
| "page": {"type": OPENAI_PAGE_TYPES["LOGIN_PASSWORD"]}, | |
| "method": "POST", | |
| "continue_url": "https://auth.example.test/log-in/password", | |
| } | |
| ), | |
| ), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["password_verify"], | |
| DummyResponse( | |
| payload={ | |
| "page": {"type": OPENAI_PAGE_TYPES["EMAIL_OTP_VERIFICATION"]}, | |
| "method": "POST", | |
| "continue_url": "https://auth.example.test/email-verification", | |
| } | |
| ), | |
| ), | |
| ("GET", OPENAI_API_ENDPOINTS["send_otp"], DummyResponse(payload={})), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["validate_otp"], | |
| DummyResponse( | |
| payload={ | |
| "page": {"type": OPENAI_PAGE_TYPES["EMAIL_OTP_VERIFICATION"]}, | |
| "method": "POST", | |
| "continue_url": "https://auth.example.test/about-you", | |
| } | |
| ), | |
| ), | |
| ( | |
| "GET", | |
| "https://auth.example.test/about-you", | |
| DummyResponse(status_code=200, url="https://auth.example.test/about-you", text="<html>about you</html>"), | |
| ), | |
| ("POST", OPENAI_API_ENDPOINTS["create_account"], create_account_user_exists_with_callback()), | |
| ]) | |
| email_service = FakeOutlookEmailService(["123456"]) | |
| engine = RegistrationEngine(email_service) | |
| engine.http_client = FakeOpenAIClient([session], ["sentinel-1"]) | |
| engine.oauth_manager = FakeOAuthManagerWithWorkspace("ws-about-you-same-session") | |
| result = engine.run() | |
| assert result.success is True | |
| assert result.workspace_id == "ws-about-you-same-session" | |
| assert result.session_token == "session-about-you-same-session" | |
| assert len([call for call in session.calls if call["url"].startswith("https://auth.example.test/flow/")]) == 1 | |
| with manager.session_scope() as session_db: | |
| account = session_db.query(Account).filter(Account.email == "tester@example.com").first() | |
| assert account.status == "active" | |
| assert account.workspace_id == "ws-about-you-same-session" | |
| def test_run_recovers_outlook_account_from_authorize_replay_after_about_you_user_exists(monkeypatch): | |
| from src.database import crud | |
| from src.database.models import Account | |
| manager = _make_engine_session_manager("recoverable-about-you-authorize-replay.db") | |
| with manager.session_scope() as session: | |
| crud.create_account( | |
| session, | |
| email="tester@example.com", | |
| password="saved-password", | |
| email_service="outlook", | |
| email_service_id="mailbox-1", | |
| status="failed", | |
| source="register", | |
| extra_data={ | |
| "recovery_ready": True, | |
| "account_created": True, | |
| "token_acquired": False, | |
| "register_failed_reason": "token_recovery_pending", | |
| "last_recovery_error": "获取 Workspace ID 失败", | |
| }, | |
| ) | |
| def fake_get_db(): | |
| session = manager.SessionLocal() | |
| try: | |
| yield session | |
| finally: | |
| session.close() | |
| monkeypatch.setattr("src.core.register.get_db", fake_get_db) | |
| user_exists_response = DummyResponse( | |
| status_code=400, | |
| payload={ | |
| "error": { | |
| "message": "An account already exists for this email address.", | |
| "code": "user_already_exists", | |
| } | |
| }, | |
| text='{"error":{"message":"An account already exists for this email address.","code":"user_already_exists"}}', | |
| ) | |
| oauth_replay_url = ( | |
| "https://auth.example.test/api/oauth/oauth2/auth" | |
| "?client_id=client-1&state=resume-1" | |
| ) | |
| login_challenge_url = ( | |
| "https://auth.example.test/api/accounts/login?login_challenge=challenge-after-about-you" | |
| ) | |
| session = QueueSession([ | |
| ("GET", "https://auth.example.test/flow/1", _response_with_did("did-1")), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["signup"], | |
| DummyResponse(payload={"page": {"type": OPENAI_PAGE_TYPES["LOGIN_PASSWORD"]}}), | |
| ), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["password_verify"], | |
| DummyResponse(payload={"page": {"type": OPENAI_PAGE_TYPES["EMAIL_OTP_VERIFICATION"]}}), | |
| ), | |
| ("GET", OPENAI_API_ENDPOINTS["send_otp"], DummyResponse(payload={})), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["validate_otp"], | |
| DummyResponse( | |
| payload={ | |
| "page": {"type": "about_you"}, | |
| "method": "GET", | |
| "continue_url": "https://auth.example.test/about-you", | |
| }, | |
| on_return=_response_with_login_cookies_without_workspace().on_return, | |
| ), | |
| ), | |
| ( | |
| "GET", | |
| "https://auth.example.test/about-you", | |
| DummyResponse(status_code=200, url="https://auth.example.test/about-you", text="<html>about you</html>"), | |
| ), | |
| ("POST", OPENAI_API_ENDPOINTS["create_account"], user_exists_response), | |
| ( | |
| "GET", | |
| "https://auth.example.test/flow/1", | |
| DummyResponse( | |
| status_code=302, | |
| headers={"Location": oauth_replay_url}, | |
| ), | |
| ), | |
| ( | |
| "GET", | |
| oauth_replay_url, | |
| DummyResponse( | |
| status_code=302, | |
| headers={"Location": login_challenge_url}, | |
| ), | |
| ), | |
| ( | |
| "GET", | |
| login_challenge_url, | |
| DummyResponse( | |
| status_code=302, | |
| headers={"Location": "http://localhost:1455/auth/callback?code=code-about-you-replay&state=state-1"}, | |
| ), | |
| ), | |
| ]) | |
| email_service = FakeOutlookEmailService(["654321"]) | |
| engine = RegistrationEngine(email_service) | |
| engine.http_client = FakeOpenAIClient([session], ["sentinel-1"]) | |
| engine.oauth_manager = FakeOAuthManagerWithWorkspace("ws-about-you-authorize-replay") | |
| result = engine.run() | |
| assert result.success is True | |
| assert result.workspace_id == "ws-about-you-authorize-replay" | |
| assert result.metadata["last_oauth_resume_source"] == "callback_found_from_authorize_replay" | |
| assert len([call for call in session.calls if call["url"] == OPENAI_API_ENDPOINTS["validate_otp"]]) == 1 | |
| assert len([call for call in session.calls if call["url"] == "https://auth.example.test/flow/1"]) == 2 | |
| assert [item["stage"] for item in email_service.otp_requests] == ["relogin_otp"] | |
| with manager.session_scope() as session_db: | |
| account = session_db.query(Account).filter(Account.email == "tester@example.com").first() | |
| assert account.status == "active" | |
| assert account.workspace_id == "ws-about-you-authorize-replay" | |
| def test_run_recovers_outlook_account_from_direct_consent_after_about_you_user_exists(monkeypatch): | |
| from src.database import crud | |
| from src.database.models import Account | |
| manager = _make_engine_session_manager("recoverable-about-you-direct-consent.db") | |
| with manager.session_scope() as session: | |
| crud.create_account( | |
| session, | |
| email="tester@example.com", | |
| password="saved-password", | |
| email_service="outlook", | |
| email_service_id="mailbox-1", | |
| status="failed", | |
| source="register", | |
| extra_data={ | |
| "recovery_ready": True, | |
| "account_created": True, | |
| "token_acquired": False, | |
| "register_failed_reason": "token_recovery_pending", | |
| "last_recovery_error": "获取 Workspace ID 失败", | |
| }, | |
| ) | |
| def fake_get_db(): | |
| session = manager.SessionLocal() | |
| try: | |
| yield session | |
| finally: | |
| session.close() | |
| monkeypatch.setattr("src.core.register.get_db", fake_get_db) | |
| user_exists_response = DummyResponse( | |
| status_code=400, | |
| payload={ | |
| "error": { | |
| "message": "An account already exists for this email address.", | |
| "code": "user_already_exists", | |
| } | |
| }, | |
| text='{"error":{"message":"An account already exists for this email address.","code":"user_already_exists"}}', | |
| ) | |
| oauth_replay_url = ( | |
| "https://auth.example.test/api/oauth/oauth2/auth" | |
| "?client_id=client-1&state=resume-1" | |
| ) | |
| login_challenge_url = ( | |
| "https://auth.example.test/api/accounts/login?login_challenge=challenge-after-about-you" | |
| ) | |
| consent_url = "https://auth.openai.com/sign-in-with-chatgpt/codex/consent" | |
| def consent_response(): | |
| def setter(queue_session): | |
| queue_session.cookies["oai-client-auth-session"] = _workspace_cookie("ws-about-you-direct-consent") | |
| queue_session.cookies["__Secure-next-auth.session-token"] = "session-about-you-direct-consent" | |
| return DummyResponse( | |
| status_code=200, | |
| url=consent_url, | |
| text="<html>consent</html>", | |
| on_return=setter, | |
| ) | |
| session = QueueSession([ | |
| ("GET", "https://auth.example.test/flow/1", _response_with_did("did-1")), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["signup"], | |
| DummyResponse(payload={"page": {"type": OPENAI_PAGE_TYPES["LOGIN_PASSWORD"]}}), | |
| ), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["password_verify"], | |
| DummyResponse(payload={"page": {"type": OPENAI_PAGE_TYPES["EMAIL_OTP_VERIFICATION"]}}), | |
| ), | |
| ("GET", OPENAI_API_ENDPOINTS["send_otp"], DummyResponse(payload={})), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["validate_otp"], | |
| DummyResponse( | |
| payload={ | |
| "page": {"type": "about_you"}, | |
| "method": "GET", | |
| "continue_url": "https://auth.example.test/about-you", | |
| }, | |
| on_return=_response_with_login_cookies_without_workspace("session-about-you-initial").on_return, | |
| ), | |
| ), | |
| ( | |
| "GET", | |
| "https://auth.example.test/about-you", | |
| DummyResponse(status_code=200, url="https://auth.example.test/about-you", text="<html>about you</html>"), | |
| ), | |
| ("POST", OPENAI_API_ENDPOINTS["create_account"], user_exists_response), | |
| ( | |
| "GET", | |
| "https://auth.example.test/flow/1", | |
| DummyResponse( | |
| status_code=302, | |
| headers={"Location": oauth_replay_url}, | |
| ), | |
| ), | |
| ( | |
| "GET", | |
| oauth_replay_url, | |
| DummyResponse( | |
| status_code=302, | |
| headers={"Location": login_challenge_url}, | |
| ), | |
| ), | |
| ( | |
| "GET", | |
| login_challenge_url, | |
| DummyResponse( | |
| status_code=302, | |
| headers={"Location": "https://auth.example.test/log-in"}, | |
| ), | |
| ), | |
| ( | |
| "GET", | |
| "https://auth.example.test/log-in", | |
| DummyResponse( | |
| status_code=200, | |
| url="https://auth.example.test/log-in", | |
| text="<html>login</html>", | |
| ), | |
| ), | |
| ( | |
| "GET", | |
| login_challenge_url, | |
| DummyResponse( | |
| status_code=302, | |
| headers={"Location": "https://auth.example.test/log-in"}, | |
| ), | |
| ), | |
| ( | |
| "GET", | |
| "https://auth.example.test/log-in", | |
| DummyResponse( | |
| status_code=200, | |
| url="https://auth.example.test/log-in", | |
| text="<html>login again</html>", | |
| ), | |
| ), | |
| ("GET", consent_url, consent_response()), | |
| ("GET", consent_url, consent_response()), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["select_workspace"], | |
| DummyResponse(payload={"continue_url": "https://auth.example.test/consent-continue"}), | |
| ), | |
| ( | |
| "GET", | |
| "https://auth.example.test/consent-continue", | |
| DummyResponse( | |
| status_code=302, | |
| headers={"Location": "http://localhost:1455/auth/callback?code=code-about-you-consent&state=state-1"}, | |
| ), | |
| ), | |
| ]) | |
| email_service = FakeOutlookEmailService(["654321"]) | |
| engine = RegistrationEngine(email_service) | |
| engine.http_client = FakeOpenAIClient([session], ["sentinel-1"]) | |
| engine.oauth_manager = FakeOAuthManagerWithWorkspace("ws-about-you-direct-consent") | |
| result = engine.run() | |
| assert result.success is True | |
| assert result.workspace_id == "ws-about-you-direct-consent" | |
| assert result.session_token == "session-about-you-direct-consent" | |
| assert any(call["url"] == consent_url for call in session.calls) | |
| assert any(call["url"] == OPENAI_API_ENDPOINTS["select_workspace"] for call in session.calls) | |
| with manager.session_scope() as session_db: | |
| account = session_db.query(Account).filter(Account.email == "tester@example.com").first() | |
| assert account.status == "active" | |
| assert account.workspace_id == "ws-about-you-direct-consent" | |
| def test_run_recovers_outlook_account_from_direct_consent_next_data_workspace(monkeypatch): | |
| from src.database import crud | |
| from src.database.models import Account | |
| manager = _make_engine_session_manager("recoverable-about-you-direct-consent-next-data.db") | |
| with manager.session_scope() as session: | |
| crud.create_account( | |
| session, | |
| email="tester@example.com", | |
| password="saved-password", | |
| email_service="outlook", | |
| email_service_id="mailbox-1", | |
| status="failed", | |
| source="register", | |
| extra_data={ | |
| "recovery_ready": True, | |
| "account_created": True, | |
| "token_acquired": False, | |
| "register_failed_reason": "token_recovery_pending", | |
| "last_recovery_error": "获取 Workspace ID 失败", | |
| }, | |
| ) | |
| def fake_get_db(): | |
| session = manager.SessionLocal() | |
| try: | |
| yield session | |
| finally: | |
| session.close() | |
| monkeypatch.setattr("src.core.register.get_db", fake_get_db) | |
| user_exists_response = DummyResponse( | |
| status_code=400, | |
| payload={ | |
| "error": { | |
| "message": "An account already exists for this email address.", | |
| "code": "user_already_exists", | |
| } | |
| }, | |
| text='{"error":{"message":"An account already exists for this email address.","code":"user_already_exists"}}', | |
| ) | |
| oauth_replay_url = ( | |
| "https://auth.example.test/api/oauth/oauth2/auth" | |
| "?client_id=client-1&state=resume-1" | |
| ) | |
| login_challenge_url = ( | |
| "https://auth.example.test/api/accounts/login?login_challenge=challenge-after-about-you" | |
| ) | |
| consent_url = "https://auth.openai.com/sign-in-with-chatgpt/codex/consent" | |
| organization_select_url = "https://auth.openai.com/api/accounts/organization/select" | |
| session = QueueSession([ | |
| ("GET", "https://auth.example.test/flow/1", _response_with_did("did-1")), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["signup"], | |
| DummyResponse(payload={"page": {"type": OPENAI_PAGE_TYPES["LOGIN_PASSWORD"]}}), | |
| ), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["password_verify"], | |
| DummyResponse(payload={"page": {"type": OPENAI_PAGE_TYPES["EMAIL_OTP_VERIFICATION"]}}), | |
| ), | |
| ("GET", OPENAI_API_ENDPOINTS["send_otp"], DummyResponse(payload={})), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["validate_otp"], | |
| DummyResponse( | |
| payload={ | |
| "page": {"type": "about_you"}, | |
| "method": "GET", | |
| "continue_url": "https://auth.example.test/about-you", | |
| }, | |
| on_return=_response_with_login_cookies_without_workspace("session-about-you-initial").on_return, | |
| ), | |
| ), | |
| ( | |
| "GET", | |
| "https://auth.example.test/about-you", | |
| DummyResponse(status_code=200, url="https://auth.example.test/about-you", text="<html>about you</html>"), | |
| ), | |
| ("POST", OPENAI_API_ENDPOINTS["create_account"], user_exists_response), | |
| ( | |
| "GET", | |
| "https://auth.example.test/flow/1", | |
| DummyResponse( | |
| status_code=302, | |
| headers={"Location": oauth_replay_url}, | |
| ), | |
| ), | |
| ( | |
| "GET", | |
| oauth_replay_url, | |
| DummyResponse( | |
| status_code=302, | |
| headers={"Location": login_challenge_url}, | |
| ), | |
| ), | |
| ( | |
| "GET", | |
| login_challenge_url, | |
| DummyResponse( | |
| status_code=302, | |
| headers={"Location": "https://auth.example.test/log-in"}, | |
| ), | |
| ), | |
| ( | |
| "GET", | |
| "https://auth.example.test/log-in", | |
| DummyResponse( | |
| status_code=200, | |
| url="https://auth.example.test/log-in", | |
| text="<html>login</html>", | |
| ), | |
| ), | |
| ( | |
| "GET", | |
| login_challenge_url, | |
| DummyResponse( | |
| status_code=302, | |
| headers={"Location": "https://auth.example.test/log-in"}, | |
| ), | |
| ), | |
| ( | |
| "GET", | |
| "https://auth.example.test/log-in", | |
| DummyResponse( | |
| status_code=200, | |
| url="https://auth.example.test/log-in", | |
| text="<html>login again</html>", | |
| ), | |
| ), | |
| ("GET", consent_url, _response_with_consent_next_data_workspace("ws-about-you-consent-html")), | |
| ("GET", consent_url, _response_with_consent_next_data_workspace("ws-about-you-consent-html")), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["select_workspace"], | |
| DummyResponse( | |
| payload={ | |
| "continue_url": "https://auth.example.test/organization", | |
| "page": {"type": "organization_selection"}, | |
| "data": { | |
| "orgs": [ | |
| { | |
| "id": "org-consent", | |
| "projects": [{"id": "proj-consent"}], | |
| } | |
| ] | |
| }, | |
| } | |
| ), | |
| ), | |
| ( | |
| "POST", | |
| organization_select_url, | |
| DummyResponse( | |
| status_code=302, | |
| headers={"Location": "http://localhost:1455/auth/callback?code=code-about-you-consent-html&state=state-1"}, | |
| ), | |
| ), | |
| ]) | |
| email_service = FakeOutlookEmailService(["654321"]) | |
| engine = RegistrationEngine(email_service) | |
| engine.http_client = FakeOpenAIClient([session], ["sentinel-1"]) | |
| engine.oauth_manager = FakeOAuthManagerWithWorkspace("ws-about-you-consent-html") | |
| result = engine.run() | |
| assert result.success is True | |
| assert result.workspace_id == "ws-about-you-consent-html" | |
| assert result.session_token == "session-consent-next-data" | |
| assert any(call["url"] == consent_url for call in session.calls) | |
| assert any(call["url"] == OPENAI_API_ENDPOINTS["select_workspace"] for call in session.calls) | |
| assert any(call["url"] == organization_select_url for call in session.calls) | |
| with manager.session_scope() as session_db: | |
| account = session_db.query(Account).filter(Account.email == "tester@example.com").first() | |
| assert account.status == "active" | |
| assert account.workspace_id == "ws-about-you-consent-html" | |
| def test_run_recovers_outlook_account_from_direct_consent_app_router_workspace(monkeypatch): | |
| from src.database import crud | |
| from src.database.models import Account | |
| manager = _make_engine_session_manager("recoverable-about-you-direct-consent-app-router.db") | |
| with manager.session_scope() as session: | |
| crud.create_account( | |
| session, | |
| email="tester@example.com", | |
| password="saved-password", | |
| email_service="outlook", | |
| email_service_id="mailbox-1", | |
| status="failed", | |
| source="register", | |
| extra_data={ | |
| "recovery_ready": True, | |
| "account_created": True, | |
| "token_acquired": False, | |
| "register_failed_reason": "token_recovery_pending", | |
| "last_recovery_error": "鑾峰彇 Workspace ID 澶辫触", | |
| }, | |
| ) | |
| def fake_get_db(): | |
| session = manager.SessionLocal() | |
| try: | |
| yield session | |
| finally: | |
| session.close() | |
| monkeypatch.setattr("src.core.register.get_db", fake_get_db) | |
| user_exists_response = DummyResponse( | |
| status_code=400, | |
| payload={ | |
| "error": { | |
| "message": "An account already exists for this email address.", | |
| "code": "user_already_exists", | |
| } | |
| }, | |
| text='{"error":{"message":"An account already exists for this email address.","code":"user_already_exists"}}', | |
| ) | |
| oauth_replay_url = ( | |
| "https://auth.example.test/api/oauth/oauth2/auth" | |
| "?client_id=client-1&state=resume-1" | |
| ) | |
| login_challenge_url = ( | |
| "https://auth.example.test/api/accounts/login?login_challenge=challenge-after-about-you" | |
| ) | |
| consent_url = "https://auth.openai.com/sign-in-with-chatgpt/codex/consent" | |
| session = QueueSession([ | |
| ("GET", "https://auth.example.test/flow/1", _response_with_did("did-1")), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["signup"], | |
| DummyResponse(payload={"page": {"type": OPENAI_PAGE_TYPES["LOGIN_PASSWORD"]}}), | |
| ), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["password_verify"], | |
| DummyResponse(payload={"page": {"type": OPENAI_PAGE_TYPES["EMAIL_OTP_VERIFICATION"]}}), | |
| ), | |
| ("GET", OPENAI_API_ENDPOINTS["send_otp"], DummyResponse(payload={})), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["validate_otp"], | |
| DummyResponse( | |
| payload={ | |
| "page": {"type": "about_you"}, | |
| "method": "GET", | |
| "continue_url": "https://auth.example.test/about-you", | |
| }, | |
| on_return=_response_with_login_cookies_without_workspace("session-about-you-initial").on_return, | |
| ), | |
| ), | |
| ( | |
| "GET", | |
| "https://auth.example.test/about-you", | |
| DummyResponse(status_code=200, url="https://auth.example.test/about-you", text="<html>about you</html>"), | |
| ), | |
| ("POST", OPENAI_API_ENDPOINTS["create_account"], user_exists_response), | |
| ( | |
| "GET", | |
| "https://auth.example.test/flow/1", | |
| DummyResponse( | |
| status_code=302, | |
| headers={"Location": oauth_replay_url}, | |
| ), | |
| ), | |
| ( | |
| "GET", | |
| oauth_replay_url, | |
| DummyResponse( | |
| status_code=302, | |
| headers={"Location": login_challenge_url}, | |
| ), | |
| ), | |
| ( | |
| "GET", | |
| login_challenge_url, | |
| DummyResponse( | |
| status_code=302, | |
| headers={"Location": "https://auth.example.test/log-in"}, | |
| ), | |
| ), | |
| ( | |
| "GET", | |
| "https://auth.example.test/log-in", | |
| DummyResponse( | |
| status_code=200, | |
| url="https://auth.example.test/log-in", | |
| text="<html>login</html>", | |
| ), | |
| ), | |
| ( | |
| "GET", | |
| login_challenge_url, | |
| DummyResponse( | |
| status_code=302, | |
| headers={"Location": "https://auth.example.test/log-in"}, | |
| ), | |
| ), | |
| ( | |
| "GET", | |
| "https://auth.example.test/log-in", | |
| DummyResponse( | |
| status_code=200, | |
| url="https://auth.example.test/log-in", | |
| text="<html>login again</html>", | |
| ), | |
| ), | |
| ("GET", consent_url, _response_with_consent_app_router_workspace("ws-about-you-consent-rsc")), | |
| ("GET", consent_url, _response_with_consent_app_router_workspace("ws-about-you-consent-rsc")), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["select_workspace"], | |
| DummyResponse(payload={"continue_url": "https://auth.example.test/consent-continue"}), | |
| ), | |
| ( | |
| "GET", | |
| "https://auth.example.test/consent-continue", | |
| DummyResponse( | |
| status_code=302, | |
| headers={"Location": "http://localhost:1455/auth/callback?code=code-about-you-consent-rsc&state=state-1"}, | |
| ), | |
| ), | |
| ]) | |
| email_service = FakeOutlookEmailService(["654321"]) | |
| engine = RegistrationEngine(email_service) | |
| engine.http_client = FakeOpenAIClient([session], ["sentinel-1"]) | |
| engine.oauth_manager = FakeOAuthManagerWithWorkspace("ws-about-you-consent-rsc") | |
| result = engine.run() | |
| assert result.success is True | |
| assert result.workspace_id == "ws-about-you-consent-rsc" | |
| assert result.session_token == "session-consent-app-router" | |
| assert any(call["url"] == consent_url for call in session.calls) | |
| assert any(call["url"] == OPENAI_API_ENDPOINTS["select_workspace"] for call in session.calls) | |
| with manager.session_scope() as session_db: | |
| account = session_db.query(Account).filter(Account.email == "tester@example.com").first() | |
| assert account.status == "active" | |
| assert account.workspace_id == "ws-about-you-consent-rsc" | |
| def test_run_retries_about_you_submission_after_relogin_when_second_session_still_requires_it(monkeypatch): | |
| from src.database import crud | |
| from src.database.models import Account | |
| manager = _make_engine_session_manager("recoverable-about-you-repeat-after-relogin.db") | |
| with manager.session_scope() as session: | |
| crud.create_account( | |
| session, | |
| email="tester@example.com", | |
| password="saved-password", | |
| email_service="outlook", | |
| email_service_id="mailbox-1", | |
| status="failed", | |
| source="register", | |
| extra_data={ | |
| "recovery_ready": True, | |
| "account_created": True, | |
| "token_acquired": False, | |
| "register_failed_reason": "token_recovery_pending", | |
| "last_recovery_error": "获取 Workspace ID 失败", | |
| }, | |
| ) | |
| def fake_get_db(): | |
| session = manager.SessionLocal() | |
| try: | |
| yield session | |
| finally: | |
| session.close() | |
| monkeypatch.setattr("src.core.register.get_db", fake_get_db) | |
| session = QueueSession([ | |
| ("GET", "https://auth.example.test/flow/1", _response_with_did("did-1")), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["signup"], | |
| DummyResponse( | |
| payload={ | |
| "page": {"type": OPENAI_PAGE_TYPES["LOGIN_PASSWORD"]}, | |
| "method": "POST", | |
| "continue_url": "https://auth.example.test/log-in/password", | |
| } | |
| ), | |
| ), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["password_verify"], | |
| DummyResponse( | |
| payload={ | |
| "page": {"type": OPENAI_PAGE_TYPES["EMAIL_OTP_VERIFICATION"]}, | |
| "method": "POST", | |
| "continue_url": "https://auth.example.test/email-verification", | |
| } | |
| ), | |
| ), | |
| ("GET", OPENAI_API_ENDPOINTS["send_otp"], DummyResponse(payload={})), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["validate_otp"], | |
| DummyResponse( | |
| payload={ | |
| "page": {"type": OPENAI_PAGE_TYPES["EMAIL_OTP_VERIFICATION"]}, | |
| "method": "GET", | |
| "continue_url": "https://auth.example.test/about-you", | |
| } | |
| ), | |
| ), | |
| ( | |
| "GET", | |
| "https://auth.example.test/about-you", | |
| DummyResponse(status_code=200, url="https://auth.example.test/about-you", text="<html>about you</html>"), | |
| ), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["create_account"], | |
| DummyResponse( | |
| status_code=400, | |
| payload={ | |
| "error": { | |
| "message": "An account already exists for this email address.", | |
| "code": "user_already_exists", | |
| } | |
| }, | |
| text='{"error":{"message":"An account already exists for this email address.","code":"user_already_exists"}}', | |
| ), | |
| ), | |
| ( | |
| "GET", | |
| "https://auth.example.test/flow/1", | |
| DummyResponse( | |
| status_code=302, | |
| headers={"Location": "https://auth.example.test/log-in"}, | |
| ), | |
| ), | |
| ( | |
| "GET", | |
| "https://auth.example.test/log-in", | |
| DummyResponse( | |
| status_code=200, | |
| url="https://auth.example.test/log-in", | |
| text="<html>login</html>", | |
| ), | |
| ), | |
| ("GET", "https://auth.example.test/flow/2", _response_with_did("did-2")), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["signup"], | |
| DummyResponse(payload={"page": {"type": OPENAI_PAGE_TYPES["LOGIN_PASSWORD"]}}), | |
| ), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["password_verify"], | |
| DummyResponse(payload={"page": {"type": OPENAI_PAGE_TYPES["EMAIL_OTP_VERIFICATION"]}}), | |
| ), | |
| ("GET", OPENAI_API_ENDPOINTS["send_otp"], DummyResponse(payload={})), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["validate_otp"], | |
| DummyResponse( | |
| payload={ | |
| "page": {"type": OPENAI_PAGE_TYPES["EMAIL_OTP_VERIFICATION"]}, | |
| "method": "GET", | |
| "continue_url": "https://auth.example.test/about-you", | |
| }, | |
| on_return=lambda queue_session: queue_session.cookies.__setitem__( | |
| "__Secure-next-auth.session-token", | |
| "session-about-you-repeat", | |
| ), | |
| ), | |
| ), | |
| ( | |
| "GET", | |
| "https://auth.example.test/about-you", | |
| DummyResponse(status_code=200, url="https://auth.example.test/about-you", text="<html>about you again</html>"), | |
| ), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["create_account"], | |
| DummyResponse( | |
| status_code=400, | |
| payload={ | |
| "error": { | |
| "message": "An account already exists for this email address.", | |
| "code": "user_already_exists", | |
| }, | |
| "callback_url": "http://localhost:1455/auth/callback?code=code-about-you-repeat&state=state-2", | |
| }, | |
| text='{"error":{"message":"An account already exists for this email address.","code":"user_already_exists"}}', | |
| ), | |
| ), | |
| ]) | |
| email_service = FakeOutlookEmailService(["123456", "654321"]) | |
| engine = RegistrationEngine(email_service) | |
| engine.http_client = FakeOpenAIClient([session], ["sentinel-1", "sentinel-2"]) | |
| engine.oauth_manager = FakeOAuthManagerWithWorkspace("ws-about-you-repeat") | |
| result = engine.run() | |
| assert result.success is True | |
| assert result.workspace_id == "ws-about-you-repeat" | |
| assert result.session_token == "session-about-you-repeat" | |
| assert len([call for call in session.calls if call["url"] == OPENAI_API_ENDPOINTS["create_account"]]) == 2 | |
| with manager.session_scope() as session_db: | |
| account = session_db.query(Account).filter(Account.email == "tester@example.com").first() | |
| assert account.status == "active" | |
| assert account.workspace_id == "ws-about-you-repeat" | |
| def test_run_recovers_outlook_account_from_about_you_next_data_callback(monkeypatch): | |
| from src.database import crud | |
| from src.database.models import Account | |
| manager = _make_engine_session_manager("recoverable-about-you-next-data-callback.db") | |
| with manager.session_scope() as session: | |
| crud.create_account( | |
| session, | |
| email="tester@example.com", | |
| password="saved-password", | |
| email_service="outlook", | |
| email_service_id="mailbox-1", | |
| status="failed", | |
| source="register", | |
| extra_data={ | |
| "recovery_ready": True, | |
| "account_created": True, | |
| "token_acquired": False, | |
| "register_failed_reason": "token_recovery_pending", | |
| "last_recovery_error": "获取 Workspace ID 失败", | |
| }, | |
| ) | |
| def fake_get_db(): | |
| session = manager.SessionLocal() | |
| try: | |
| yield session | |
| finally: | |
| session.close() | |
| monkeypatch.setattr("src.core.register.get_db", fake_get_db) | |
| session = QueueSession([ | |
| ("GET", "https://auth.example.test/flow/1", _response_with_did("did-1")), | |
| ("POST", OPENAI_API_ENDPOINTS["signup"], DummyResponse(payload={"page": {"type": OPENAI_PAGE_TYPES["LOGIN_PASSWORD"]}})), | |
| ("POST", OPENAI_API_ENDPOINTS["password_verify"], DummyResponse(payload={"page": {"type": OPENAI_PAGE_TYPES["EMAIL_OTP_VERIFICATION"]}})), | |
| ("GET", OPENAI_API_ENDPOINTS["send_otp"], DummyResponse(payload={})), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["validate_otp"], | |
| DummyResponse( | |
| payload={ | |
| "page": {"type": OPENAI_PAGE_TYPES["EMAIL_OTP_VERIFICATION"]}, | |
| "method": "GET", | |
| "continue_url": "https://auth.example.test/about-you", | |
| } | |
| ), | |
| ), | |
| ("GET", "https://auth.example.test/about-you", _response_with_about_you_next_data_callback()), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["create_account"], | |
| DummyResponse( | |
| status_code=400, | |
| payload={ | |
| "error": { | |
| "message": "An account already exists for this email address.", | |
| "code": "user_already_exists", | |
| } | |
| }, | |
| text='{"error":{"message":"An account already exists for this email address.","code":"user_already_exists"}}', | |
| ), | |
| ), | |
| ]) | |
| email_service = FakeOutlookEmailService(["123456"]) | |
| engine = RegistrationEngine(email_service) | |
| engine.http_client = FakeOpenAIClient([session], ["sentinel-1"]) | |
| engine.oauth_manager = FakeOAuthManagerWithWorkspace("ws-about-you-next-data") | |
| result = engine.run() | |
| assert result.success is True | |
| assert result.workspace_id == "ws-about-you-next-data" | |
| assert len([call for call in session.calls if call["url"] == OPENAI_API_ENDPOINTS["validate_otp"]]) == 1 | |
| assert result.metadata["last_oauth_resume_source"] in { | |
| "callback_found_from_about_you_page_text_1", | |
| "callback_found_from_cached_navigation", | |
| } | |
| with manager.session_scope() as session_db: | |
| account = session_db.query(Account).filter(Account.email == "tester@example.com").first() | |
| assert account.status == "active" | |
| assert account.workspace_id == "ws-about-you-next-data" | |
| def test_run_recovers_outlook_account_from_about_you_next_data_resume(monkeypatch): | |
| from src.database import crud | |
| from src.database.models import Account | |
| manager = _make_engine_session_manager("recoverable-about-you-next-data-resume.db") | |
| with manager.session_scope() as session: | |
| crud.create_account( | |
| session, | |
| email="tester@example.com", | |
| password="saved-password", | |
| email_service="outlook", | |
| email_service_id="mailbox-1", | |
| status="failed", | |
| source="register", | |
| extra_data={ | |
| "recovery_ready": True, | |
| "account_created": True, | |
| "token_acquired": False, | |
| "register_failed_reason": "token_recovery_pending", | |
| "last_recovery_error": "获取 Workspace ID 失败", | |
| }, | |
| ) | |
| def fake_get_db(): | |
| session = manager.SessionLocal() | |
| try: | |
| yield session | |
| finally: | |
| session.close() | |
| monkeypatch.setattr("src.core.register.get_db", fake_get_db) | |
| resume_url = "https://auth.example.test/api/oauth/oauth2/auth?client_id=client-1&state=resume-about-you" | |
| session = QueueSession([ | |
| ("GET", "https://auth.example.test/flow/1", _response_with_did("did-1")), | |
| ("POST", OPENAI_API_ENDPOINTS["signup"], DummyResponse(payload={"page": {"type": OPENAI_PAGE_TYPES["LOGIN_PASSWORD"]}})), | |
| ("POST", OPENAI_API_ENDPOINTS["password_verify"], DummyResponse(payload={"page": {"type": OPENAI_PAGE_TYPES["EMAIL_OTP_VERIFICATION"]}})), | |
| ("GET", OPENAI_API_ENDPOINTS["send_otp"], DummyResponse(payload={})), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["validate_otp"], | |
| DummyResponse( | |
| payload={ | |
| "page": {"type": OPENAI_PAGE_TYPES["EMAIL_OTP_VERIFICATION"]}, | |
| "method": "GET", | |
| "continue_url": "https://auth.example.test/about-you", | |
| } | |
| ), | |
| ), | |
| ("GET", "https://auth.example.test/about-you", _response_with_about_you_next_data_resume(resume_url)), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["create_account"], | |
| DummyResponse( | |
| status_code=400, | |
| payload={ | |
| "error": { | |
| "message": "An account already exists for this email address.", | |
| "code": "user_already_exists", | |
| } | |
| }, | |
| text='{"error":{"message":"An account already exists for this email address.","code":"user_already_exists"}}', | |
| ), | |
| ), | |
| ( | |
| "GET", | |
| resume_url, | |
| DummyResponse( | |
| status_code=302, | |
| headers={"Location": "http://localhost:1455/auth/callback?code=code-about-you-resume&state=state-1"}, | |
| ), | |
| ), | |
| ]) | |
| email_service = FakeOutlookEmailService(["123456"]) | |
| engine = RegistrationEngine(email_service) | |
| engine.http_client = FakeOpenAIClient([session], ["sentinel-1"]) | |
| engine.oauth_manager = FakeOAuthManagerWithWorkspace("ws-about-you-next-resume") | |
| result = engine.run() | |
| assert result.success is True | |
| assert result.workspace_id == "ws-about-you-next-resume" | |
| assert any(call["url"] == resume_url for call in session.calls) | |
| assert len([call for call in session.calls if call["url"] == OPENAI_API_ENDPOINTS["validate_otp"]]) == 1 | |
| with manager.session_scope() as session_db: | |
| account = session_db.query(Account).filter(Account.email == "tester@example.com").first() | |
| assert account.status == "active" | |
| assert account.workspace_id == "ws-about-you-next-resume" | |
| def test_run_stops_relogin_after_repeated_about_you_user_exists_without_resume_evidence(monkeypatch): | |
| from src.database import crud | |
| from src.database.models import Account | |
| manager = _make_engine_session_manager("recoverable-about-you-user-exists-exhausted.db") | |
| with manager.session_scope() as session: | |
| crud.create_account( | |
| session, | |
| email="tester@example.com", | |
| password="saved-password", | |
| email_service="outlook", | |
| email_service_id="mailbox-1", | |
| status="failed", | |
| source="register", | |
| extra_data={ | |
| "recovery_ready": True, | |
| "account_created": True, | |
| "token_acquired": False, | |
| "register_failed_reason": "token_recovery_pending", | |
| "last_recovery_error": "获取 Workspace ID 失败", | |
| }, | |
| ) | |
| def fake_get_db(): | |
| session = manager.SessionLocal() | |
| try: | |
| yield session | |
| finally: | |
| session.close() | |
| monkeypatch.setattr("src.core.register.get_db", fake_get_db) | |
| user_exists_response = DummyResponse( | |
| status_code=400, | |
| payload={ | |
| "error": { | |
| "message": "An account already exists for this email address.", | |
| "code": "user_already_exists", | |
| } | |
| }, | |
| text='{"error":{"message":"An account already exists for this email address.","code":"user_already_exists"}}', | |
| ) | |
| session = QueueSession([ | |
| ("GET", "https://auth.example.test/flow/1", _response_with_did("did-1")), | |
| ("POST", OPENAI_API_ENDPOINTS["signup"], DummyResponse(payload={"page": {"type": OPENAI_PAGE_TYPES["LOGIN_PASSWORD"]}})), | |
| ("POST", OPENAI_API_ENDPOINTS["password_verify"], DummyResponse(payload={"page": {"type": OPENAI_PAGE_TYPES["EMAIL_OTP_VERIFICATION"]}})), | |
| ("GET", OPENAI_API_ENDPOINTS["send_otp"], DummyResponse(payload={})), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["validate_otp"], | |
| DummyResponse( | |
| payload={ | |
| "page": {"type": OPENAI_PAGE_TYPES["EMAIL_OTP_VERIFICATION"]}, | |
| "method": "GET", | |
| "continue_url": "https://auth.example.test/about-you", | |
| } | |
| ), | |
| ), | |
| ("GET", "https://auth.example.test/about-you", DummyResponse(status_code=200, url="https://auth.example.test/about-you", text="<html>about you</html>")), | |
| ("POST", OPENAI_API_ENDPOINTS["create_account"], user_exists_response), | |
| ( | |
| "GET", | |
| "https://auth.example.test/flow/1", | |
| DummyResponse( | |
| status_code=302, | |
| headers={"Location": "https://auth.example.test/log-in"}, | |
| ), | |
| ), | |
| ( | |
| "GET", | |
| "https://auth.example.test/log-in", | |
| DummyResponse( | |
| status_code=200, | |
| url="https://auth.example.test/log-in", | |
| text="<html>login</html>", | |
| ), | |
| ), | |
| ("GET", "https://auth.example.test/flow/2", _response_with_did("did-2")), | |
| ("POST", OPENAI_API_ENDPOINTS["signup"], DummyResponse(payload={"page": {"type": OPENAI_PAGE_TYPES["LOGIN_PASSWORD"]}})), | |
| ("POST", OPENAI_API_ENDPOINTS["password_verify"], DummyResponse(payload={"page": {"type": OPENAI_PAGE_TYPES["EMAIL_OTP_VERIFICATION"]}})), | |
| ("GET", OPENAI_API_ENDPOINTS["send_otp"], DummyResponse(payload={})), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["validate_otp"], | |
| DummyResponse( | |
| payload={ | |
| "page": {"type": OPENAI_PAGE_TYPES["EMAIL_OTP_VERIFICATION"]}, | |
| "method": "GET", | |
| "continue_url": "https://auth.example.test/about-you", | |
| } | |
| ), | |
| ), | |
| ("GET", "https://auth.example.test/about-you", DummyResponse(status_code=200, url="https://auth.example.test/about-you", text="<html>about you again</html>")), | |
| ("POST", OPENAI_API_ENDPOINTS["create_account"], user_exists_response), | |
| ( | |
| "GET", | |
| "https://auth.example.test/flow/2", | |
| DummyResponse( | |
| status_code=302, | |
| headers={"Location": "https://auth.example.test/log-in"}, | |
| ), | |
| ), | |
| ( | |
| "GET", | |
| "https://auth.example.test/log-in", | |
| DummyResponse( | |
| status_code=200, | |
| url="https://auth.example.test/log-in", | |
| text="<html>login again</html>", | |
| ), | |
| ), | |
| ]) | |
| email_service = FakeOutlookEmailService(["123456", "654321"]) | |
| engine = RegistrationEngine(email_service) | |
| engine.http_client = FakeOpenAIClient([session], ["sentinel-1", "sentinel-2"]) | |
| engine.oauth_manager = FakeOAuthManager() | |
| result = engine.run() | |
| assert result.success is False | |
| assert result.error_message == "about-you 返回 user_already_exists,但连续两次未暴露 callback/workspace" | |
| assert len([call for call in session.calls if call["url"] == OPENAI_API_ENDPOINTS["validate_otp"]]) == 2 | |
| assert any("停止重复登录" in log for log in engine.logs) | |
| with manager.session_scope() as session_db: | |
| account = session_db.query(Account).filter(Account.email == "tester@example.com").first() | |
| assert account.status == "failed" | |
| assert account.extra_data["last_recovery_error"] == result.error_message | |
| assert account.extra_data["last_oauth_resume_source"] == "about_you_user_exists_without_resume_exhausted" | |
| assert account.extra_data["last_workspace_resolution_source"] == "about_you_user_exists_without_resume" | |
| def test_run_recovers_outlook_account_from_authorize_replay_login_challenge(monkeypatch): | |
| from src.database import crud | |
| from src.database.models import Account | |
| manager = _make_engine_session_manager("recoverable-authorize-login-challenge-success.db") | |
| with manager.session_scope() as session: | |
| crud.create_account( | |
| session, | |
| email="tester@example.com", | |
| password="saved-password", | |
| email_service="outlook", | |
| email_service_id="mailbox-1", | |
| status="failed", | |
| source="register", | |
| extra_data={ | |
| "recovery_ready": True, | |
| "account_created": True, | |
| "token_acquired": False, | |
| "register_failed_reason": "token_recovery_pending", | |
| "last_recovery_error": "获取 Workspace ID 失败", | |
| }, | |
| ) | |
| def fake_get_db(): | |
| session = manager.SessionLocal() | |
| try: | |
| yield session | |
| finally: | |
| session.close() | |
| monkeypatch.setattr("src.core.register.get_db", fake_get_db) | |
| session = QueueSession([ | |
| ("GET", "https://auth.example.test/flow/1", _response_with_did("did-1")), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["signup"], | |
| DummyResponse(payload={"page": {"type": OPENAI_PAGE_TYPES["LOGIN_PASSWORD"]}}), | |
| ), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["password_verify"], | |
| DummyResponse(payload={"page": {"type": OPENAI_PAGE_TYPES["EMAIL_OTP_VERIFICATION"]}}), | |
| ), | |
| ("GET", OPENAI_API_ENDPOINTS["send_otp"], DummyResponse(payload={})), | |
| ("POST", OPENAI_API_ENDPOINTS["validate_otp"], _response_with_login_cookies_without_workspace()), | |
| ( | |
| "GET", | |
| "https://auth.example.test/flow/1", | |
| DummyResponse( | |
| status_code=302, | |
| headers={"Location": "https://auth.example.test/login-challenge?login_challenge=replay-success"}, | |
| ), | |
| ), | |
| ( | |
| "GET", | |
| "https://auth.example.test/login-challenge?login_challenge=replay-success", | |
| DummyResponse( | |
| status_code=302, | |
| headers={"Location": "http://localhost:1455/auth/callback?code=code-1&state=state-1"}, | |
| ), | |
| ), | |
| ]) | |
| email_service = FakeOutlookEmailService(["789012"]) | |
| engine = RegistrationEngine(email_service) | |
| engine.http_client = FakeOpenAIClient([session], ["sentinel-1"]) | |
| engine.oauth_manager = FakeOAuthManagerWithWorkspace("ws-authorize-replay") | |
| result = engine.run() | |
| assert result.success is True | |
| assert result.workspace_id == "ws-authorize-replay" | |
| assert result.metadata["last_oauth_resume_source"] == "callback_found_from_authorize_replay" | |
| assert "callback_found_in_redirect_chain" in (result.metadata["last_recovery_debug_summary"] or "") | |
| assert "%3Credacted%3E" in (result.metadata["last_recovery_debug_summary"] or "") | |
| with manager.session_scope() as session_db: | |
| account = session_db.query(Account).filter(Account.email == "tester@example.com").first() | |
| assert account.status == "active" | |
| assert account.workspace_id == "ws-authorize-replay" | |
| def test_submit_login_start_omits_screen_hint_like_login_web(): | |
| session = QueueSession([ | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["signup"], | |
| DummyResponse(payload={"page": {"type": OPENAI_PAGE_TYPES["LOGIN_PASSWORD"]}}), | |
| ), | |
| ]) | |
| email_service = FakeEmailService([]) | |
| engine = RegistrationEngine(email_service) | |
| engine.session = session | |
| engine.email = "tester@example.com" | |
| result = engine._submit_login_start("did-1", "sentinel-1") | |
| assert result.success is True | |
| payload = json.loads(session.calls[0]["kwargs"]["data"]) | |
| assert payload == { | |
| "username": { | |
| "value": "tester@example.com", | |
| "kind": "email", | |
| }, | |
| } | |
| def test_start_saved_password_recovery_uses_login_screen_hint(): | |
| session = QueueSession([ | |
| ("GET", "https://auth.example.test/flow/1", _response_with_did("did-1")), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["signup"], | |
| DummyResponse(payload={"page": {"type": OPENAI_PAGE_TYPES["LOGIN_PASSWORD"]}}), | |
| ), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["password_verify"], | |
| DummyResponse(payload={"page": {"type": OPENAI_PAGE_TYPES["EMAIL_OTP_VERIFICATION"]}}), | |
| ), | |
| ("GET", OPENAI_API_ENDPOINTS["send_otp"], DummyResponse(payload={})), | |
| ]) | |
| email_service = FakeOutlookEmailService([]) | |
| engine = RegistrationEngine(email_service) | |
| engine.email = "tester@example.com" | |
| engine.password = "saved-password" | |
| engine.http_client = FakeOpenAIClient([session], ["sentinel-1"]) | |
| engine.oauth_manager = FakeOAuthManager() | |
| ok, error = engine._start_saved_password_recovery() | |
| assert ok is True | |
| assert error == "" | |
| payload = json.loads(session.calls[1]["kwargs"]["data"]) | |
| assert payload["screen_hint"] == "login" | |
| def test_run_recovers_outlook_account_with_session_bound_reauth_after_bare_login(monkeypatch): | |
| from src.database import crud | |
| from src.database.models import Account | |
| manager = _make_engine_session_manager("recoverable-session-bound-reauth-success.db") | |
| with manager.session_scope() as session: | |
| crud.create_account( | |
| session, | |
| email="tester@example.com", | |
| password="saved-password", | |
| email_service="outlook", | |
| email_service_id="mailbox-1", | |
| status="failed", | |
| source="register", | |
| extra_data={ | |
| "recovery_ready": True, | |
| "account_created": True, | |
| "token_acquired": False, | |
| "register_failed_reason": "token_recovery_pending", | |
| "last_recovery_error": "获取 Workspace ID 失败", | |
| }, | |
| ) | |
| def fake_get_db(): | |
| session = manager.SessionLocal() | |
| try: | |
| yield session | |
| finally: | |
| session.close() | |
| monkeypatch.setattr("src.core.register.get_db", fake_get_db) | |
| session = QueueSession([ | |
| ("GET", "https://auth.example.test/flow/1", _response_with_did("did-1")), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["signup"], | |
| DummyResponse(payload={"page": {"type": OPENAI_PAGE_TYPES["LOGIN_PASSWORD"]}}), | |
| ), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["password_verify"], | |
| DummyResponse(payload={"page": {"type": OPENAI_PAGE_TYPES["EMAIL_OTP_VERIFICATION"]}}), | |
| ), | |
| ("GET", OPENAI_API_ENDPOINTS["send_otp"], DummyResponse(payload={})), | |
| ("POST", OPENAI_API_ENDPOINTS["validate_otp"], _response_with_login_cookies_without_workspace()), | |
| ( | |
| "GET", | |
| "https://auth.example.test/flow/1", | |
| DummyResponse( | |
| status_code=302, | |
| headers={"Location": "https://auth.example.test/login-challenge?login_challenge=reauth-1"}, | |
| ), | |
| ), | |
| ( | |
| "GET", | |
| "https://auth.example.test/login-challenge?login_challenge=reauth-1", | |
| DummyResponse( | |
| status_code=302, | |
| headers={"Location": "https://auth.example.test/log-in"}, | |
| ), | |
| ), | |
| ( | |
| "GET", | |
| "https://auth.example.test/log-in", | |
| DummyResponse(status_code=200, payload={}, url="https://auth.example.test/log-in"), | |
| ), | |
| ( | |
| "GET", | |
| "https://auth.example.test/login-challenge?login_challenge=reauth-1", | |
| DummyResponse( | |
| status_code=302, | |
| headers={"Location": "https://auth.example.test/log-in"}, | |
| ), | |
| ), | |
| ( | |
| "GET", | |
| "https://auth.example.test/log-in", | |
| DummyResponse(status_code=200, payload={}, url="https://auth.example.test/log-in"), | |
| ), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["signup"], | |
| DummyResponse(payload={"page": {"type": OPENAI_PAGE_TYPES["LOGIN_PASSWORD"]}}), | |
| ), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["password_verify"], | |
| DummyResponse(payload={"page": {"type": OPENAI_PAGE_TYPES["EMAIL_OTP_VERIFICATION"]}}), | |
| ), | |
| ("GET", OPENAI_API_ENDPOINTS["send_otp"], DummyResponse(payload={})), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["validate_otp"], | |
| _response_with_callback_before_workspace( | |
| callback_url="http://localhost:1455/auth/callback?code=code-reauth&state=state-reauth", | |
| session_token="session-reauth", | |
| ), | |
| ), | |
| ]) | |
| email_service = FakeOutlookEmailService(["123456", "654321"]) | |
| engine = RegistrationEngine(email_service) | |
| engine.http_client = FakeOpenAIClient([session], ["sentinel-1", "sentinel-2"]) | |
| engine.oauth_manager = FakeOAuthManagerWithWorkspace("ws-session-reauth") | |
| result = engine.run() | |
| assert result.success is True | |
| assert result.workspace_id == "ws-session-reauth" | |
| assert result.session_token == "session-reauth" | |
| assert result.metadata["last_oauth_resume_source"] == "session_bound_reauth" | |
| assert "session_bound_reauth_callback_resolved" in (result.metadata["last_recovery_debug_summary"] or "") | |
| assert len([call for call in session.calls if call["url"] == OPENAI_API_ENDPOINTS["validate_otp"]]) == 2 | |
| assert [item["stage"] for item in email_service.otp_requests] == ["relogin_otp", "relogin_otp"] | |
| with manager.session_scope() as session_db: | |
| account = session_db.query(Account).filter(Account.email == "tester@example.com").first() | |
| assert account.status == "active" | |
| assert account.workspace_id == "ws-session-reauth" | |
| def test_run_reports_reentered_login_page_when_fresh_authorize_replay_loops_back_to_log_in(monkeypatch): | |
| from src.database.models import Account | |
| manager = _make_engine_session_manager("recoverable-authorize-replay-fail.db") | |
| def fake_get_db(): | |
| session = manager.SessionLocal() | |
| try: | |
| yield session | |
| finally: | |
| session.close() | |
| monkeypatch.setattr("src.core.register.get_db", fake_get_db) | |
| session_one = QueueSession([ | |
| ("GET", "https://auth.example.test/flow/1", _response_with_did("did-1")), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["signup"], | |
| DummyResponse(payload={"page": {"type": OPENAI_PAGE_TYPES["PASSWORD_REGISTRATION"]}}), | |
| ), | |
| ("POST", OPENAI_API_ENDPOINTS["register"], DummyResponse(payload={})), | |
| ("GET", OPENAI_API_ENDPOINTS["send_otp"], DummyResponse(payload={})), | |
| ("POST", OPENAI_API_ENDPOINTS["validate_otp"], DummyResponse(payload={})), | |
| ("POST", OPENAI_API_ENDPOINTS["create_account"], DummyResponse(payload={})), | |
| ]) | |
| session_two = QueueSession([ | |
| ("GET", "https://auth.example.test/flow/2", _response_with_did("did-2")), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["signup"], | |
| DummyResponse(payload={"page": {"type": OPENAI_PAGE_TYPES["LOGIN_PASSWORD"]}}), | |
| ), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["password_verify"], | |
| DummyResponse(payload={"page": {"type": OPENAI_PAGE_TYPES["EMAIL_OTP_VERIFICATION"]}}), | |
| ), | |
| ("GET", OPENAI_API_ENDPOINTS["send_otp"], DummyResponse(payload={})), | |
| ("POST", OPENAI_API_ENDPOINTS["validate_otp"], _response_with_login_cookies_without_workspace()), | |
| ( | |
| "GET", | |
| "https://auth.example.test/flow/2", | |
| DummyResponse( | |
| status_code=302, | |
| headers={"Location": "https://auth.example.test/login-challenge?login_challenge=replay-1"}, | |
| ), | |
| ), | |
| ( | |
| "GET", | |
| "https://auth.example.test/login-challenge?login_challenge=replay-1", | |
| DummyResponse( | |
| status_code=302, | |
| headers={"Location": "https://auth.example.test/log-in"}, | |
| ), | |
| ), | |
| ( | |
| "GET", | |
| "https://auth.example.test/log-in", | |
| DummyResponse(status_code=200, payload={}, url="https://auth.example.test/log-in"), | |
| ), | |
| ( | |
| "GET", | |
| "https://auth.example.test/login-challenge?login_challenge=replay-1", | |
| DummyResponse( | |
| status_code=302, | |
| headers={"Location": "https://auth.example.test/log-in"}, | |
| ), | |
| ), | |
| ( | |
| "GET", | |
| "https://auth.example.test/log-in", | |
| DummyResponse(status_code=200, payload={}, url="https://auth.example.test/log-in"), | |
| ), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["signup"], | |
| DummyResponse(payload={"page": {"type": OPENAI_PAGE_TYPES["LOGIN_PASSWORD"]}}), | |
| ), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["password_verify"], | |
| DummyResponse(payload={"page": {"type": OPENAI_PAGE_TYPES["EMAIL_OTP_VERIFICATION"]}}), | |
| ), | |
| ("GET", OPENAI_API_ENDPOINTS["send_otp"], DummyResponse(payload={})), | |
| ("POST", OPENAI_API_ENDPOINTS["validate_otp"], _response_with_login_cookies_without_workspace()), | |
| ( | |
| "GET", | |
| "https://auth.example.test/flow/2", | |
| DummyResponse( | |
| status_code=302, | |
| headers={"Location": "https://auth.example.test/login-challenge?login_challenge=replay-2"}, | |
| ), | |
| ), | |
| ( | |
| "GET", | |
| "https://auth.example.test/login-challenge?login_challenge=replay-2", | |
| DummyResponse( | |
| status_code=302, | |
| headers={"Location": "https://auth.example.test/log-in"}, | |
| ), | |
| ), | |
| ( | |
| "GET", | |
| "https://auth.example.test/log-in", | |
| DummyResponse(status_code=200, payload={}, url="https://auth.example.test/log-in"), | |
| ), | |
| ( | |
| "GET", | |
| "https://auth.example.test/login-challenge?login_challenge=replay-2", | |
| DummyResponse( | |
| status_code=302, | |
| headers={"Location": "https://auth.example.test/log-in"}, | |
| ), | |
| ), | |
| ( | |
| "GET", | |
| "https://auth.example.test/log-in", | |
| DummyResponse(status_code=200, payload={}, url="https://auth.example.test/log-in"), | |
| ), | |
| ]) | |
| email_service = FakeOutlookEmailService(["123456", "654321", "777888"]) | |
| engine = RegistrationEngine(email_service) | |
| engine.http_client = FakeOpenAIClient( | |
| [session_one, session_two], | |
| ["sentinel-1", "sentinel-2", "sentinel-3"], | |
| ) | |
| engine.oauth_manager = FakeOAuthManager() | |
| result = engine.run() | |
| assert result.success is False | |
| assert result.error_message == "OAuth 恢复链路重新进入登录页" | |
| with manager.session_scope() as session_db: | |
| account = session_db.query(Account).filter(Account.email == "tester@example.com").first() | |
| extra_data = dict(account.extra_data or {}) | |
| assert extra_data["recovery_ready"] is True | |
| assert extra_data["last_recovery_error"] == "OAuth 恢复链路重新进入登录页" | |
| assert extra_data["last_oauth_resume_source"] == "session_bound_reauth_reentered_login" | |
| assert "session_bound_reauth_reentered_login" in (extra_data.get("last_recovery_debug_summary") or "") | |
| def test_run_records_workspace_resolution_metadata_when_all_sources_missing(monkeypatch): | |
| from src.database.models import Account | |
| manager = _make_engine_session_manager("recoverable-workspace-metadata.db") | |
| def fake_get_db(): | |
| session = manager.SessionLocal() | |
| try: | |
| yield session | |
| finally: | |
| session.close() | |
| monkeypatch.setattr("src.core.register.get_db", fake_get_db) | |
| session_one = QueueSession([ | |
| ("GET", "https://auth.example.test/flow/1", _response_with_did("did-1")), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["signup"], | |
| DummyResponse(payload={"page": {"type": OPENAI_PAGE_TYPES["PASSWORD_REGISTRATION"]}}), | |
| ), | |
| ("POST", OPENAI_API_ENDPOINTS["register"], DummyResponse(payload={})), | |
| ("GET", OPENAI_API_ENDPOINTS["send_otp"], DummyResponse(payload={})), | |
| ("POST", OPENAI_API_ENDPOINTS["validate_otp"], DummyResponse(payload={})), | |
| ("POST", OPENAI_API_ENDPOINTS["create_account"], DummyResponse(payload={})), | |
| ]) | |
| session_two = QueueSession([ | |
| ("GET", "https://auth.example.test/flow/2", _response_with_did("did-2")), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["signup"], | |
| DummyResponse(payload={"page": {"type": OPENAI_PAGE_TYPES["LOGIN_PASSWORD"]}}), | |
| ), | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["password_verify"], | |
| DummyResponse(payload={"page": {"type": OPENAI_PAGE_TYPES["EMAIL_OTP_VERIFICATION"]}}), | |
| ), | |
| ("GET", OPENAI_API_ENDPOINTS["send_otp"], DummyResponse(payload={})), | |
| ("POST", OPENAI_API_ENDPOINTS["validate_otp"], _response_with_login_cookies_without_workspace()), | |
| ]) | |
| email_service = FakeOutlookEmailService(["123456", "654321"]) | |
| engine = RegistrationEngine(email_service) | |
| engine.http_client = FakeOpenAIClient([session_one, session_two], ["sentinel-1", "sentinel-2"]) | |
| engine.oauth_manager = FakeOAuthManager() | |
| result = engine.run() | |
| assert result.success is False | |
| with manager.session_scope() as session: | |
| account = session.query(Account).filter(Account.email == "tester@example.com").first() | |
| extra_data = dict(account.extra_data or {}) | |
| assert extra_data["workspace_acquired"] is False | |
| assert extra_data["last_workspace_resolution_source"] == "auth_cookie_decoded_without_workspace" | |
| assert extra_data["last_workspace_resolution_error"] == "获取 Workspace ID 失败" | |
| def _outlook_verification_email(code, received_timestamp): | |
| return EmailMessage( | |
| id=f"msg-{code}-{received_timestamp}", | |
| subject=f"Your ChatGPT code is {code}", | |
| sender="noreply@tm.openai.com", | |
| recipients=["tester@outlook.com"], | |
| body=f"Your ChatGPT code is {code}", | |
| received_at=datetime.fromtimestamp(received_timestamp), | |
| received_timestamp=received_timestamp, | |
| is_read=False, | |
| ) | |
| def _outlook_verification_email_from_sender(code, received_timestamp, sender): | |
| return EmailMessage( | |
| id=f"msg-{code}-{received_timestamp}-{sender}", | |
| subject=f"Your ChatGPT code is {code}", | |
| sender=sender, | |
| recipients=["tester@outlook.com"], | |
| body=f"Your ChatGPT code is {code}", | |
| received_at=datetime.fromtimestamp(received_timestamp), | |
| received_timestamp=received_timestamp, | |
| is_read=False, | |
| ) | |
| def test_outlook_verification_codes_are_isolated_by_stage(monkeypatch): | |
| service = OutlookService({ | |
| "email": "tester@outlook.com", | |
| "password": "pw", | |
| }) | |
| emails = [_outlook_verification_email("123456", 1_700_000_000)] | |
| monkeypatch.setattr( | |
| "src.services.outlook.service.get_email_code_settings", | |
| lambda: {"timeout": 1, "poll_interval": 0}, | |
| ) | |
| monkeypatch.setattr(service, "_try_providers_for_emails", lambda account, count=15, only_unseen=True: emails) | |
| service.set_verification_stage("tester@outlook.com", "signup_otp") | |
| first_code = service.get_verification_code( | |
| email="tester@outlook.com", | |
| timeout=1, | |
| otp_sent_at=1_700_000_000, | |
| ) | |
| service.set_verification_stage("tester@outlook.com", "relogin_otp") | |
| second_code = service.get_verification_code( | |
| email="tester@outlook.com", | |
| timeout=1, | |
| otp_sent_at=1_700_000_000, | |
| ) | |
| assert first_code == "123456" | |
| assert second_code == "123456" | |
| def test_outlook_verification_rejects_codes_outside_current_window(monkeypatch): | |
| service = OutlookService({ | |
| "email": "tester@outlook.com", | |
| "password": "pw", | |
| }) | |
| old_email = [_outlook_verification_email("123456", 1_700_000_000)] | |
| monkeypatch.setattr( | |
| "src.services.outlook.service.get_email_code_settings", | |
| lambda: {"timeout": 1, "poll_interval": 0}, | |
| ) | |
| monkeypatch.setattr(service, "_try_providers_for_emails", lambda account, count=15, only_unseen=True: old_email) | |
| service.set_verification_stage("tester@outlook.com", "relogin_otp") | |
| code = service.get_verification_code( | |
| email="tester@outlook.com", | |
| timeout=1, | |
| otp_sent_at=1_700_000_200, | |
| ) | |
| assert code is None | |
| def test_outlook_verification_allows_same_code_from_newer_email_in_same_stage(monkeypatch): | |
| service = OutlookService({ | |
| "email": "tester@outlook.com", | |
| "password": "pw", | |
| }) | |
| first_email = _outlook_verification_email("123456", 1_700_000_000) | |
| second_email = _outlook_verification_email("123456", 1_700_000_030) | |
| batches = [ | |
| [first_email], | |
| [second_email, first_email], | |
| ] | |
| monkeypatch.setattr( | |
| "src.services.outlook.service.get_email_code_settings", | |
| lambda: {"timeout": 1, "poll_interval": 0}, | |
| ) | |
| monkeypatch.setattr( | |
| service, | |
| "_try_providers_for_emails", | |
| lambda account, count=15, only_unseen=True: batches.pop(0) if batches else [second_email, first_email], | |
| ) | |
| service.set_verification_stage("tester@outlook.com", "relogin_otp") | |
| first_code = service.get_verification_code( | |
| email="tester@outlook.com", | |
| timeout=1, | |
| otp_sent_at=1_700_000_000, | |
| ) | |
| service.set_verification_stage("tester@outlook.com", "relogin_otp") | |
| second_code = service.get_verification_code( | |
| email="tester@outlook.com", | |
| timeout=1, | |
| otp_sent_at=1_700_000_030, | |
| ) | |
| assert first_code == "123456" | |
| assert second_code == "123456" | |
| def test_outlook_relogin_prefers_otp_sender_before_generic_noreply(monkeypatch): | |
| service = OutlookService({ | |
| "email": "tester@outlook.com", | |
| "password": "pw", | |
| }) | |
| generic_email = _outlook_verification_email_from_sender( | |
| "111111", | |
| 1_700_000_000, | |
| "noreply@tm.openai.com", | |
| ) | |
| preferred_email = _outlook_verification_email_from_sender( | |
| "222222", | |
| 1_700_000_010, | |
| "OpenAI <otp@tm1.openai.com>", | |
| ) | |
| batches = [ | |
| [generic_email], | |
| [preferred_email, generic_email], | |
| ] | |
| monkeypatch.setattr( | |
| "src.services.outlook.service.get_email_code_settings", | |
| lambda: {"timeout": 1, "poll_interval": 0}, | |
| ) | |
| monkeypatch.setattr( | |
| service, | |
| "_try_providers_for_emails", | |
| lambda account, count=15, only_unseen=True: batches.pop(0) if batches else [preferred_email, generic_email], | |
| ) | |
| service.set_verification_stage("tester@outlook.com", "relogin_otp") | |
| code = service.get_verification_code( | |
| email="tester@outlook.com", | |
| timeout=1, | |
| otp_sent_at=1_699_999_990, | |
| ) | |
| assert code == "222222" | |
| def test_outlook_verification_debug_tracks_poll_and_selected_sender(monkeypatch): | |
| service = OutlookService({ | |
| "email": "tester@outlook.com", | |
| "password": "pw", | |
| }) | |
| generic_email = _outlook_verification_email_from_sender( | |
| "111111", | |
| 1_700_000_000, | |
| "noreply@tm.openai.com", | |
| ) | |
| preferred_email = _outlook_verification_email_from_sender( | |
| "222222", | |
| 1_700_000_010, | |
| "OpenAI <otp@tm1.openai.com>", | |
| ) | |
| batches = [ | |
| [generic_email], | |
| [preferred_email, generic_email], | |
| ] | |
| monkeypatch.setattr( | |
| "src.services.outlook.service.get_email_code_settings", | |
| lambda: {"timeout": 1, "poll_interval": 0}, | |
| ) | |
| monkeypatch.setattr( | |
| service, | |
| "_try_providers_for_emails", | |
| lambda account, count=15, only_unseen=True: batches.pop(0) if batches else [preferred_email, generic_email], | |
| ) | |
| service.set_verification_stage("tester@outlook.com", "relogin_otp") | |
| code = service.get_verification_code( | |
| email="tester@outlook.com", | |
| timeout=1, | |
| otp_sent_at=1_699_999_990, | |
| ) | |
| debug = service.get_last_verification_debug("tester@outlook.com") | |
| assert code == "222222" | |
| assert debug["stage"] == "relogin_otp" | |
| assert debug["poll_count"] == 2 | |
| assert debug["selected_sender"] == "OpenAI <otp@tm1.openai.com>" | |
| assert debug["selected_code"] == "222222" | |
| assert debug["deferred_generic_only_polls"] == 1 | |
| def test_outlook_verification_uses_fresh_generic_when_preferred_candidates_are_stale(monkeypatch): | |
| service = OutlookService({ | |
| "email": "tester@outlook.com", | |
| "password": "pw", | |
| }) | |
| otp_sent_at = 1_700_000_200 | |
| fresh_generic_email = _outlook_verification_email_from_sender( | |
| "333333", | |
| otp_sent_at, | |
| "noreply@tm.openai.com", | |
| ) | |
| stale_preferred_email = _outlook_verification_email_from_sender( | |
| "222222", | |
| otp_sent_at - 600, | |
| "OpenAI <otp@tm1.openai.com>", | |
| ) | |
| monkeypatch.setattr( | |
| "src.services.outlook.service.get_email_code_settings", | |
| lambda: {"timeout": 1, "poll_interval": 0}, | |
| ) | |
| monkeypatch.setattr( | |
| service, | |
| "_try_providers_for_emails", | |
| lambda account, count=15, only_unseen=True: [fresh_generic_email, stale_preferred_email], | |
| ) | |
| service.set_verification_stage("tester@outlook.com", "relogin_otp") | |
| code = service.get_verification_code( | |
| email="tester@outlook.com", | |
| timeout=1, | |
| otp_sent_at=otp_sent_at, | |
| ) | |
| debug = service.get_last_verification_debug("tester@outlook.com") | |
| assert code == "333333" | |
| assert debug["selected_sender"] == "noreply@tm.openai.com" | |
| assert debug["selected_code"] == "333333" | |
| assert debug["fresh_verification_count"] == 1 | |
| assert debug["fresh_preferred_sender_count"] == 0 | |
| assert debug["stale_preferred_sender_count"] == 1 | |
| def test_outlook_verification_uses_unused_generic_when_fresh_preferred_was_already_consumed(monkeypatch): | |
| service = OutlookService({ | |
| "email": "tester@outlook.com", | |
| "password": "pw", | |
| }) | |
| first_preferred_email = _outlook_verification_email_from_sender( | |
| "444444", | |
| 1_700_000_000, | |
| "OpenAI <otp@tm1.openai.com>", | |
| ) | |
| second_generic_email = _outlook_verification_email_from_sender( | |
| "444444", | |
| 1_700_000_030, | |
| "noreply@tm.openai.com", | |
| ) | |
| batches = [ | |
| [first_preferred_email], | |
| [second_generic_email, first_preferred_email], | |
| [second_generic_email, first_preferred_email], | |
| [second_generic_email, first_preferred_email], | |
| ] | |
| monkeypatch.setattr( | |
| "src.services.outlook.service.get_email_code_settings", | |
| lambda: {"timeout": 1, "poll_interval": 0}, | |
| ) | |
| monkeypatch.setattr( | |
| service, | |
| "_try_providers_for_emails", | |
| lambda account, count=15, only_unseen=True: batches.pop(0) if batches else [second_generic_email, first_preferred_email], | |
| ) | |
| service.set_verification_stage("tester@outlook.com", "relogin_otp") | |
| first_code = service.get_verification_code( | |
| email="tester@outlook.com", | |
| timeout=1, | |
| otp_sent_at=1_700_000_000, | |
| ) | |
| service.set_verification_stage("tester@outlook.com", "relogin_otp") | |
| second_code = service.get_verification_code( | |
| email="tester@outlook.com", | |
| timeout=1, | |
| otp_sent_at=1_700_000_030, | |
| ) | |
| debug = service.get_last_verification_debug("tester@outlook.com") | |
| assert first_code == "444444" | |
| assert second_code == "444444" | |
| assert debug["selected_sender"] == "noreply@tm.openai.com" | |
| assert debug["available_fresh_preferred_sender_count"] == 0 | |
| assert debug["used_fresh_preferred_sender_count"] == 1 | |
| def test_validate_otp_logs_error_context_for_non_200_response(): | |
| session = QueueSession([ | |
| ( | |
| "POST", | |
| OPENAI_API_ENDPOINTS["validate_otp"], | |
| DummyResponse( | |
| status_code=400, | |
| payload={ | |
| "error": { | |
| "message": "Incorrect code", | |
| "code": "invalid_request_error", | |
| } | |
| }, | |
| text='{"error":{"message":"Incorrect code","code":"invalid_request_error"}}', | |
| headers={"x-request-id": "req-validate-1"}, | |
| url=OPENAI_API_ENDPOINTS["validate_otp"], | |
| ), | |
| ), | |
| ]) | |
| engine = RegistrationEngine(FakeEmailService([])) | |
| engine.session = session | |
| ok = engine._validate_verification_code("123456") | |
| assert ok is False | |
| assert any("req-validate-1" in log for log in engine.logs) | |
| assert any("invalid_request_error" in log for log in engine.logs) | |
| assert any("Incorrect code" in log for log in engine.logs) | |