"""云客鉴权失败信封检测(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)