Files
creator-hub/internal/creator/growth_test.go
T
rogee 01d1d44a15
douyin-release-gate / verify (push) Failing after 3m23s
feat(creator): 竞品作品指标采集改用匿名浏览器并新增收藏数与爆品增速
- 指标采集不再依赖已登录自有账号:竞品作品到期后经匿名浏览器拉取
  抖音作品详情接口(detail 返回完整点赞/收藏/转发/评论快照)
- 补采收藏数:creator_work 与 creator_work_metric 新增 collect_count
  (迁移 039),works 列表与 detail 解析均映射该字段
- 新增点赞增速:LoadWorksGrowth 按时间窗计算基线增量(full/partial
  覆盖标注),works API 支持 growth_hours/min_likes_growth 筛选排序
- 前端监控详情页:作品卡片展示 24h 点赞增量、支持按增量筛选爆品;
  作品 Drawer 新增增长曲线折线图(@ant-design/charts)与收藏列
2026-09-27 19:44:49 +08:00

83 lines
2.8 KiB
Go

package creator
import (
"testing"
"time"
)
func TestComputeWorkGrowth(t *testing.T) {
now := time.Date(2026, 9, 27, 12, 0, 0, 0, time.UTC)
likes := func(at time.Time, value int64) MetricPoint {
return MetricPoint{CollectedAt: at, Likes: &value}
}
tooOld := now.Add(-72 * time.Hour)
t.Run("full baseline inside window", func(t *testing.T) {
points := []MetricPoint{
likes(tooOld, 100),
likes(now.Add(-25*time.Hour), 200), // 窗口起点前最近点 → 基线
likes(now.Add(-2*time.Hour), 500),
}
growth := computeWorkGrowth(points, now, 24)
if growth.Likes == nil || *growth.Likes != 300 || growth.Coverage != "full" {
t.Fatalf("unexpected growth: %+v", growth)
}
})
t.Run("partial when only one point", func(t *testing.T) {
points := []MetricPoint{likes(now.Add(-2*time.Hour), 500)}
growth := computeWorkGrowth(points, now, 24)
if growth.Likes == nil || *growth.Likes != 0 || growth.Coverage != "partial" {
t.Fatalf("unexpected growth: %+v", growth)
}
})
t.Run("partial falls back to first point", func(t *testing.T) {
points := []MetricPoint{likes(now.Add(-20*time.Hour), 100), likes(now.Add(-2*time.Hour), 500)}
growth := computeWorkGrowth(points, now, 24)
// 两个点都在窗口内、窗口前无基线 → 以首点近似,partial。
if growth.Likes == nil || *growth.Likes != 400 || growth.Coverage != "partial" {
t.Fatalf("unexpected growth: %+v likes=%v", growth, *growth.Likes)
}
})
t.Run("old pre-window baseline counts as full", func(t *testing.T) {
points := []MetricPoint{likes(tooOld, 100), likes(now.Add(-2*time.Hour), 500)}
growth := computeWorkGrowth(points, now, 24)
// 窗口前存在真实基线(72h 前)→ full;增量跨 72h 但可比。
if growth.Likes == nil || *growth.Likes != 400 || growth.Coverage != "full" {
t.Fatalf("unexpected growth: %+v", growth)
}
})
t.Run("no points", func(t *testing.T) {
if growth := (WorkGrowth{}); computeWorkGrowth(nil, now, 24) != growth {
t.Fatal("empty points must yield zero growth")
}
})
}
func TestApplyWorkGrowthFilterSortsAndFilters(t *testing.T) {
mk := func(id string, growth int64) (Work, WorkGrowth) {
value := growth
return Work{ID: id}, WorkGrowth{Likes: &value, Coverage: "full"}
}
w1, g1 := mk("a", 100)
w2, g2 := mk("b", 900)
w3, _ := mk("c", 0) // 无采样点:不附加增速
works := []Work{w1, w2, w3}
growth := map[string]WorkGrowth{"a": g1, "b": g2}
min := int64(50)
filtered, err := ApplyWorkGrowthFilter(works, growth, WorkGrowthFilter{Hours: 24, MinLikes: &min})
if err != nil {
t.Fatal(err)
}
if len(filtered) != 2 || filtered[0].ID != "b" || filtered[1].ID != "a" {
t.Fatalf("unexpected filter/sort result: %+v", filtered)
}
if filtered[0].LikesGrowth == nil || *filtered[0].LikesGrowth != 900 {
t.Fatalf("growth not attached: %+v", filtered[0])
}
}