Handle Jin10 QR expiration reported by upstream
This commit is contained in:
1 parent
4184bc68e6
commit
57424fd661
3 files changed
+39
-2
No files matched your search
@@ -14,6 +14,10 @@ class Jin10Error(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class Jin10QrExpired(Jin10Error):
|
||||
pass
|
||||
|
||||
|
||||
async def uc_request(path: str, params: dict | None = None, token: str | None = None) -> dict:
|
||||
headers = {**HEADERS, **({"x-token": token} if token else {})}
|
||||
try:
|
||||
@@ -21,6 +25,8 @@ async def uc_request(path: str, params: dict | None = None, token: str | None =
|
||||
response = await client.get(UC_URL + path, params=params, headers=headers)
|
||||
response.raise_for_status()
|
||||
body = response.json()
|
||||
if path == "/qrcode/try_login" and isinstance(body, dict) and body.get("status") == 400 and body.get("message") == "二维码已过期":
|
||||
raise Jin10QrExpired("金十二维码已过期")
|
||||
if not isinstance(body, dict) or body.get("status") != 200:
|
||||
raise Jin10Error("金十未接受请求,请重新扫码或稍后重试")
|
||||
return body
|
||||
@@ -48,7 +54,10 @@ def qr_image(token: str) -> str:
|
||||
|
||||
|
||||
async def poll_qr(token: str) -> tuple[str, str | None]:
|
||||
body = await uc_request("/qrcode/try_login", {"token": token})
|
||||
try:
|
||||
body = await uc_request("/qrcode/try_login", {"token": token})
|
||||
except Jin10QrExpired:
|
||||
return "expired", None
|
||||
data = body.get("data")
|
||||
if data is None:
|
||||
return "pending", None
|
||||
|
||||
@@ -225,3 +225,31 @@ def test_upstream_errors_never_expose_tokens(qr_env, monkeypatch):
|
||||
with pytest.raises(jin10_auth.Jin10Error, match="金十连接失败") as exc:
|
||||
asyncio.run(jin10_auth.uc_request("/qrcode/try_login", {"token": "private"}))
|
||||
assert "private" not in str(exc.value)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("message,expired", [("二维码已过期", True), ("请求被拒绝", False)])
|
||||
def test_observed_upstream_expiry_is_not_a_generic_connection_failure(qr_env, monkeypatch, message, expired):
|
||||
from alphapulse.calendar import jin10_auth
|
||||
import importlib
|
||||
jin10_auth = importlib.reload(jin10_auth)
|
||||
response = httpx.Response(200, json={"status": 400, "message": message}, request=httpx.Request("GET", "https://uc-api.jin10.com/qrcode/try_login"))
|
||||
monkeypatch.setattr(httpx.AsyncClient, "get", AsyncMock(return_value=response))
|
||||
if expired:
|
||||
assert asyncio.run(jin10_auth.poll_qr("private")) == ("expired", None)
|
||||
else:
|
||||
with pytest.raises(jin10_auth.Jin10Error, match="未接受请求"):
|
||||
asyncio.run(jin10_auth.poll_qr("private"))
|
||||
|
||||
|
||||
def test_upstream_expiry_clears_task_without_disconnecting_existing_account(qr_env):
|
||||
e = qr_env
|
||||
e.authorize()
|
||||
e.pending()
|
||||
e.row.token_encrypted = b"existing-encrypted-credential"
|
||||
e.upstream.poll_qr.return_value = "expired", None
|
||||
response = request(e, "POST", "/qr/task-a/poll")
|
||||
assert response.status_code == 200
|
||||
assert response.json()["data"]["state"] == "expired"
|
||||
assert e.row.token_encrypted == b"existing-encrypted-credential"
|
||||
assert e.row.qr_encrypted is None
|
||||
assert e.row.error is None
|
||||
@@ -135,7 +135,7 @@ watch(visible, (value) => {
|
||||
<div v-loading="busy" class="jin10-qr">
|
||||
<img v-if="qr && state === 'pending'" :src="qr.image" alt="金十登录二维码" width="240" height="240">
|
||||
<p v-if="state === 'pending'">
|
||||
请用金十 App 扫描并确认,二维码 60 秒后失效。
|
||||
请立即用金十 App 扫描并确认,二维码有效时间较短,失效后可刷新。
|
||||
</p>
|
||||
<p v-else-if="state === 'expired'">
|
||||
二维码已失效,请刷新后重新扫码。
|
||||
|
||||
Reference in new issue
Block a user