305 lines
12 KiB
Go
305 lines
12 KiB
Go
package utils
|
||
|
||
import (
|
||
"fmt"
|
||
"os"
|
||
"path/filepath"
|
||
"testing"
|
||
)
|
||
|
||
// getProjectRoot 获取项目根目录
|
||
func getProjectRoot() string {
|
||
// 假设测试从项目根目录运行
|
||
dir, _ := os.Getwd()
|
||
// 向上查找 go.mod 确定项目根目录
|
||
for {
|
||
if _, err := os.Stat(filepath.Join(dir, "go.mod")); err == nil {
|
||
return dir
|
||
}
|
||
parent := filepath.Dir(dir)
|
||
if parent == dir {
|
||
break
|
||
}
|
||
dir = parent
|
||
}
|
||
return ""
|
||
}
|
||
|
||
// TestGenerateCompetitorReportPDF 测试生成竞品报告PDF
|
||
func TestGenerateCompetitorReportPDF1(t *testing.T) {
|
||
// 获取项目根目录
|
||
root := getProjectRoot()
|
||
fmt.Printf("项目根目录: %s\n", root)
|
||
|
||
// 准备测试数据
|
||
data := CompetitorReportData{
|
||
HighlightAnalysis: HighlightAnalysisData{
|
||
Summary: "本视频通过展示产品使用的真实场景,突出用户产品优势和痛点,内容详实且具有吸引力。",
|
||
Points: PointsData{
|
||
Theme: "标题简洁有力,突出核心卖点'省时省力',引发用户好奇心",
|
||
Narrative: "采用情景剧形式展示产品使用场景,剧情贴近生活,易引发共鸣",
|
||
Content: "通过前后对比展示产品效果,直观呈现产品价值",
|
||
Copywriting: "文案简洁明了,突出用户痛点解决方案,语气亲切自然",
|
||
Data: "点赞量10万+,评论5000+,分享2万+,数据表现优异",
|
||
Music: "背景音乐节奏轻快,与视频内容匹配度高,增强观看体验",
|
||
},
|
||
},
|
||
DataPerformance: DataPerformanceData{
|
||
Views: "播放量突破500万,推荐流量占比60%,自然流量表现优秀",
|
||
Completion: "完播率45%,高于同类视频平均值(30%),前3秒吸引力强",
|
||
Engagement: "点赞率2%,评论率0.1%,分享率0.4%,互动数据表现优秀",
|
||
},
|
||
OverallSummary: "整体来看,该竞品视频在内容策划、表现形式和互动数据方面都表现优秀。优势在于:1)内容真实可信,通过实际使用场景展示产品效果;2)剧情设计合理,前3秒抓住用户注意力;3)文案简洁有力,直击用户痛点。建议优化方向:1)可以增加更多用户评价内容,增强可信度;2)适当增加福利引导,提高转化率;3)结尾可以增加引导关注话术,提升粉丝沉淀。",
|
||
}
|
||
|
||
// 模板路径
|
||
templatePath := filepath.Join(root, "data", "竞品报告pdf模板.pdf")
|
||
// 输出路径
|
||
outputPath := filepath.Join(root, "data", "output", "竞品报告测试_multicell.pdf")
|
||
|
||
// 确保输出目录存在
|
||
outputDir := filepath.Dir(outputPath)
|
||
if err := os.MkdirAll(outputDir, 0755); err != nil {
|
||
t.Errorf("创建输出目录失败: %v", err)
|
||
return
|
||
}
|
||
|
||
// 调用函数生成PDF
|
||
err := GenerateCompetitorReportPDF(templatePath, outputPath, data)
|
||
if err != nil {
|
||
t.Errorf("生成竞品报告PDF失败: %v", err)
|
||
return
|
||
}
|
||
|
||
fmt.Printf("PDF生成成功: %s\n", outputPath)
|
||
}
|
||
|
||
// TestGenerateCompetitorReportPDFImageOnly 测试仅图片的竞品报告PDF(无配乐和完播率)
|
||
func TestGenerateCompetitorReportPDFImageOnly(t *testing.T) {
|
||
// 获取项目根目录
|
||
root := getProjectRoot()
|
||
|
||
// 准备测试数据(仅图片,没有视频的配乐和完播率)
|
||
data := CompetitorReportData{
|
||
HighlightAnalysis: HighlightAnalysisData{
|
||
Summary: "该图文内容通过精美的视觉设计和精准的标签定位,成功吸引目标用户关注。",
|
||
Points: PointsData{
|
||
Theme: "标题设置悬念,引发用户点击欲望",
|
||
Narrative: "采用九宫格形式展示产品特点,视觉冲击力强",
|
||
Content: "内容排版清晰,重点突出,便于用户快速获取信息",
|
||
Copywriting: "文案简洁,配合表情符号增加趣味性",
|
||
Data: "收藏量5万+,评论1000+,分享8000+",
|
||
Music: "", // 图片无配乐
|
||
},
|
||
},
|
||
DataPerformance: DataPerformanceData{
|
||
Views: "曝光量100万+,点击率3%,表现良好",
|
||
Completion: "", // 图文无完播率
|
||
Engagement: "收藏率5%,评论率0.1%,分享率0.8%",
|
||
},
|
||
OverallSummary: "该图文内容整体表现优秀,特别是在视觉设计和内容排版方面。亮点:1)九宫格形式统一,视觉效果好;2)标签设置精准,触达目标用户;3)发布时间合理,获得更多曝光。优化建议:1)可以增加更多用户案例展示;2)适当加入互动话题,提高评论量。",
|
||
}
|
||
|
||
// 模板路径
|
||
templatePath := filepath.Join(root, "data", "竞品报告pdf模板.pdf")
|
||
// 输出路径
|
||
outputPath := filepath.Join(root, "data", "output", "竞品报告测试_图文11.pdf")
|
||
|
||
// 确保输出目录存在
|
||
outputDir := filepath.Dir(outputPath)
|
||
if err := os.MkdirAll(outputDir, 0755); err != nil {
|
||
t.Errorf("创建输出目录失败: %v", err)
|
||
return
|
||
}
|
||
|
||
// 调用函数生成PDF
|
||
err := GenerateCompetitorReportPDF(templatePath, outputPath, data)
|
||
if err != nil {
|
||
t.Errorf("生成竞品报告PDF失败: %v", err)
|
||
return
|
||
}
|
||
|
||
fmt.Printf("PDF生成成功: %s\n", outputPath)
|
||
}
|
||
|
||
// TestGenerateCompetitorReportPDFWithImage 测试带图片的竞品报告PDF
|
||
// 注意:此测试需要网络连接来下载图片,如果网络不可用会被跳过
|
||
func TestGenerateCompetitorReportPDFWithImage1(t *testing.T) {
|
||
// 获取项目根目录
|
||
root := getProjectRoot()
|
||
|
||
// 准备测试数据(带图片)
|
||
// 使用一个已知可用的测试图片URL
|
||
data := CompetitorReportData{
|
||
HighlightAnalysis: HighlightAnalysisData{
|
||
Summary: "本视频通过展示产品使用的真实场景,突出用户产品优势和痛点,内容详实且具有吸引力。",
|
||
Points: PointsData{
|
||
Theme: "标题简洁有力,突出核心卖点'省时省力',引发用户好奇心",
|
||
Narrative: "采用情景剧形式展示产品使用场景,剧情贴近生活,易引发共鸣",
|
||
Content: "通过前后对比展示产品效果,直观呈现产品价值",
|
||
Copywriting: "文案简洁明了,突出用户痛点解决方案,语气亲切自然",
|
||
Data: "点赞量10万+,评论5000+,分享2万+,数据表现优异",
|
||
Music: "背景音乐节奏轻快,与视频内容匹配度高,增强观看体验",
|
||
},
|
||
},
|
||
DataPerformance: DataPerformanceData{
|
||
Views: "播放量突破500万,推荐流量占比60%,自然流量表现优秀",
|
||
Completion: "完播率45%,高于同类视频平均值(30%),前3秒吸引力强",
|
||
Engagement: "点赞率2%,评论率0.1%,分享率0.4%,互动数据表现优秀",
|
||
},
|
||
OverallSummary: "整体来看,该竞品视频在内容策划、表现形式和互动数据方面都表现优秀。",
|
||
ImageURL: "https://cdn-test.szjixun.cn/fonchain-main/test/image/12345/artwork/0.png", // 测试用图片URL
|
||
}
|
||
|
||
// 模板路径
|
||
templatePath := filepath.Join(root, "data", "竞品报告pdf模板.pdf")
|
||
// 输出路径
|
||
outputPath := filepath.Join(root, "data", "output", "竞品报告测试_带图片.pdf")
|
||
|
||
// 确保输出目录存在
|
||
outputDir := filepath.Dir(outputPath)
|
||
if err := os.MkdirAll(outputDir, 0755); err != nil {
|
||
t.Errorf("创建输出目录失败: %v", err)
|
||
return
|
||
}
|
||
|
||
// 调用函数生成PDF
|
||
err := GenerateCompetitorReportPDF(templatePath, outputPath, data)
|
||
if err != nil {
|
||
t.Logf("图片下载测试跳过(网络问题): %v", err)
|
||
t.Skip("网络不可用,跳过图片测试")
|
||
return
|
||
}
|
||
|
||
fmt.Printf("PDF生成成功: %s\n", outputPath)
|
||
}
|
||
|
||
// TestGenerateCompetitorReportPDFMixedContent 测试中英混合、数字、符号的复杂情况
|
||
func TestGenerateCompetitorReportPDFMixedContent(t *testing.T) {
|
||
root := getProjectRoot()
|
||
|
||
// 准备包含中英混合、数字、符号的复杂测试数据
|
||
data := CompetitorReportData{
|
||
HighlightAnalysis: HighlightAnalysisData{
|
||
Summary: "这是一个关于2024年短视频创作的爆款分析报告!视频总播放量达到1.2亿次,点赞率8.5%(远超行业平均3.2%)。内容包括:①生活技巧类占比40% ②知识科普类30% ③娱乐搞笑类30%。推荐算法推荐流量占比85%,搜索流量10%,其他渠道5%。",
|
||
Points: PointsData{
|
||
Theme: "标题:【逆袭】3个月从0到100万粉丝!我是如何做到的?必看!🔥",
|
||
Narrative: "采用问题-解决叙事结构:前3秒抛出痛点你还在为XX发愁吗,然后展示解决方案ABC",
|
||
Content: "内容分为3个板块:①前置干货预告15秒 ②核心内容展示30秒 ③互动引导15秒",
|
||
Copywriting: "文案使用了大量emoji表情🔥💯👍,增加年轻化气息!结尾一句评论区见强化互动",
|
||
Data: "点赞率8.5%,高于同类平均3.2%,转发率2.1%,评论数2.3万条,评论区活跃度TOP10%",
|
||
Music: "使用热门BGM孤勇者前奏3秒,配合画面节奏卡点!音量为Original-3dB",
|
||
},
|
||
},
|
||
DataPerformance: DataPerformanceData{
|
||
Views: "发布24小时内播放量突破500万,48小时达到1200万,72小时稳定在1500万。推荐流量占比85%,搜索流量占比10%,其他渠道5%。数据表现:PV=15000000,UV=8500000。",
|
||
Completion: "平均完播率45.2%,高于行业均值28%,3秒留存率72%,5秒完播率58%,10秒完播率35%,属于高质量流量。平均观看时长18.5秒,视频总时长42秒。",
|
||
Engagement: "点赞数128000,点赞率0.85%,评论数23000,其中神评论占比15%,高赞评论TOP3:①学到了 ②太棒了 ③收藏了,分享数56000。评论区@相关账号12个,引发二次传播。",
|
||
},
|
||
OverallSummary: "该视频成功因素:①标题使用数字热门词激发点击逆袭必看 ②内容结构清晰,每15秒一个高潮点 ③BGM选择契合内容情绪孤勇者 ④评论区运营到位。建议优化:1)可增加合集功能,将同类内容串联 2)提升粉丝粘性到10%以上 3)适当增加直播预告。整体来看,这是一条典型的爆款体质视频,值得借鉴学习!数据支撑:ROI=3.5,CPM=25元,CPC=0.8元。",
|
||
ImageURL: "",
|
||
}
|
||
|
||
// 模板路径
|
||
templatePath := filepath.Join(root, "data", "竞品报告pdf模板.pdf")
|
||
// 输出路径
|
||
outputPath := filepath.Join(root, "data", "output", "竞品报告测试_中英混合.pdf")
|
||
|
||
// 确保输出目录存在
|
||
outputDir := filepath.Dir(outputPath)
|
||
if err := os.MkdirAll(outputDir, 0755); err != nil {
|
||
t.Errorf("创建输出目录失败: %v", err)
|
||
return
|
||
}
|
||
|
||
// 调用函数生成PDF
|
||
err := GenerateCompetitorReportPDF(templatePath, outputPath, data)
|
||
if err != nil {
|
||
t.Errorf("生成PDF失败: %v", err)
|
||
return
|
||
}
|
||
|
||
fmt.Printf("PDF生成成功(中英混合测试): %s\n", outputPath)
|
||
}
|
||
|
||
// TestTruncateTextByRune 测试按字符权重截断文本
|
||
func TestTruncateTextByRune(t *testing.T) {
|
||
tests := []struct {
|
||
name string
|
||
text string
|
||
maxWidth float64
|
||
want string
|
||
}{
|
||
{
|
||
name: "空文本",
|
||
text: "",
|
||
maxWidth: 10,
|
||
want: "",
|
||
},
|
||
{
|
||
name: "纯中文截断",
|
||
text: "这是一段很长的中文内容需要被截断",
|
||
maxWidth: 10,
|
||
want: "这是一段很长的中文内", // 10个中文字=10,正好
|
||
},
|
||
{
|
||
name: "纯英文截断(按0.5计算)",
|
||
text: "abcdefghijklmnop",
|
||
maxWidth: 5,
|
||
want: "abcdefghij", // 10个字母=5.0,正好
|
||
},
|
||
{
|
||
name: "数字截断(按0.5计算)",
|
||
text: "1234567890",
|
||
maxWidth: 3,
|
||
want: "123456", // 6个数字=3.0,正好
|
||
},
|
||
{
|
||
name: "中英混合截断",
|
||
text: "hello你好world世界",
|
||
maxWidth: 6,
|
||
// h(0.5)+e(0.5)+l(0.5)+l(0.5)+o(0.5)=2.5
|
||
// +你(1)+好(1)=4.5
|
||
// +w(0.5)+o(0.5)+r(0.5)+l(0.5)=2,再加就超过6了
|
||
// 所以应该是 hello你好wor
|
||
want: "hello你好wor",
|
||
},
|
||
{
|
||
name: "符号截断(按0.5计算)",
|
||
text: "hello,world!",
|
||
maxWidth: 5,
|
||
// h(0.5)+e(0.5)+l(0.5)+l(0.5)+o(0.5)+,(0.5)=3
|
||
// +w(0.5)+o(0.5)+r(0.5)+l(0.5)+d(0.5)+!(0.5)=6,超过5
|
||
// 所以保留 hello,worl
|
||
want: "hello,worl",
|
||
},
|
||
{
|
||
name: "截断到正好边界",
|
||
text: "中文字符",
|
||
maxWidth: 4,
|
||
want: "中文字符", // 4个中文字=4.0,正好
|
||
},
|
||
{
|
||
name: "宽度为0",
|
||
text: "hello",
|
||
maxWidth: 0,
|
||
want: "",
|
||
},
|
||
{
|
||
name: "宽度小于单个字符",
|
||
text: "hello",
|
||
maxWidth: 0.3,
|
||
want: "",
|
||
},
|
||
}
|
||
|
||
for _, tt := range tests {
|
||
t.Run(tt.name, func(t *testing.T) {
|
||
got := TruncateTextByRune(tt.text, tt.maxWidth)
|
||
if got != tt.want {
|
||
t.Errorf("TruncateTextByRune(%q, %v) = %q, want %q", tt.text, tt.maxWidth, got, tt.want)
|
||
}
|
||
})
|
||
}
|
||
}
|