"""钉钉通知测试。""" 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 "![image](https://download.example.com/app.png)" 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")