- App_Store 完成后同样发送钉钉通知:标题改为「【iOS】App_Store 包信息」, 下载链接为 IPA 地址,不带二维码。 - 钉钉 markdown 消息需在正文出现 @手机号 才会真正 @ 到人,故除 at.atMobiles 外同时在正文追加。 - 新增 upload.dingtalk.at_mobiles(Ad_Hoc)与 at_mobiles_app_store(App_Store) 两份名单,按 BUILD_TYPE 选用;配置页「钉钉通知」区新增两个输入框。 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YZULZ8fkiaiJrxqf318xTn
134 lines
5.1 KiB
Python
134 lines
5.1 KiB
Python
"""钉钉通知测试。"""
|
||
from unittest.mock import MagicMock, patch
|
||
|
||
import pytest
|
||
|
||
from backend.services.notification import NotificationError, build_dingtalk_payload, send_dingtalk_notification
|
||
|
||
|
||
def _build_config():
|
||
return {
|
||
"SERVER": "测试环境",
|
||
"VERSION": "2.195.0",
|
||
"APPID_NAME": "阅门户测试App",
|
||
"BUNDLE_ID": "cn.touchv.a4YX061",
|
||
"APPID": "647372741900537856",
|
||
}
|
||
|
||
|
||
def test_dingtalk_payload_matches_autopacking_content():
|
||
payload = build_dingtalk_payload(
|
||
_build_config(),
|
||
"https://download.example.com/app.html",
|
||
"https://download.example.com/app.png",
|
||
)
|
||
|
||
text = payload["markdown"]["text"]
|
||
assert payload["markdown"]["title"] == "iOS应用下载"
|
||
assert "## 【iOS】打包信息" in text
|
||
assert "**环境:** 测试环境" in text
|
||
assert "**版本:** 2.195.0" in text
|
||
assert "**APP名称:** 阅门户测试App" in text
|
||
assert "**包名:** cn.touchv.a4YX061" in text
|
||
assert "**App Guid:** 647372741900537856" in text
|
||
assert "**iOS 下载链接:** https://download.example.com/app.html" in text
|
||
assert "" in text
|
||
|
||
|
||
def test_dingtalk_payload_at_mobiles_appended_to_text_and_at_field():
|
||
payload = build_dingtalk_payload(
|
||
_build_config(),
|
||
"https://download.example.com/app.html",
|
||
at_mobiles=["13800000000", " 13900000001 ", "13800000000", ""],
|
||
)
|
||
|
||
# 去重去空后写入 at.atMobiles,并在正文追加 @手机号(钉钉 markdown 必需)
|
||
assert payload["at"]["atMobiles"] == ["13800000000", "13900000001"]
|
||
assert "@13800000000" in payload["markdown"]["text"]
|
||
assert "@13900000001" in payload["markdown"]["text"]
|
||
|
||
|
||
def test_dingtalk_payload_without_at_mobiles_has_empty_at():
|
||
payload = build_dingtalk_payload(_build_config(), "https://download.example.com/app.html")
|
||
assert payload["at"]["atMobiles"] == []
|
||
assert "@" not in payload["markdown"]["text"]
|
||
|
||
|
||
def test_dingtalk_notification_forwards_at_mobiles():
|
||
response = MagicMock(status_code=200)
|
||
response.json.return_value = {"errcode": 0}
|
||
with patch("backend.services.notification.httpx.post", return_value=response) as post:
|
||
send_dingtalk_notification(
|
||
{
|
||
"enabled": True,
|
||
"webhook_url": "https://example.com/robot",
|
||
"at_mobiles": ["13800000000", "13900000001"],
|
||
},
|
||
_build_config(),
|
||
"https://download.example.com/app.html",
|
||
)
|
||
|
||
sent = post.call_args.kwargs["json"]
|
||
assert sent["at"]["atMobiles"] == ["13800000000", "13900000001"]
|
||
assert "@13800000000" in sent["markdown"]["text"]
|
||
|
||
|
||
def test_dingtalk_payload_app_store_heading_and_no_qr():
|
||
config = {**_build_config(), "BUILD_TYPE": "App_Store"}
|
||
# App_Store 流程不传二维码地址(publish_ipa 返回空串)
|
||
payload = build_dingtalk_payload(
|
||
config, "https://oss.example.com/readoor/iOS/app_2_197_1.ipa"
|
||
)
|
||
text = payload["markdown"]["text"]
|
||
assert "## 【iOS】App_Store 包信息" in text
|
||
assert "## 【iOS】打包信息" not in text
|
||
assert "**iOS 下载链接:** https://oss.example.com/readoor/iOS/app_2_197_1.ipa" in text
|
||
assert "![image]" not in text
|
||
|
||
|
||
def test_dingtalk_notification_app_store_uses_own_at_list():
|
||
response = MagicMock(status_code=200)
|
||
response.json.return_value = {"errcode": 0}
|
||
dingtalk_config = {
|
||
"enabled": True,
|
||
"webhook_url": "https://example.com/robot",
|
||
"at_mobiles": ["13800000000"],
|
||
"at_mobiles_app_store": ["13911111111", "13922222222"],
|
||
}
|
||
with patch("backend.services.notification.httpx.post", return_value=response) as post:
|
||
send_dingtalk_notification(
|
||
dingtalk_config,
|
||
{**_build_config(), "BUILD_TYPE": "App_Store"},
|
||
"https://oss.example.com/app.ipa",
|
||
)
|
||
|
||
sent = post.call_args.kwargs["json"]
|
||
assert sent["at"]["atMobiles"] == ["13911111111", "13922222222"]
|
||
assert "## 【iOS】App_Store 包信息" in sent["markdown"]["text"]
|
||
assert "@13800000000" not in sent["markdown"]["text"]
|
||
|
||
|
||
def test_dingtalk_notification_skips_when_disabled():
|
||
with patch("backend.services.notification.httpx.post") as post:
|
||
assert send_dingtalk_notification({}, _build_config(), "https://download.example.com/app") is False
|
||
post.assert_not_called()
|
||
|
||
|
||
def test_dingtalk_notification_sends_markdown():
|
||
response = MagicMock(status_code=200)
|
||
response.json.return_value = {"errcode": 0}
|
||
with patch("backend.services.notification.httpx.post", return_value=response) as post:
|
||
assert send_dingtalk_notification(
|
||
{"enabled": True, "webhook_url": "https://example.com/robot"},
|
||
_build_config(),
|
||
"https://download.example.com/app.html",
|
||
"https://download.example.com/app.png",
|
||
) is True
|
||
|
||
assert post.call_args.kwargs["json"]["markdown"]["title"] == "iOS应用下载"
|
||
|
||
|
||
def test_dingtalk_notification_requires_webhook_when_enabled():
|
||
with pytest.raises(NotificationError, match="Webhook"):
|
||
send_dingtalk_notification({"enabled": True}, _build_config(), "https://download.example.com/app")
|