"""测试摘要键盘渲染:推广邮件的退订链接按钮必须显示且排最前。""" from src.tg_bot import _is_unsub_link, _link_buttons, _summary_keyboard def _rows_of(kb: dict) -> list[list[dict]]: return kb["inline_keyboard"] def test_promotion_with_unsub_link_shows_button_first(): """推广邮件:AI 提取到退订链接时必须显示,且排在第一行""" links = [ {"text": "Manage Preferences", "url": "https://e.example.com/pref"}, {"text": "推广活动详情", "url": "https://e.example.com/deal"}, ] kb = _summary_keyboard(can_reply=False, is_promotion=True, links=links) rows = _rows_of(kb) first = rows[0][0] assert first["url"] == "https://e.example.com/pref" assert "🔕" in first["text"] # 其余按钮仍然存在 texts = [b["text"] for r in rows for b in r] assert "告诉我更多" in texts assert "查看原文" in texts def test_promotion_without_links_keeps_default_buttons(): """推广邮件:没有链接时保持原有按钮""" kb = _summary_keyboard(can_reply=False, is_promotion=True, links=None) rows = _rows_of(kb) assert len(rows) == 2 assert rows[0][0]["text"] == "告诉我更多" assert rows[1][0]["text"] == "查看原文" def test_promotion_with_only_non_unsub_link(): """推广邮件:只有非退订链接时也正常渲染(🔗),不冒充退订按钮""" links = [{"text": "查看活动", "url": "https://e.example.com/event"}] kb = _summary_keyboard(can_reply=False, is_promotion=True, links=links) rows = _rows_of(kb) assert rows[0][0]["text"] == "🔗 查看活动" assert rows[1][0]["text"] == "告诉我更多" assert rows[2][0]["text"] == "查看原文" def test_normal_email_links_still_rendered(): """普通邮件:链接按钮行为不变""" links = [{"text": "查看账单", "url": "https://e.example.com/bill"}] kb = _summary_keyboard(can_reply=True, is_promotion=False, links=links) rows = _rows_of(kb) texts = [b["text"] for r in rows for b in r] assert "🔗 查看账单" in texts def test_unsub_detection_variants(): """退订链接识别:文字与 URL 关键词变体""" assert _is_unsub_link({"text": "Unsubscribe", "url": "https://x.com"}) assert _is_unsub_link({"text": "退订", "url": "https://x.com"}) assert _is_unsub_link({"text": "点击这里取消订阅", "url": "https://x.com"}) assert _is_unsub_link({"text": "管理偏好设置", "url": "https://x.com"}) assert _is_unsub_link({"text": "更多", "url": "https://x.com/unsubscribe"}) assert _is_unsub_link({"text": "Preferences", "url": "https://x.com/email-pref"}) assert not _is_unsub_link({"text": "查看详情", "url": "https://x.com/deal"}) def test_unsub_link_sorted_before_other_links(): """退订链接即使不在 links 第一位也会被排到最前""" links = [ {"text": "查看详情", "url": "https://x.com/a"}, {"text": "unsubscribe", "url": "https://x.com/b"}, ] rows = _link_buttons(links) assert rows[0][0]["url"] == "https://x.com/b" assert rows[1][0]["url"] == "https://x.com/a" def test_invalid_url_filtered(): """非法 URL(非 http、多重协议头、超长)被过滤""" links = [ {"text": "退订", "url": "javascript:alert(1)"}, {"text": "退订", "url": "https://x.com/https://evil.com"}, {"text": "退订", "url": "https://ok.example.com/unsub"}, ] rows = _link_buttons(links) assert len(rows) == 1 assert rows[0][0]["url"] == "https://ok.example.com/unsub"