"""YouTube 模块单元测试"""
import pytest
from datetime import datetime, timezone, timedelta
from src.models.video import VideoData
from src.youtube.analyzer import ViralAnalyzer
class TestViralAnalyzer:
"""爆款分析器测试"""
def test_calculate_hours_since_publish(self):
"""测试发布时长计算"""
# 2 小时前发布
published_at = datetime.now(timezone.utc) - timedelta(hours=2)
hours = ViralAnalyzer.calculate_hours_since_publish(published_at)
assert 1.9 <= hours <= 2.1 # 允许小误差
def test_calculate_vph(self):
"""测试 VPH 计算"""
# 10,000 播放量,2 小时
vph = ViralAnalyzer.calculate_vph(10000, 2)
assert vph == 5000
# 边界情况:0 小时
vph = ViralAnalyzer.calculate_vph(1000, 0)
assert vph == 10000 # 至少算作 0.1 小时
def test_calculate_engagement_rate(self):
"""测试互动率计算"""
# 1000 播放,50 点赞,10 评论
rate = ViralAnalyzer.calculate_engagement_rate(1000, 50, 10)
assert rate == 6.0 # (50+10)/1000 * 100 = 6%
# 边界情况:0 播放
rate = ViralAnalyzer.calculate_engagement_rate(0, 10, 5)
assert rate == 0.0
def test_calculate_viral_score(self):
"""测试爆款指数计算"""
# VPH=5000, 2小时前, 互动率=5%
score = ViralAnalyzer.calculate_viral_score(5000, 2, 5)
# 时效权重 = 1/(2+1) = 0.333
# 互动权重 = 1 + (5/100*10) = 1.5
# score = 5000 * 0.333 * 1.5 ≈ 2500
assert 2400 <= score <= 2600
def test_categorize_potential(self):
"""测试潜力等级分类"""
assert "超级爆款" in ViralAnalyzer.categorize_potential(15000)
assert "高潜力" in ViralAnalyzer.categorize_potential(6000)
assert "潜力视频" in ViralAnalyzer.categorize_potential(2000)
assert "普通视频" in ViralAnalyzer.categorize_potential(500)
def test_analyze_video(self):
"""测试视频分析"""
# 创建测试视频
video = VideoData(
video_id="test123",
title="测试视频",
channel_name="测试频道",
channel_id="channel123",
views=10000,
likes=500,
comments=100,
published_at=datetime.now(timezone.utc) - timedelta(hours=2),
duration="PT30S",
url="https://youtube.com/shorts/test123"
)
# 分析视频
analyzed = ViralAnalyzer.analyze_video(video)
# 验证指标已填充
assert analyzed.vph > 0
assert analyzed.engagement_rate > 0
assert analyzed.viral_score > 0
assert analyzed.hours_since_publish > 0
def test_rank_videos(self):
"""测试视频排序"""
videos = [
VideoData(
video_id=f"test{i}",
title=f"视频{i}",
channel_name="频道",
channel_id="ch1",
views=1000,
likes=10,
comments=5,
published_at=datetime.now(timezone.utc),
duration="PT30S",
url=f"https://youtube.com/shorts/test{i}",
viral_score=float(i * 100)
)
for i in range(5)
]
ranked = ViralAnalyzer.rank_videos(videos)
# 验证降序排列
assert ranked[0].viral_score == 400
assert ranked[-1].viral_score == 0
def test_filter_by_vph(self):
"""测试 VPH 筛选"""
videos = [
VideoData(
video_id=f"test{i}",
title=f"视频{i}",
channel_name="频道",
channel_id="ch1",
views=1000,
likes=10,
comments=5,
published_at=datetime.now(timezone.utc),
duration="PT30S",
url=f"https://youtube.com/shorts/test{i}",
vph=float(i * 1000)
)
for i in range(5)
]
filtered = ViralAnalyzer.filter_by_vph(videos, 2000)
# 应该只保留 VPH >= 2000 的视频
assert len(filtered) == 3 # vph=2000, 3000, 4000
assert all(v.vph >= 2000 for v in filtered)
class TestVideoData:
"""视频数据模型测试"""
def test_to_markdown_row(self):
"""测试 Markdown 行生成"""
video = VideoData(
video_id="test123",
title="测试视频标题",
channel_name="测试频道",
channel_id="ch1",
views=10000,
likes=500,
comments=100,
published_at=datetime.now(timezone.utc),
duration="PT30S",
url="https://youtube.com/shorts/test123",
vph=5000,
engagement_rate=6.0,
viral_score=2500,
hours_since_publish=2.0
)
row = video.to_markdown_row()
assert "测试视频标题" in row
assert "测试频道" in row
assert "10,000" in row
assert "5,000" in row
assert "6.00%" in row
assert "2,500" in row
def test_markdown_header(self):
"""测试 Markdown 表头"""
header = VideoData.markdown_header()
assert "标题" in header
assert "VPH" in header
assert "互动率" in header
assert "爆款指数" in header
if __name__ == "__main__":
pytest.main([__file__, "-v"])