Files
yunke/yunke-server/tests/test_scrape_auth_guard.py
浅念 e365108244 fix(scraper): 云客鉴权失败信封统一拦截——cookie 过期响亮报错不再静默
事故(2026-09-03 手动同步实测):cookie 过期时 WA 路径对 str data 调 .get
崩 AttributeError,微信路径更糟——auth 错误信封被当空数据,55 员工全部
rows=0 无任何告警,整轮同步一无所获。

守卫:_browser_fetch 收口统一走 _check_auth_envelope,识别 success=false
且 code=±20020(实测信封)抛 CookieExpiredError;main 入口捕获后输出
一句话可读信息 + 退出码 3(不再天书堆栈)。业务错误(-90000 等)不受影响
照旧放行。测试 5 新增,全量 517 绿。

后续(等确认真实密码):CookieExpiredError 触发自动重新登录换 cookie,
过期彻底自愈。

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-03 17:52:09 +08:00

50 lines
1.9 KiB
Python

"""云客鉴权失败信封检测(2026-09-03 wa-friends 崩溃事故的防护)。
事故:cookie 过期时云客返回 ``{"code":"-20020","data":"账号认证已过期…",
"success":false}``——WA 路径对 str data 调 .get 直接 AttributeError 天书,
微信路径更糟:静默当空数据(55 员工 rows=0 无任何告警,同步一无所获)。
守卫:_browser_fetch 统一走 :func:`_check_auth_envelope`,识别鉴权失败信封
抛 :class:`CookieExpiredError`(调用方中止本轮,日志一句话可读)。
"""
from __future__ import annotations
import pytest
from scripts.scrape_via_page import CookieExpiredError, _check_auth_envelope
def test_auth_fail_envelope_raises():
resp = {
"code": "-20020",
"data": "账号认证已过期,请重新登录",
"message": "鉴权失败,token验证失败",
"success": False,
}
with pytest.raises(CookieExpiredError, match="cookie"):
_check_auth_envelope(resp)
def test_non_dict_resp_passes_through():
# 响应体不是 dict(如字符串)时不应崩——原样放行由调用方语义处理
assert _check_auth_envelope("plain string") == "plain string"
def test_normal_success_envelope_passes():
resp = {"code": "0", "data": {"totalCount": 5, "overviews": [{"a": 1}]}, "success": True}
assert _check_auth_envelope(resp) is resp
def test_other_error_codes_do_not_raise():
# 业务错误(如 -90000 系统错误)不是鉴权问题——照旧返回,由调用方决定
resp = {"code": "-90000", "data": "系统错误", "success": False}
assert _check_auth_envelope(resp) is resp
def test_code_as_int_20020_also_raises():
# 云客 code 时而是字符串时而是数字——两种都识别
resp = {"code": 20020, "data": "账号认证已过期,请重新登录", "success": False}
with pytest.raises(CookieExpiredError):
_check_auth_envelope(resp)