This commit is contained in:
Your Name
2026-08-27 18:32:03 +08:00
parent 4ac6990efe
commit 1f3addcf79
50 changed files with 9145 additions and 1760 deletions
@@ -18,9 +18,12 @@ if str(BACKEND_DIR) not in sys.path:
sys.path.insert(0, str(BACKEND_DIR))
from rpa_engine.douyin_im import http_client as http_client_module
from rpa_engine.douyin_im.auth import DouyinAuth
from rpa_engine.douyin_im.frontier import ensure_frontier_ws
from rpa_engine.douyin_im.http_client import DouyinImHttpClient
from rpa_engine.douyin_im.session import DouyinImSession
from rpa_engine.egress_channels import EgressChannel
from rpa_engine import playwright_worker as playwright_worker_module
from rpa_engine.playwright_worker import DouyinWorker
@@ -33,6 +36,75 @@ class SendTextMessageEntryTests(unittest.IsolatedAsyncioTestCase):
)
return DouyinImHttpClient(session, account_id=account_id)
def test_query_user_does_not_replace_existing_im_uid(self):
client = self._make_client()
auth = SimpleNamespace(
source_ip="",
get_uid=MagicMock(return_value=938334054809296),
)
resolved = client._resolve_authoritative_uid(auth)
self.assertEqual(resolved, 10001)
self.assertEqual(client.session.my_uid, 10001)
auth.get_uid.assert_not_called()
def test_verified_uid_does_not_replace_the_runtime_device_id(self):
# device_id 是 query/user 返回的设备注册号,my_uid 是账号 UID。
# 拿 my_uid 顶替 device_id 会让 frontier 订阅到另一个地址:握手照样
# 成功,却永远收不到这个账号的私信。
session = DouyinImSession(
cookies={"sessionid": "test-session"},
my_uid=2609567359568155,
device_id="7678285795559818786",
web_id="7678286623234475535",
uid_verified=True,
)
auth = DouyinAuth.from_im_session(session)
self.assertEqual(auth.device_id, "7678285795559818786")
self.assertEqual(session.device_id, "7678285795559818786")
def test_captured_tokenless_browser_frontier_url_is_kept(self):
url = (
"wss://frontier100-normal.zijieapi.com/ws/v2?aid=6383&"
"device_platform=web&fpid=9&device_id=7678285795559818786&"
"access_key=0123456789abcdef0123456789abcdef&version_code=fws_1.0.0"
)
session = DouyinImSession(
cookies={"sessionid": "test-session"},
ws_urls=[url],
my_uid=2609567359568155,
device_id="7678285795559818786",
)
resolved = ensure_frontier_ws(session)
self.assertEqual(resolved, url)
self.assertEqual(session.ws_urls, [url])
def test_non_im_frontier_product_is_rejected(self):
url = (
"wss://frontier100-normal.zijieapi.com/ws/v2?aid=6383&"
"device_platform=web&fpid=971&device_id=7678285795559818786&"
"access_key=0123456789abcdef0123456789abcdef"
)
session = DouyinImSession(
cookies={"sessionid": "test-session"},
ws_urls=[url],
my_uid=2609567359568155,
device_id="7678285795559818786",
uid_verified=True,
)
resolved = ensure_frontier_ws(session)
self.assertIn("frontier-im.douyin.com", resolved)
self.assertIn("fpid=9", resolved)
self.assertIn("device_id=7678285795559818786", resolved)
self.assertNotIn("fpid=971", resolved)
async def test_public_entry_submits_the_whole_send_to_outbound_queue(self):
client = self._make_client(account_id=73)
submit = AsyncMock(return_value=True)
@@ -194,6 +266,59 @@ class SendTextMessageEntryTests(unittest.IsolatedAsyncioTestCase):
class WorkerLifecycleTests(unittest.IsolatedAsyncioTestCase):
async def test_virtual_display_starts_before_playwright_driver(self):
ensure_display = AsyncMock()
async def start_driver():
ensure_display.assert_awaited_once_with(False)
return "playwright-driver"
manager = SimpleNamespace(start=AsyncMock(side_effect=start_driver))
with (
patch.object(
playwright_worker_module,
"resolve_headless",
return_value=False,
),
patch.object(
playwright_worker_module,
"ensure_browser_display",
ensure_display,
),
patch.object(
playwright_worker_module,
"async_playwright",
return_value=manager,
),
):
driver, headless = await playwright_worker_module._start_playwright_for_browser()
self.assertEqual(driver, "playwright-driver")
self.assertFalse(headless)
manager.start.assert_awaited_once_with()
async def test_visible_login_prompt_overrides_stale_sessionid(self):
worker = DouyinWorker(account_id=917, login_mode="browser")
worker._has_visible_login_prompt = AsyncMock(return_value=True)
worker.check_homepage_login_status = AsyncMock(return_value=True)
worker.check_logged_in_by_cookie = AsyncMock(return_value=True)
logged_in = await worker._verify_login_state()
self.assertFalse(logged_in)
worker.check_homepage_login_status.assert_not_awaited()
worker.check_logged_in_by_cookie.assert_not_awaited()
async def test_homepage_message_entry_is_not_login_evidence(self):
worker = DouyinWorker(account_id=918, login_mode="browser")
worker._has_visible_login_prompt = AsyncMock(return_value=False)
worker.page = SimpleNamespace(query_selector=AsyncMock(return_value=None))
logged_in = await worker.check_homepage_login_status()
self.assertFalse(logged_in)
worker.page.query_selector.assert_awaited_once()
async def test_start_saves_task_and_stop_waits_until_it_is_done(self):
worker = DouyinWorker(account_id=919, login_mode="im_direct")
loop_started = asyncio.Event()