222 lines
9.5 KiB
Python
222 lines
9.5 KiB
Python
"""平台规则引擎单元测试"""
|
||
import pytest
|
||
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# PlatformRuleEngine 测试
|
||
# ---------------------------------------------------------------------------
|
||
|
||
class TestPlatformRuleEngine:
|
||
@pytest.fixture
|
||
def engine(self):
|
||
from app.services.distribution.platform_rules import PlatformRuleEngine
|
||
return PlatformRuleEngine()
|
||
|
||
def test_get_platforms_returns_all(self, engine):
|
||
"""返回所有平台"""
|
||
platforms = engine.get_platforms()
|
||
assert len(platforms) >= 6
|
||
ids = {p["id"] for p in platforms}
|
||
# 验证包含核心平台
|
||
assert "wechat" in ids
|
||
assert "zhihu" in ids
|
||
assert "xiaohongshu" in ids
|
||
|
||
def test_get_platforms_fields(self, engine):
|
||
"""每个平台包含必要字段"""
|
||
platforms = engine.get_platforms()
|
||
required_fields = {"id", "name", "max_title_length", "max_content_length",
|
||
"min_content_length"}
|
||
for p in platforms:
|
||
assert required_fields.issubset(p.keys()), f"Platform {p.get('id')} missing fields: {required_fields - p.keys()}"
|
||
|
||
def test_validate_title_too_long(self, engine):
|
||
"""标题超长返回 high severity issue"""
|
||
# 微信公众号标题上限 22 字
|
||
title = "这个标题非常非常非常非常非常非常非常长超过了微信的限制"
|
||
content = "这是正文内容,字数足够满足最低要求。" * 30
|
||
result = engine.validate_content(content, title, "wechat")
|
||
|
||
assert result["is_valid"] is False
|
||
issues = result["issues"]
|
||
high_issues = [i for i in issues if i["severity"] == "high"]
|
||
assert any("标题" in i["message"] for i in high_issues)
|
||
|
||
def test_validate_content_too_short(self, engine):
|
||
"""内容过短返回 medium severity issue"""
|
||
title = "正常标题"
|
||
content = "短内容" # 远少于 300 字最低要求
|
||
result = engine.validate_content(content, title, "wechat")
|
||
|
||
issues = result["issues"]
|
||
assert any("内容长度" in i["message"] for i in issues)
|
||
|
||
def test_validate_valid_content(self, engine):
|
||
"""合规内容 score >= 90"""
|
||
title = "合规标题测试" # 长度在 22 字以内
|
||
content = "这是符合要求的内容。" * 50 # 足够长度,无违规
|
||
result = engine.validate_content(content, title, "wechat")
|
||
|
||
assert result["score"] >= 90
|
||
|
||
def test_validate_unknown_platform(self, engine):
|
||
"""未知平台返回 is_valid=False"""
|
||
result = engine.validate_content("content", "title", "unknown_platform_xyz")
|
||
assert result["is_valid"] is False
|
||
assert result["score"] == 0
|
||
|
||
def test_validate_wechat_external_link(self, engine):
|
||
"""微信正文包含外部链接返回 high severity issue"""
|
||
title = "标题"
|
||
content = "正文内容 " * 50 + " 点击 https://example.com/test 查看"
|
||
result = engine.validate_content(content, title, "wechat")
|
||
|
||
issues = result["issues"]
|
||
assert any("外部链接" in i["message"] for i in issues)
|
||
|
||
def test_validate_wechat_consecutive_symbols_in_title(self, engine):
|
||
"""微信标题含连续特殊符号返回 medium issue"""
|
||
title = "惊喜!!!三连"
|
||
content = "正文内容,满足最低字数要求。" * 30
|
||
result = engine.validate_content(content, title, "wechat")
|
||
|
||
issues = result["issues"]
|
||
assert any("连续特殊符号" in i["message"] for i in issues)
|
||
|
||
def test_get_optimization_tips_returns_list(self, engine):
|
||
"""get_optimization_tips 返回非空列表"""
|
||
tips = engine.get_optimization_tips("wechat")
|
||
assert isinstance(tips, list)
|
||
assert len(tips) > 0
|
||
|
||
def test_get_optimization_tips_unknown_platform(self, engine):
|
||
"""未知平台返回空列表"""
|
||
tips = engine.get_optimization_tips("unknown_xyz")
|
||
assert tips == []
|
||
|
||
# --- 标签验证测试 ---
|
||
|
||
def test_validate_tags_within_range(self, engine):
|
||
"""标签数量在范围内验证通过"""
|
||
title = "合规标题测试"
|
||
content = "这是符合要求的内容。" * 50
|
||
result = engine.validate_content(content, title, "wechat", tags=["标签1", "标签2", "标签3"])
|
||
|
||
tag_issues = [i for i in result["issues"] if i.get("category") == "tag_count"]
|
||
assert len(tag_issues) == 0
|
||
|
||
def test_validate_tags_below_minimum(self, engine):
|
||
"""标签数量低于最低要求返回 medium issue"""
|
||
title = "合规标题测试"
|
||
content = "这是符合要求的内容。" * 50
|
||
result = engine.validate_content(content, title, "wechat", tags=[])
|
||
|
||
tag_issues = [i for i in result["issues"] if i.get("category") == "tag_count"]
|
||
assert len(tag_issues) > 0
|
||
assert tag_issues[0]["severity"] == "medium"
|
||
assert "低于最低要求" in tag_issues[0]["message"]
|
||
|
||
def test_validate_tags_exceed_maximum(self, engine):
|
||
"""标签数量超过限制返回 high issue"""
|
||
title = "合规标题测试"
|
||
content = "这是符合要求的内容。" * 50
|
||
# 微信 max_tags=10,传入 15 个
|
||
many_tags = [f"标签{i}" for i in range(15)]
|
||
result = engine.validate_content(content, title, "wechat", tags=many_tags)
|
||
|
||
tag_issues = [i for i in result["issues"] if i.get("category") == "tag_count"]
|
||
assert len(tag_issues) > 0
|
||
assert tag_issues[0]["severity"] == "high"
|
||
assert "超过限制" in tag_issues[0]["message"]
|
||
|
||
def test_validate_tags_as_comma_separated_string(self, engine):
|
||
"""标签为逗号分隔字符串时正确解析"""
|
||
title = "合规标题测试"
|
||
content = "这是符合要求的内容。" * 50
|
||
result = engine.validate_content(content, title, "wechat", tags="标签1,标签2,标签3")
|
||
|
||
tag_issues = [i for i in result["issues"] if i.get("category") == "tag_count"]
|
||
assert len(tag_issues) == 0
|
||
|
||
def test_validate_no_tags_treated_as_zero(self, engine):
|
||
"""无标签时视为 0 个标签,低于 min_tags 时触发 issue"""
|
||
title = "合规标题测试"
|
||
content = "这是符合要求的内容。" * 50
|
||
result = engine.validate_content(content, title, "wechat", tags=None)
|
||
|
||
tag_issues = [i for i in result["issues"] if i.get("category") == "tag_count"]
|
||
# 微信 min_tags=3,tags=None 时 tag_count=0 < 3,触发 medium issue
|
||
assert len(tag_issues) > 0
|
||
assert tag_issues[0]["severity"] == "medium"
|
||
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# ContentFormatter 测试
|
||
# ---------------------------------------------------------------------------
|
||
|
||
class TestContentFormatter:
|
||
@pytest.fixture
|
||
def formatter(self):
|
||
from app.services.distribution.formatter import ContentFormatter
|
||
return ContentFormatter()
|
||
|
||
def test_formatter_xiaohongshu_strips_markdown(self, formatter):
|
||
"""小红书格式:移除 Markdown 标记"""
|
||
md_content = "# 标题\n\n**加粗内容**\n\n[链接文本](http://example.com)"
|
||
result = formatter.format_for_platform(md_content, "xiaohongshu")
|
||
|
||
assert "**" not in result
|
||
assert "#" not in result or result.strip().startswith("#") is False
|
||
assert "http://example.com" not in result
|
||
|
||
def test_formatter_wechat_removes_links(self, formatter):
|
||
"""微信格式:去除非公众号外链,保留链接文本"""
|
||
content = "请访问 [示例网站](https://external-site.com/page) 了解详情"
|
||
result = formatter.format_for_platform(content, "wechat")
|
||
|
||
# 外部链接被移除,但文本保留
|
||
assert "https://external-site.com" not in result
|
||
assert "示例网站" in result
|
||
|
||
def test_formatter_wechat_converts_headers(self, formatter):
|
||
"""微信格式:Markdown 标题转 HTML"""
|
||
content = "## 二级标题\n\n正文内容"
|
||
result = formatter.format_for_platform(content, "wechat")
|
||
|
||
assert "<h2>" in result
|
||
assert "二级标题" in result
|
||
|
||
def test_formatter_zhihu_keeps_markdown(self, formatter):
|
||
"""知乎格式:保留 Markdown 结构"""
|
||
content = "## 知乎标题\n\n**加粗内容**\n\n`代码示例`"
|
||
result = formatter.format_for_platform(content, "zhihu")
|
||
|
||
# 知乎支持 Markdown,应保留
|
||
assert "**加粗内容**" in result
|
||
|
||
def test_formatter_default_strips_markdown(self, formatter):
|
||
"""默认格式:清理 Markdown 为纯文本"""
|
||
content = "# 标题\n\n**加粗**\n\n[链接](http://test.com)"
|
||
result = formatter.format_for_platform(content, "unknown_platform")
|
||
|
||
assert "#" not in result.split("\n")[0] # 标题标记被移除
|
||
assert "**" not in result
|
||
assert "http://test.com" not in result
|
||
|
||
def test_formatter_xiaohongshu_tags_moved_to_end(self, formatter):
|
||
"""小红书格式:话题标签移到文末"""
|
||
content = "正文内容 #话题1 更多内容 #话题2"
|
||
result = formatter.format_for_platform(content, "xiaohongshu")
|
||
|
||
lines = result.strip().split("\n")
|
||
last_line = lines[-1]
|
||
# 最后一行包含标签
|
||
assert "#话题1" in last_line or "#话题2" in last_line
|
||
|
||
def test_formatter_wechat_bold_to_strong(self, formatter):
|
||
"""微信格式:**加粗** 转为 <strong>"""
|
||
content = "这是**重要内容**需要加粗"
|
||
result = formatter.format_for_platform(content, "wechat")
|
||
|
||
assert "<strong>重要内容</strong>" in result
|