douyin-release-gate / verify (push) Failing after 3m23s
- 指标采集不再依赖已登录自有账号:竞品作品到期后经匿名浏览器拉取 抖音作品详情接口(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)与收藏列
83 lines
2.8 KiB
Go
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])
|
|
}
|
|
}
|