Files

213 lines
8.3 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""工具层测试:notifier 推送逻辑(mock外部HTTP"""
import json
from unittest.mock import patch, MagicMock
import pytest
from app.utils.notifier import (
send_wecom_robot,
send_wecom_app,
send_mail,
push_alert,
_build_content,
push_pending_alerts,
)
class TestWecomRobot:
"""企业微信群机器人推送测试(mock HTTP"""
@patch("urllib.request.urlopen")
def test_send_success(self, mock_urlopen):
"""推送成功"""
mock_resp = MagicMock()
mock_resp.read.return_value = json.dumps({"errcode": 0}).encode()
mock_urlopen.return_value.__enter__.return_value = mock_resp
result = send_wecom_robot("https://qyapi.weixin.qq.com/webhook/test",
"测试标题", "测试内容", "red")
assert result["success"] is True
assert "已推送" in result["message"]
@patch("urllib.request.urlopen")
def test_send_api_error(self, mock_urlopen):
"""企微API返回错误"""
mock_resp = MagicMock()
mock_resp.read.return_value = json.dumps({"errcode": 40001, "errmsg": "invalid webhook"}).encode()
mock_urlopen.return_value.__enter__.return_value = mock_resp
result = send_wecom_robot("https://qyapi.weixin.qq.com/webhook/test",
"测试", "内容", "yellow")
assert result["success"] is False
assert "推送失败" in result["message"]
@patch("urllib.request.urlopen")
def test_send_network_error(self, mock_urlopen):
"""网络异常"""
mock_urlopen.side_effect = Exception("Connection refused")
result = send_wecom_robot("https://qyapi.weixin.qq.com/webhook/test",
"测试", "内容", "green")
assert result["success"] is False
assert "推送异常" in result["message"]
def test_build_content(self):
"""_build_content 内容构建"""
alert = {
"kpi_name": "销售总额",
"period": "2026-06",
"actual_value": "800,000",
"target_value": "1,000,000",
"alert_level": "red",
"resolution": "加强销售推广",
}
content = _build_content(alert)
assert "销售总额" in content
assert "2026-06" in content
assert "800,000" in content
assert "加强销售推广" in content
assert "紧急" in content
class TestPushAlert:
"""主推送函数测试"""
@patch("app.utils.notifier.send_wecom_robot")
def test_push_to_wecom_robot(self, mock_send):
"""推送到企微机器人渠道"""
mock_send.return_value = {"success": True, "message": "ok"}
channels = [{
"name": "测试群", "channel_type": "wecom", "enabled": True,
"config": {"webhook_url": "https://qyapi.weixin.qq.com/webhook/abc"}
}]
results = push_alert({"alert_level": "yellow", "alert_message": "测试"}, channels)
assert len(results) == 1
assert results[0]["channel"] == "wecom"
assert results[0]["success"] is True
@patch("app.utils.notifier.send_wecom_app")
def test_push_to_wecom_app(self, mock_send):
"""推送到企微应用消息"""
mock_send.return_value = {"success": True, "message": "ok"}
channels = [{
"name": "应用通知", "channel_type": "wecom_app", "enabled": True,
"config": {"corp_id": "xxx", "corp_secret": "yyy", "agent_id": "1000002", "touser": "@all"}
}]
results = push_alert({"alert_level": "yellow", "alert_message": "测试"}, channels)
assert len(results) == 1
assert results[0]["channel"] == "wecom_app"
@patch("app.utils.notifier.send_mail")
def test_push_to_mail(self, mock_send):
"""推送到邮件"""
mock_send.return_value = {"success": True, "message": "ok"}
channels = [{
"name": "通知邮箱", "channel_type": "mail", "enabled": True,
"config": {"to": ["admin@example.com"]}
}]
results = push_alert({"alert_level": "red", "alert_message": "测试"}, channels)
assert len(results) == 1
assert results[0]["channel"] == "mail"
def test_disabled_channel_skipped(self):
"""已禁用的渠道跳过"""
channels = [{
"name": "禁用渠道", "channel_type": "wecom", "enabled": False,
"config": {}
}]
results = push_alert({"alert_level": "yellow", "alert_message": "测试"}, channels)
assert len(results) == 0
def test_empty_channels(self):
"""无渠道 → 空结果"""
results = push_alert({"alert_level": "yellow", "alert_message": "测试"}, [])
assert results == []
class TestSendWecomApp:
"""企微应用消息测试"""
@patch("requests.get")
@patch("requests.post")
def test_app_send_success(self, mock_post, mock_get):
"""企微应用消息发送成功"""
mock_get.return_value.json.return_value = {"errcode": 0, "access_token": "fake_token"}
mock_post.return_value.json.return_value = {"errcode": 0}
result = send_wecom_app("corp123", "secret456", "1000001",
"@all", "标题", "内容", "yellow")
assert result["success"] is True
@patch("requests.get")
def test_app_token_fail(self, mock_get):
"""获取token失败"""
mock_get.return_value.json.return_value = {"errcode": 40013, "errmsg": "invalid corpid"}
result = send_wecom_app("bad_corp", "bad_secret", "1000001",
"@all", "标题", "内容", "yellow")
assert result["success"] is False
assert "获取token失败" in result["message"]
class TestSendMail:
"""邮件推送测试(mock SMTP"""
@patch("smtplib.SMTP_SSL")
def test_mail_send_success(self, mock_smtp):
"""邮件发送成功"""
mock_server = MagicMock()
mock_smtp.return_value = mock_server
config = {"host": "smtp.example.com", "port": 465, "user": "user@example.com",
"password": "pass", "use_ssl": True, "from_addr": "user@example.com"}
result = send_mail(config, ["admin@example.com"], "测试标题", "测试内容")
assert result["success"] is True
mock_server.login.assert_called_once()
mock_server.sendmail.assert_called_once()
@patch("smtplib.SMTP_SSL")
def test_mail_send_failure(self, mock_smtp):
"""邮件发送异常"""
mock_smtp.side_effect = Exception("SMTP server error")
config = {"host": "smtp.example.com", "port": 465, "user": "user@example.com",
"password": "pass", "use_ssl": True, "from_addr": "user@example.com"}
result = send_mail(config, ["admin@example.com"], "测试标题", "测试内容")
assert result["success"] is False
assert "邮件发送失败" in result["message"]
class TestPushPendingAlerts:
"""push_pending_alerts 测试"""
def test_no_channels(self, db):
"""没有渠道 → 返回0"""
count = push_pending_alerts(db)
assert count == 0
def test_no_pending_alerts(self, db):
"""没有待处理预警 → 返回0"""
from app.models import NotificationChannel
db.add(NotificationChannel(name="测试群", channel_type="wecom", config={}, enabled=True))
db.commit()
count = push_pending_alerts(db)
assert count == 0
@patch("app.utils.notifier.push_alert")
def test_push_pending_with_data(self, mock_push, db):
"""有渠道+待处理预警 → 尝试推送"""
from app.models import NotificationChannel, KPIAlert, KPIDefinition
kpi = KPIDefinition(kpi_code="TEST_PUSH", kpi_name="推送测试", dimension="finance", status="active")
db.add(kpi)
db.commit()
db.add(NotificationChannel(name="测试群", channel_type="wecom",
config={"webhook_url": "http://test"}, enabled=True))
db.add(KPIAlert(kpi_id=kpi.id, alert_level="red", alert_message="测试预警", status="pending"))
db.commit()
mock_push.return_value = [{"channel": "wecom", "channel_name": "测试群", "success": True, "message": "ok"}]
count = push_pending_alerts(db)
mock_push.assert_called_once()
assert count == 1