Files
any-auto-register/tests/test_chatgpt_phone_flow.py
2026-03-30 17:41:24 +08:00

204 lines
7.9 KiB
Python

import base64
import json
import tempfile
import unittest
from pathlib import Path
from unittest import mock
from platforms.chatgpt.oauth_client import OAuthClient
from platforms.chatgpt.phone_service import SMSToMePhoneService
from platforms.chatgpt.utils import FlowState
from smstome_tool import PhoneEntry, parse_country_slugs
class OAuthCookieDecodeTests(unittest.TestCase):
def test_decode_signed_cookie_payload(self):
payload = {
"email": "demo@example.com",
"phone_number": "+447456344799",
"phone_verification_channel": "whatsapp",
}
encoded = base64.urlsafe_b64encode(json.dumps(payload).encode("utf-8")).decode("utf-8").rstrip("=")
cookie_value = f"{encoded}.sig-a.sig-b"
self.assertEqual(OAuthClient._decode_cookie_json_value(cookie_value), payload)
def test_decode_invalid_cookie_payload(self):
self.assertIsNone(OAuthClient._decode_cookie_json_value("not-a-valid-cookie"))
class SMSToMeConfigTests(unittest.TestCase):
def test_parse_country_slugs_accepts_csv_and_iterables(self):
self.assertEqual(
parse_country_slugs("united-kingdom, poland;finland"),
["united-kingdom", "poland", "finland"],
)
self.assertEqual(
parse_country_slugs(["united-kingdom", "poland", "united_kingdom"]),
["united-kingdom", "poland"],
)
def test_phone_service_enabled_when_pool_file_exists(self):
with tempfile.TemporaryDirectory() as tmp_dir:
pool_path = Path(tmp_dir) / "phones.txt"
pool_path.write_text("+447456344799\tunited-kingdom\thttps://example.com\n", encoding="utf-8")
service = SMSToMePhoneService({"smstome_global_file": str(pool_path)})
self.assertTrue(service.enabled)
def test_phone_service_disabled_for_empty_pool_without_cookie(self):
with tempfile.TemporaryDirectory() as tmp_dir:
pool_path = Path(tmp_dir) / "phones.txt"
pool_path.write_text("", encoding="utf-8")
service = SMSToMePhoneService({"smstome_global_file": str(pool_path)})
self.assertFalse(service.enabled)
def test_wait_for_code_forwards_cookie_timeout_and_poll_interval(self):
entry = PhoneEntry(
country_slug="united-kingdom",
phone="+447456344799",
detail_url="https://example.com/phone/1",
)
service = SMSToMePhoneService(
{
"smstome_cookie": "cf_clearance=demo",
"smstome_otp_timeout_seconds": "66",
"smstome_poll_interval_seconds": "7",
}
)
with mock.patch("platforms.chatgpt.phone_service.wait_for_otp", return_value="123456") as mocked:
code = service.wait_for_code(entry)
self.assertEqual(code, "123456")
mocked.assert_called_once()
kwargs = mocked.call_args.kwargs
self.assertEqual(kwargs["cookie_header"], "cf_clearance=demo")
self.assertEqual(kwargs["timeout"], 66)
self.assertEqual(kwargs["poll_interval"], 7)
self.assertFalse(kwargs["raise_on_timeout"])
def test_ensure_pool_ready_syncs_with_configured_page_limit(self):
with tempfile.TemporaryDirectory() as tmp_dir:
pool_path = Path(tmp_dir) / "phones.txt"
service = SMSToMePhoneService(
{
"smstome_cookie": "cf_clearance=demo",
"smstome_country_slugs": "united-kingdom",
"smstome_global_file": str(pool_path),
"smstome_sync_max_pages_per_country": "9",
}
)
with mock.patch("platforms.chatgpt.phone_service.update_global_phone_list", return_value=3) as mocked:
service.ensure_pool_ready()
mocked.assert_called_once()
kwargs = mocked.call_args.kwargs
self.assertEqual(kwargs["cookie_header"], "cf_clearance=demo")
self.assertEqual(kwargs["countries"], ["united-kingdom"])
self.assertEqual(kwargs["output_path"], pool_path)
self.assertEqual(kwargs["max_pages_per_country"], 9)
class OAuthPhoneBlacklistTests(unittest.TestCase):
def test_should_blacklist_explicit_phone_rejection(self):
state = FlowState(
page_type="add_phone",
payload={"error": {"message": "phone number is invalid"}},
)
self.assertTrue(
OAuthClient._should_blacklist_phone_failure(
"add-phone/send 失败: 400 - phone number is invalid",
state,
)
)
def test_should_not_blacklist_whatsapp_or_delivery_failures(self):
self.assertFalse(
OAuthClient._should_blacklist_phone_failure(
"add_phone 已切到 whatsapp 通道,当前 SMSToMe 仅支持短信接码"
)
)
self.assertFalse(
OAuthClient._should_blacklist_phone_failure("手机号 +447000000001 未收到短信验证码")
)
def test_handle_add_phone_blacklists_explicitly_rejected_number(self):
client = OAuthClient(config={}, verbose=False)
client._log = lambda _msg: None
entry = PhoneEntry(
country_slug="united-kingdom",
phone="+447000000001",
detail_url="https://example.com/phone/1",
)
phone_service = mock.Mock()
phone_service.enabled = True
phone_service.max_attempts = 1
phone_service.acquire_phone.return_value = entry
phone_service.prefix_hint.return_value = "+447000"
with mock.patch("platforms.chatgpt.oauth_client.SMSToMePhoneService", return_value=phone_service):
with mock.patch.object(
client,
"_send_phone_number",
return_value=(False, None, "add-phone/send 失败: 400 - phone number is invalid"),
):
state = client._handle_add_phone_verification(
"device-id",
"Mozilla/5.0",
None,
None,
FlowState(page_type="add_phone"),
)
self.assertIsNone(state)
phone_service.mark_blacklisted.assert_called_once_with(entry.phone)
self.assertIn("add_phone 阶段失败", client.last_error)
def test_handle_add_phone_does_not_blacklist_whatsapp_channel(self):
client = OAuthClient(config={}, verbose=False)
client._log = lambda _msg: None
entry = PhoneEntry(
country_slug="united-kingdom",
phone="+447000000002",
detail_url="https://example.com/phone/2",
)
phone_service = mock.Mock()
phone_service.enabled = True
phone_service.max_attempts = 1
phone_service.acquire_phone.return_value = entry
phone_service.prefix_hint.return_value = "+447000"
next_state = FlowState(
page_type="phone_otp_verification",
continue_url="https://auth.openai.com/phone-verification",
)
with mock.patch("platforms.chatgpt.oauth_client.SMSToMePhoneService", return_value=phone_service):
with mock.patch.object(client, "_send_phone_number", return_value=(True, next_state, "")):
with mock.patch.object(
client,
"_decode_oauth_session_cookie",
return_value={
"phone_verification_channel": "whatsapp",
"phone_number": entry.phone,
},
):
state = client._handle_add_phone_verification(
"device-id",
"Mozilla/5.0",
None,
None,
FlowState(page_type="add_phone"),
)
self.assertIsNone(state)
phone_service.mark_blacklisted.assert_not_called()
self.assertIn("whatsapp", client.last_error)
if __name__ == "__main__":
unittest.main()