|
|
@@ -0,0 +1,117 @@
|
|
|
+"""服务号 mp-bind / mp-bind-status:成功与冲突路径(mock oauth)。"""
|
|
|
+from types import SimpleNamespace
|
|
|
+from unittest.mock import AsyncMock, MagicMock, patch
|
|
|
+
|
|
|
+import pytest
|
|
|
+from fastapi import HTTPException
|
|
|
+
|
|
|
+
|
|
|
+@pytest.mark.asyncio
|
|
|
+async def test_mp_bind_success():
|
|
|
+ user = SimpleNamespace(
|
|
|
+ id="u1",
|
|
|
+ del_flag=0,
|
|
|
+ wx_mp_openid=None,
|
|
|
+ wx_unionid=None,
|
|
|
+ wx_mp_bind_time=None,
|
|
|
+ update_time=None,
|
|
|
+ )
|
|
|
+ db = MagicMock()
|
|
|
+ with (
|
|
|
+ patch(
|
|
|
+ "app.api.mini_auth._resolve_user_id",
|
|
|
+ new_callable=AsyncMock,
|
|
|
+ return_value="u1",
|
|
|
+ ),
|
|
|
+ patch(
|
|
|
+ "app.api.mini_auth.oauth_code_to_openid",
|
|
|
+ return_value={"openid": "mp-oid-1", "unionid": "uni-1"},
|
|
|
+ ),
|
|
|
+ patch("app.api.mini_auth.user_crud.get_by_wx_mp_openid", return_value=None),
|
|
|
+ patch("app.api.mini_auth.user_crud.get", return_value=user),
|
|
|
+ ):
|
|
|
+ from app.api.mini_auth import mp_bind
|
|
|
+
|
|
|
+ resp = await mp_bind(
|
|
|
+ request=MagicMock(),
|
|
|
+ body={"code": "oauth-code"},
|
|
|
+ db=db,
|
|
|
+ )
|
|
|
+
|
|
|
+ assert resp["code"] == 200
|
|
|
+ assert resp["data"]["bound"] is True
|
|
|
+ assert user.wx_mp_openid == "mp-oid-1"
|
|
|
+ assert user.wx_unionid == "uni-1"
|
|
|
+ assert user.wx_mp_bind_time is not None
|
|
|
+ db.add.assert_called_once_with(user)
|
|
|
+ db.commit.assert_called_once()
|
|
|
+
|
|
|
+
|
|
|
+@pytest.mark.asyncio
|
|
|
+async def test_mp_bind_conflict_when_openid_bound_to_other():
|
|
|
+ other = SimpleNamespace(id="u-other")
|
|
|
+ db = MagicMock()
|
|
|
+ with (
|
|
|
+ patch(
|
|
|
+ "app.api.mini_auth._resolve_user_id",
|
|
|
+ new_callable=AsyncMock,
|
|
|
+ return_value="u1",
|
|
|
+ ),
|
|
|
+ patch(
|
|
|
+ "app.api.mini_auth.oauth_code_to_openid",
|
|
|
+ return_value={"openid": "mp-oid-taken", "unionid": None},
|
|
|
+ ),
|
|
|
+ patch(
|
|
|
+ "app.api.mini_auth.user_crud.get_by_wx_mp_openid",
|
|
|
+ return_value=other,
|
|
|
+ ),
|
|
|
+ ):
|
|
|
+ from app.api.mini_auth import mp_bind
|
|
|
+
|
|
|
+ resp = await mp_bind(
|
|
|
+ request=MagicMock(),
|
|
|
+ body={"code": "oauth-code"},
|
|
|
+ db=db,
|
|
|
+ )
|
|
|
+
|
|
|
+ assert resp["code"] == 409
|
|
|
+ assert "已绑定其他账号" in resp["message"]
|
|
|
+ db.commit.assert_not_called()
|
|
|
+
|
|
|
+
|
|
|
+@pytest.mark.asyncio
|
|
|
+async def test_mp_bind_status_bound_true():
|
|
|
+ user = SimpleNamespace(id="u1", wx_mp_openid="mp-oid-1")
|
|
|
+ with (
|
|
|
+ patch(
|
|
|
+ "app.api.mini_auth._resolve_user_id",
|
|
|
+ new_callable=AsyncMock,
|
|
|
+ return_value="u1",
|
|
|
+ ),
|
|
|
+ patch("app.api.mini_auth.user_crud.get", return_value=user),
|
|
|
+ ):
|
|
|
+ from app.api.mini_auth import mp_bind_status
|
|
|
+
|
|
|
+ resp = await mp_bind_status(request=MagicMock(), db=MagicMock())
|
|
|
+
|
|
|
+ assert resp["code"] == 200
|
|
|
+ assert resp["data"]["bound"] is True
|
|
|
+
|
|
|
+
|
|
|
+@pytest.mark.asyncio
|
|
|
+async def test_mp_bind_unauthorized():
|
|
|
+ with patch(
|
|
|
+ "app.api.mini_auth._resolve_user_id",
|
|
|
+ new_callable=AsyncMock,
|
|
|
+ side_effect=HTTPException(status_code=401, detail="未授权"),
|
|
|
+ ):
|
|
|
+ from app.api.mini_auth import mp_bind
|
|
|
+
|
|
|
+ resp = await mp_bind(
|
|
|
+ request=MagicMock(),
|
|
|
+ body={"code": "x"},
|
|
|
+ db=MagicMock(),
|
|
|
+ )
|
|
|
+
|
|
|
+ assert resp["code"] == 401
|
|
|
+ assert resp["message"] == "未授权"
|