"""微信登录 M1 测试:bind_ticket 令牌、wechat-login(openid 命中/未命中)、 bind-phone(建号/占用/令牌过期)。 沿用 tests/test_auth.py 风格:HTTP 走 client fixture;微信 code→openid 用 monkeypatch 拦掉(conftest 里 WECHAT_APP_ID/SECRET 是 dummy,不真连微信);短信走 SMS_MOCK(任意 6 位通过)。 """ from __future__ import annotations import pytest from app.core import security from app.integrations import wxpay def _fake_userinfo(openid: str, nickname: str | None = "微信昵称", avatar: str | None = "http://x/a.png"): """返回一个可传给 monkeypatch 的假 code_to_userinfo(忽略 code,固定返回给定 openid)。""" def _f(code: str) -> dict: return {"openid": openid, "nickname": nickname, "avatar_url": avatar, "raw": {}} return _f # ===== Task 1: bind_ticket 令牌 ===== def test_bind_ticket_roundtrip() -> None: token = security.create_bind_ticket(openid="oid1", wechat_nickname="昵", wechat_avatar_url="http://a") claims = security.decode_bind_ticket(token) assert claims["openid"] == "oid1" assert claims["wnk"] == "昵" assert claims["wav"] == "http://a" def test_bind_ticket_wrong_type_rejected() -> None: # 用 access token 冒充 bind_ticket → TokenError(typ 不匹配) access, _ = security.create_token(user_id=1, token_type="access") with pytest.raises(security.TokenError): security.decode_bind_ticket(access) def test_bind_ticket_expired_rejected(monkeypatch) -> None: monkeypatch.setattr(security.settings, "WECHAT_BIND_TICKET_EXPIRE_MINUTES", -1) token = security.create_bind_ticket(openid="oid", wechat_nickname=None, wechat_avatar_url=None) with pytest.raises(security.TokenError): security.decode_bind_ticket(token) # ===== Task 2: wechat-login ===== def test_wechat_login_new_openid_returns_bind_ticket(client, monkeypatch) -> None: monkeypatch.setattr(wxpay, "code_to_userinfo", _fake_userinfo("openid_new_1", "小明", "http://x/m.png")) r = client.post("/api/v1/auth/wechat-login", json={"code": "wxcode1", "device_id": "devA"}) assert r.status_code == 200, r.text body = r.json() assert body["status"] == "need_bind_phone" assert body["bind_ticket"] assert body["wechat_nickname"] == "小明" assert body["wechat_avatar_url"] == "http://x/m.png" assert body["token"] is None def test_wechat_login_invalid_code_returns_400(client, monkeypatch) -> None: def _raise(code: str) -> dict: raise ValueError("微信授权失败: invalid code") monkeypatch.setattr(wxpay, "code_to_userinfo", _raise) r = client.post("/api/v1/auth/wechat-login", json={"code": "bad", "device_id": "devA"}) assert r.status_code == 400, r.text