first commit
This commit is contained in:
@@ -0,0 +1,156 @@
|
||||
import logging
|
||||
import unittest
|
||||
import urllib.error
|
||||
from unittest.mock import AsyncMock, Mock, patch
|
||||
|
||||
from app.bilibili_cookie_refresh import BilibiliQrLogin, _request_json
|
||||
|
||||
|
||||
class _JsonResponse:
|
||||
def __init__(self, payload=b'{"code": 0}'):
|
||||
self.payload = payload
|
||||
|
||||
def read(self):
|
||||
return self.payload
|
||||
|
||||
|
||||
class BilibiliRequestTests(unittest.TestCase):
|
||||
def test_get_retries_transient_connection_failures(self):
|
||||
opener = Mock()
|
||||
response = _JsonResponse()
|
||||
opener.open.side_effect = [
|
||||
urllib.error.URLError(ConnectionRefusedError(10061, "connection refused")),
|
||||
urllib.error.URLError(ConnectionRefusedError(10061, "connection refused")),
|
||||
response,
|
||||
]
|
||||
|
||||
with patch("app.bilibili_cookie_refresh.time.sleep") as sleep:
|
||||
payload, actual_response = _request_json(
|
||||
"https://passport.bilibili.com/test",
|
||||
opener=opener,
|
||||
retries=2,
|
||||
)
|
||||
|
||||
self.assertEqual(payload, {"code": 0})
|
||||
self.assertIs(actual_response, response)
|
||||
self.assertEqual(opener.open.call_count, 3)
|
||||
self.assertEqual([call.args[0] for call in sleep.call_args_list], [0.4, 0.8])
|
||||
|
||||
def test_exhausted_retries_hide_low_level_network_error(self):
|
||||
opener = Mock()
|
||||
opener.open.side_effect = urllib.error.URLError(
|
||||
ConnectionRefusedError(10061, "connection refused")
|
||||
)
|
||||
|
||||
with patch("app.bilibili_cookie_refresh.time.sleep"), self.assertRaisesRegex(
|
||||
RuntimeError,
|
||||
"^连接B站登录服务失败,请稍后重试$",
|
||||
) as raised:
|
||||
_request_json(
|
||||
"https://passport.bilibili.com/test",
|
||||
opener=opener,
|
||||
retries=2,
|
||||
)
|
||||
|
||||
self.assertEqual(opener.open.call_count, 3)
|
||||
self.assertNotIn("10061", str(raised.exception))
|
||||
|
||||
|
||||
class _CredentialStore:
|
||||
def __init__(self):
|
||||
self.refresh_token = ""
|
||||
|
||||
def save_refresh_token(self, refresh_token):
|
||||
self.refresh_token = refresh_token
|
||||
|
||||
def is_configured(self):
|
||||
return bool(self.refresh_token)
|
||||
|
||||
|
||||
class BilibiliQrLoginTests(unittest.IsolatedAsyncioTestCase):
|
||||
def setUp(self):
|
||||
self.store = _CredentialStore()
|
||||
self.updated_cookies = []
|
||||
self.on_logged_in = AsyncMock()
|
||||
self.login = BilibiliQrLogin(
|
||||
credential_store=self.store,
|
||||
update_cookie=self.updated_cookies.append,
|
||||
logger=logging.getLogger("test-bilibili-qr"),
|
||||
on_logged_in=self.on_logged_in,
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _generate_response():
|
||||
return ({
|
||||
"code": 0,
|
||||
"data": {
|
||||
"url": "https://passport.bilibili.com/h5-app/passport/login/scan?navhide=1",
|
||||
"qrcode_key": "private-qrcode-key",
|
||||
},
|
||||
}, None)
|
||||
|
||||
async def test_start_keeps_qrcode_key_on_server(self):
|
||||
with patch("app.bilibili_cookie_refresh._request_json", return_value=self._generate_response()):
|
||||
result = await self.login.start()
|
||||
|
||||
self.assertEqual(result["state"], "awaiting_scan")
|
||||
self.assertTrue(result["has_qr_image"])
|
||||
self.assertNotIn("qr_key", result)
|
||||
self.assertNotIn("qr_url", result)
|
||||
self.assertFalse(result["credential_configured"])
|
||||
|
||||
async def test_poll_reports_scanned_without_saving_credentials(self):
|
||||
with patch("app.bilibili_cookie_refresh._request_json", return_value=self._generate_response()):
|
||||
await self.login.start()
|
||||
with patch("app.bilibili_cookie_refresh._request_json", return_value=({
|
||||
"code": 0,
|
||||
"data": {"code": 86090, "message": "二维码已扫码未确认"},
|
||||
}, None)):
|
||||
result = await self.login.poll()
|
||||
|
||||
self.assertEqual(result["state"], "awaiting_confirm")
|
||||
self.assertEqual(self.store.refresh_token, "")
|
||||
self.assertEqual(self.updated_cookies, [])
|
||||
self.on_logged_in.assert_not_awaited()
|
||||
|
||||
async def test_success_saves_refresh_token_and_updates_cookie(self):
|
||||
with patch("app.bilibili_cookie_refresh._request_json", return_value=self._generate_response()):
|
||||
await self.login.start()
|
||||
|
||||
login_url = (
|
||||
"https://www.bilibili.com/?SESSDATA=session-value&bili_jct=csrf-value"
|
||||
"&DedeUserID=123456"
|
||||
)
|
||||
|
||||
def request_side_effect(url, **_kwargs):
|
||||
if "qrcode/poll" in url:
|
||||
return ({
|
||||
"code": 0,
|
||||
"data": {
|
||||
"code": 0,
|
||||
"url": login_url,
|
||||
"refresh_token": "refresh-token-value",
|
||||
},
|
||||
}, None)
|
||||
if "x/web-interface/nav" in url:
|
||||
return ({
|
||||
"code": 0,
|
||||
"data": {"isLogin": True, "mid": 123456, "uname": "测试账号"},
|
||||
}, None)
|
||||
raise AssertionError(f"unexpected url: {url}")
|
||||
|
||||
with patch("app.bilibili_cookie_refresh._request_json", side_effect=request_side_effect):
|
||||
result = await self.login.poll()
|
||||
|
||||
self.assertEqual(result["state"], "completed")
|
||||
self.assertEqual(result["account"]["uname"], "测试账号")
|
||||
self.assertTrue(result["credential_configured"])
|
||||
self.assertNotIn("refresh_token", result)
|
||||
self.assertEqual(self.store.refresh_token, "refresh-token-value")
|
||||
self.assertEqual(self.updated_cookies[0]["SESSDATA"], "session-value")
|
||||
self.assertEqual(self.updated_cookies[0]["bili_jct"], "csrf-value")
|
||||
self.on_logged_in.assert_awaited_once()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user