| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264 |
- from unittest.mock import MagicMock, patch
- from app.core.config import settings
- def test_send_approval_requires_mp_openid():
- from app.services.wechat_mp_message import send_oa_approval_notify
- r = send_oa_approval_notify(
- mp_openid="",
- pagepath="pages/oa/webview/index?instanceId=i1&taskId=t1&mode=approve",
- title="请假",
- applicant="张三",
- node_name="主管审批",
- time_text="2026-08-13 12:00",
- )
- assert r.get("success") is False
- assert r.get("error") == "missing_mp_openid"
- def test_send_approval_requires_template_id():
- from app.services.wechat_mp_message import send_oa_approval_notify
- with patch.object(settings, "WX_MP_TEMPLATE_APPROVAL", ""):
- r = send_oa_approval_notify(
- mp_openid="mp_openid_1",
- pagepath="pages/oa/webview/index?instanceId=i1&taskId=t1&mode=approve",
- title="请假",
- applicant="张三",
- node_name="主管审批",
- time_text="2026-08-13 12:00",
- )
- assert r.get("success") is False
- assert r.get("error") == "missing_template_id"
- @patch("app.services.wechat_mp_message.get_mp_access_token", return_value="tok_mp")
- @patch("app.services.wechat_mp_message.httpx.Client")
- def test_send_approval_success_payload_shape(mock_client_cls, _mock_token):
- from app.services.wechat_mp_message import send_oa_approval_notify
- mock_resp = MagicMock()
- mock_resp.raise_for_status = MagicMock()
- mock_resp.json.return_value = {"errcode": 0, "msgid": 1001}
- mock_client = MagicMock()
- mock_client.__enter__.return_value = mock_client
- mock_client.__exit__.return_value = False
- mock_client.post.return_value = mock_resp
- mock_client_cls.return_value = mock_client
- long_title = "这是一个非常非常长的审批标题需要截断处理一下才行"
- with patch.object(settings, "WX_MP_TEMPLATE_APPROVAL", "tpl_approval"), patch.object(
- settings, "WXPAY_MINI_APPID", "wx_mini_appid"
- ), patch.object(settings, "WX_MP_TPL_FIELD_TITLE", "thing12"), patch.object(
- settings, "WX_MP_TPL_FIELD_APPLICANT", "thing21"
- ), patch.object(settings, "WX_MP_TPL_FIELD_NODE", "thing7"), patch.object(
- settings, "WX_MP_TPL_FIELD_TIME", "time3"
- ), patch.object(settings, "WX_MP_TPL_FIELD_PROCESS", "thing48"):
- r = send_oa_approval_notify(
- mp_openid="mp_openid_1",
- pagepath="/pages/oa/webview/index?instanceId=i1&taskId=t1&mode=approve",
- title=long_title,
- applicant="张三",
- node_name="主管审批",
- time_text="2026-08-13 12:00",
- process_name="请假申请",
- )
- assert r == {"success": True, "msgid": 1001}
- assert mock_client.post.call_count == 1
- args, kwargs = mock_client.post.call_args
- assert args[0].startswith(
- "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token="
- )
- payload = kwargs["json"]
- assert payload["touser"] == "mp_openid_1"
- assert payload["template_id"] == "tpl_approval"
- assert payload["miniprogram"] == {
- "appid": "wx_mini_appid",
- "pagepath": "pages/oa/webview/index?instanceId=i1&taskId=t1&mode=approve",
- }
- assert payload["data"]["thing12"]["value"] == long_title[:20]
- assert payload["data"]["thing21"]["value"] == "张三"
- assert payload["data"]["thing7"]["value"] == "主管审批"
- assert payload["data"]["time3"]["value"] == "2026-08-13 12:00"
- assert payload["data"]["thing48"]["value"] == "请假申请"
- @patch("app.services.wechat_mp_message.get_mp_access_token", return_value="tok_mp")
- @patch("app.services.wechat_mp_message.httpx.Client")
- def test_send_approval_skips_blank_field_keys(mock_client_cls, _mock_token):
- """临时模板仅申请人+时间时,TITLE/NODE 留空不进 data。"""
- from app.services.wechat_mp_message import send_oa_approval_notify
- mock_resp = MagicMock()
- mock_resp.raise_for_status = MagicMock()
- mock_resp.json.return_value = {"errcode": 0, "msgid": 3003}
- mock_client = MagicMock()
- mock_client.__enter__.return_value = mock_client
- mock_client.__exit__.return_value = False
- mock_client.post.return_value = mock_resp
- mock_client_cls.return_value = mock_client
- with patch.object(settings, "WX_MP_TEMPLATE_APPROVAL", "tpl_tmp"), patch.object(
- settings, "WXPAY_MINI_APPID", "wx_mini_appid"
- ), patch.object(settings, "WX_MP_TPL_FIELD_TITLE", ""), patch.object(
- settings, "WX_MP_TPL_FIELD_APPLICANT", "thing1"
- ), patch.object(settings, "WX_MP_TPL_FIELD_NODE", ""), patch.object(
- settings, "WX_MP_TPL_FIELD_TIME", "time2"
- ), patch.object(settings, "WX_MP_TPL_FIELD_PROCESS", ""):
- r = send_oa_approval_notify(
- mp_openid="mp_openid_1",
- pagepath="pages/oa/webview/index?instanceId=i1&taskId=t1&mode=approve",
- title="请假",
- applicant="张三",
- node_name="主管审批",
- time_text="2026-08-13 12:00",
- )
- assert r.get("success") is True
- data = mock_client.post.call_args.kwargs["json"]["data"]
- assert data == {
- "thing1": {"value": "张三"},
- "time2": {"value": "2026-08-13 12:00"},
- }
- @patch("app.services.wechat_mp_message.get_mp_access_token", return_value="tok_mp")
- @patch("app.services.wechat_mp_message.httpx.Client")
- def test_send_cc_success_payload_shape(mock_client_cls, _mock_token):
- from app.services.wechat_mp_message import send_oa_cc_notify
- mock_resp = MagicMock()
- mock_resp.raise_for_status = MagicMock()
- mock_resp.json.return_value = {"errcode": 0, "msgid": 2002}
- mock_client = MagicMock()
- mock_client.__enter__.return_value = mock_client
- mock_client.__exit__.return_value = False
- mock_client.post.return_value = mock_resp
- mock_client_cls.return_value = mock_client
- with patch.object(settings, "WX_MP_TEMPLATE_CC", "tpl_cc"), patch.object(
- settings, "WXPAY_MINI_APPID", "wx_mini_appid"
- ), patch.object(settings, "WX_MP_TPL_CC_FIELD_TITLE", "thing5"), patch.object(
- settings, "WX_MP_TPL_CC_FIELD_APPLICANT", "thing2"
- ), patch.object(settings, "WX_MP_TPL_CC_FIELD_TIME", "time3"), patch.object(
- settings, "WX_MP_TPL_CC_FIELD_PROCESS", "thing1"
- ):
- r = send_oa_cc_notify(
- mp_openid="mp_openid_2",
- pagepath="pages/oa/webview/index?instanceId=i2&taskId=t2&mode=view",
- title="李四 - 报销申请",
- applicant="李四",
- node_name="抄送知会",
- time_text="2026-08-13 15:30",
- process_name="报销申请",
- )
- assert r == {"success": True, "msgid": 2002}
- payload = mock_client.post.call_args.kwargs["json"]
- assert payload["touser"] == "mp_openid_2"
- assert payload["template_id"] == "tpl_cc"
- assert payload["miniprogram"]["appid"] == "wx_mini_appid"
- assert payload["miniprogram"]["pagepath"].endswith("mode=view")
- assert payload["data"] == {
- "thing5": {"value": "李四 - 报销申请"},
- "thing2": {"value": "李四"},
- "time3": {"value": "2026-08-13 15:30"},
- "thing1": {"value": "报销申请"},
- }
- @patch("app.services.wechat_mp_message.get_mp_access_token", return_value="tok_mp")
- @patch("app.services.wechat_mp_message.httpx.Client")
- def test_send_applicant_progress_payload_shape(mock_client_cls, _mock_token):
- from app.services.wechat_mp_message import send_oa_applicant_progress_notify
- mock_resp = MagicMock()
- mock_resp.raise_for_status = MagicMock()
- mock_resp.json.return_value = {"errcode": 0, "msgid": 5005}
- mock_client = MagicMock()
- mock_client.__enter__.return_value = mock_client
- mock_client.__exit__.return_value = False
- mock_client.post.return_value = mock_resp
- mock_client_cls.return_value = mock_client
- with patch.object(
- settings, "WX_MP_TEMPLATE_APPLICANT_PROGRESS", "tpl_progress"
- ), patch.object(settings, "WXPAY_MINI_APPID", "wx_mini_appid"), patch.object(
- settings, "WX_MP_TPL_PROGRESS_FIELD_BUSINESS", "thing5"
- ), patch.object(
- settings, "WX_MP_TPL_PROGRESS_FIELD_PROCESS", "thing1"
- ), patch.object(
- settings, "WX_MP_TPL_PROGRESS_FIELD_APPROVER", "thing6"
- ), patch.object(
- settings, "WX_MP_TPL_PROGRESS_FIELD_RESULT", "const4"
- ), patch.object(settings, "WX_MP_TPL_PROGRESS_FIELD_TIME", "time11"):
- r = send_oa_applicant_progress_notify(
- mp_openid="mp_ini",
- pagepath="pages/oa/webview/index?instanceId=i1&taskId=t1&mode=view",
- business_name="张三 - 请假申请",
- process_name="请假申请",
- approver_name="李四",
- result="通过",
- time_text="2026-08-14 14:30",
- )
- assert r == {"success": True, "msgid": 5005}
- payload = mock_client.post.call_args.kwargs["json"]
- assert payload["template_id"] == "tpl_progress"
- assert payload["data"] == {
- "thing5": {"value": "张三 - 请假申请"},
- "thing1": {"value": "请假申请"},
- "thing6": {"value": "李四"},
- "const4": {"value": "通过"},
- "time11": {"value": "2026-08-14 14:30"},
- }
- @patch("app.services.wechat_mp_message.get_mp_access_token", return_value="tok_mp")
- @patch("app.services.wechat_mp_message.httpx.Client")
- def test_send_approval_retries_without_miniprogram_on_40165(
- mock_client_cls, _mock_token
- ):
- from app.services.wechat_mp_message import send_oa_approval_notify
- mock_resp_fail = MagicMock()
- mock_resp_fail.raise_for_status = MagicMock()
- mock_resp_fail.json.return_value = {
- "errcode": 40165,
- "errmsg": "invalid weapp pagepath",
- }
- mock_resp_ok = MagicMock()
- mock_resp_ok.raise_for_status = MagicMock()
- mock_resp_ok.json.return_value = {"errcode": 0, "msgid": 4004}
- mock_client = MagicMock()
- mock_client.__enter__.return_value = mock_client
- mock_client.__exit__.return_value = False
- mock_client.post.side_effect = [mock_resp_fail, mock_resp_ok]
- mock_client_cls.return_value = mock_client
- with patch.object(settings, "WX_MP_TEMPLATE_APPROVAL", "tpl_approval"), patch.object(
- settings, "WXPAY_MINI_APPID", "wx_mini_appid"
- ), patch.object(settings, "WX_MP_TPL_FIELD_TITLE", ""), patch.object(
- settings, "WX_MP_TPL_FIELD_APPLICANT", "thing1"
- ), patch.object(settings, "WX_MP_TPL_FIELD_NODE", ""), patch.object(
- settings, "WX_MP_TPL_FIELD_TIME", "time2"
- ), patch.object(settings, "WX_MP_TPL_FIELD_PROCESS", ""):
- r = send_oa_approval_notify(
- mp_openid="mp_openid_1",
- pagepath="pages/oa/webview/index?instanceId=i1&taskId=t1&mode=approve",
- title="请假",
- applicant="张三",
- node_name="主管审批",
- time_text="2026-08-13 12:00",
- )
- assert r == {"success": True, "msgid": 4004}
- assert mock_client.post.call_count == 2
- first = mock_client.post.call_args_list[0].kwargs["json"]
- second = mock_client.post.call_args_list[1].kwargs["json"]
- assert "miniprogram" in first
- assert "miniprogram" not in second
|