study / test /test_account_client_context.py
xiaolongmr
整合功能更新并排除本地恢复产物
2dc6fe4
Raw History Blame Contribute Delete
6.75 kB
from __future__ import annotations
import base64
import json
import unittest
from typing import Any
from unittest.mock import Mock, patch
import services.openai_backend_api as backend_module
from services.account_service import (
AccountService,
DEFAULT_CHATGPT_USER_AGENT,
build_chatgpt_client_fingerprint,
sanitize_registered_proxy,
)
from services.openai_backend_api import OpenAIBackendAPI
class MemoryStorage:
def __init__(self, accounts: list[dict[str, Any]] | None = None) -> None:
self.accounts = [dict(item) for item in (accounts or [])]
def load_accounts(self) -> list[dict[str, Any]]:
return [dict(item) for item in self.accounts]
def save_accounts(self, accounts: list[dict[str, Any]]) -> None:
self.accounts = [dict(item) for item in accounts]
def make_jwt(payload: dict[str, Any]) -> str:
def encode(value: dict[str, Any]) -> str:
raw = json.dumps(value, separators=(",", ":")).encode("utf-8")
return base64.urlsafe_b64encode(raw).decode("ascii").rstrip("=")
return f'{encode({"alg": "none"})}.{encode(payload)}.sig'
class FakeCookies:
def __init__(self) -> None:
self.values: list[tuple[str, str, str]] = []
def set(self, name: str, value: str, *, domain: str) -> None:
self.values.append((name, value, domain))
class FakeSession:
def __init__(self) -> None:
self.headers: dict[str, str] = {}
self.cookies = FakeCookies()
self.closed = False
def close(self) -> None:
self.closed = True
class AccountClientContextTests(unittest.TestCase):
def test_account_context_is_migrated_and_stable_across_reload(self) -> None:
access_token = make_jwt(
{"https://api.openai.com/auth": {"chatgpt_account_id": "acct_stable"}}
)
storage = MemoryStorage([{"access_token": access_token}])
first_service = AccountService(storage)
first = first_service.get_account(access_token)
second_service = AccountService(storage)
second = second_service.get_account(access_token)
self.assertEqual(first["account_id"], "acct_stable")
self.assertTrue(first["account_key"])
self.assertEqual(first["account_key"], second["account_key"])
self.assertEqual(first["fp"], second["fp"])
self.assertEqual(first["fp"]["impersonate"], "chrome145")
self.assertIn("Chrome/145.", first["fp"]["user-agent"])
rotated = build_chatgpt_client_fingerprint(
"rotated-access-token",
account_key=first["account_key"],
)
self.assertEqual(rotated["oai-device-id"], first["fp"]["oai-device-id"])
self.assertEqual(rotated["oai-session-id"], first["fp"]["oai-session-id"])
def test_legacy_mismatched_edge_profile_is_replaced_by_consistent_profile(self) -> None:
fp = build_chatgpt_client_fingerprint(
"token",
{
"user-agent": (
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
"Chrome/143.0.0.0 Safari/537.36 Edg/143.0.0.0"
),
"impersonate": "edge101",
"oai-device-id": "device-stable",
"oai-session-id": "session-stable",
},
)
self.assertEqual(fp["impersonate"], "chrome145")
self.assertIn("Chrome/145.", fp["user-agent"])
self.assertEqual(fp["oai-device-id"], "device-stable")
self.assertEqual(fp["oai-session-id"], "session-stable")
def test_backend_reuses_context_proxy_and_account_header_for_rotated_token(self) -> None:
storage = MemoryStorage()
service = AccountService(storage)
fp = build_chatgpt_client_fingerprint(
raw_fp={"user-agent": DEFAULT_CHATGPT_USER_AGENT, "impersonate": "chrome145"},
device_id="login-device",
session_id="login-session",
)
session = FakeSession()
context = {
"account_id": "acct_context",
"fp": fp,
"registered_proxy": "http://127.0.0.1:7890",
}
with (
patch.object(backend_module, "account_service", service),
patch.object(backend_module.requests, "Session", return_value=session) as factory,
):
backend = OpenAIBackendAPI("rotated-token", account_context=context)
self.assertEqual(
factory.call_args.kwargs,
{"impersonate": "chrome110", "verify": True, "proxy": "http://127.0.0.1:7890"},
)
self.assertEqual(backend.device_id, "login-device")
self.assertEqual(backend.session_id, "login-session")
self.assertEqual(session.headers["ChatGPT-Account-Id"], "acct_context")
self.assertEqual(session.headers["OAI-Client-Version"], "prod-a194cd50d4416d3c0b47c740f206b12ce60f5887")
self.assertEqual(session.headers["OAI-Client-Build-Number"], "6708908")
self.assertEqual(session.headers["Sec-Ch-Ua-Platform-Version"], '"19.0.0"')
self.assertEqual(backend._headers("/backend-api/me")["ChatGPT-Account-Id"], "acct_context")
backend.close()
self.assertTrue(session.closed)
def test_discovered_account_id_is_applied_before_followup_requests_and_saved(self) -> None:
storage = MemoryStorage([{"access_token": "opaque-token"}])
service = AccountService(storage)
session = FakeSession()
with (
patch.object(backend_module, "account_service", service),
patch.object(backend_module.requests, "Session", return_value=session),
):
backend = OpenAIBackendAPI("opaque-token")
backend._get_default_account = Mock(return_value={"account_id": "acct_discovered", "plan_type": "free"})
def get_me() -> dict[str, str]:
self.assertEqual(session.headers["ChatGPT-Account-Id"], "acct_discovered")
return {"email": "user@example.com", "id": "user-1"}
backend._get_me = Mock(side_effect=get_me)
backend._get_conversation_init = Mock(return_value={"limits_progress": []})
result = backend.get_user_info()
self.assertEqual(result["account_id"], "acct_discovered")
self.assertEqual(service.get_account("opaque-token")["account_id"], "acct_discovered")
def test_authenticated_proxy_is_not_persisted_in_account_data(self) -> None:
self.assertEqual(sanitize_registered_proxy("http://user:secret@proxy.example:8080"), "")
self.assertEqual(
sanitize_registered_proxy("http://127.0.0.1:7890/path?token=secret"),
"http://127.0.0.1:7890",
)
if __name__ == "__main__":
unittest.main()