mirror of
https://github.com/truewhile/MeBox.git
synced 2026-10-10 15:26:38 +08:00
添加跳过片头片尾功能
This commit is contained in:
@@ -0,0 +1,154 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/glebarez/sqlite"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/truewhile/MeBox/internal/database"
|
||||
"github.com/truewhile/MeBox/internal/model"
|
||||
)
|
||||
|
||||
func newSegmentTestRepos(t *testing.T) *Container {
|
||||
t.Helper()
|
||||
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := database.AutoMigrate(db); err != nil {
|
||||
t.Fatalf("migrate: %v", err)
|
||||
}
|
||||
return New(db)
|
||||
}
|
||||
|
||||
func TestReplaceForMediaIsIdempotentAcrossRefreshes(t *testing.T) {
|
||||
repos := newSegmentTestRepos(t)
|
||||
ctx := t.Context()
|
||||
|
||||
rows := []model.MediaSegment{
|
||||
{MediaID: "m-1", Kind: model.SegmentKindIntro, StartMs: 228_664, EndMs: 246_143, Source: "theintrodb"},
|
||||
{MediaID: "m-1", Kind: model.SegmentKindCredits, StartMs: 3_431_000, EndMs: 0, Source: "theintrodb"},
|
||||
}
|
||||
// 重复刷新不能因为 (media_id, kind, start_ms, source) 唯一索引而失败,也不能累积重复行。
|
||||
for i := 0; i < 2; i++ {
|
||||
if err := repos.MediaSegment.ReplaceForMedia(ctx, "m-1", "theintrodb", rows); err != nil {
|
||||
t.Fatalf("replace #%d: %v", i+1, err)
|
||||
}
|
||||
}
|
||||
got, err := repos.MediaSegment.ListByMedia(ctx, "m-1")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(got) != 2 {
|
||||
t.Fatalf("rows = %d, want 2 after two refreshes of the same source", len(got))
|
||||
}
|
||||
|
||||
// 替换只影响同一来源:另一个来源的数据必须保留。
|
||||
other := []model.MediaSegment{
|
||||
{MediaID: "m-1", Kind: model.SegmentKindIntro, StartMs: 10, EndMs: 20, Source: "manual"},
|
||||
}
|
||||
if err := repos.MediaSegment.ReplaceForMedia(ctx, "m-1", "manual", other); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
got, err = repos.MediaSegment.ListByMedia(ctx, "m-1")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(got) != 3 {
|
||||
t.Fatalf("rows = %d, want 3 (2 theintrodb + 1 manual)", len(got))
|
||||
}
|
||||
if got[0].Source != "manual" || got[0].StartMs != 10 {
|
||||
t.Fatalf("rows should be ordered by start_ms, got first = %#v", got[0])
|
||||
}
|
||||
}
|
||||
|
||||
func TestReplaceForMediaAllowsSameRangeFromDifferentSources(t *testing.T) {
|
||||
repos := newSegmentTestRepos(t)
|
||||
ctx := t.Context()
|
||||
span := model.MediaSegment{MediaID: "m-1", Kind: model.SegmentKindIntro, StartMs: 1_000, EndMs: 2_000}
|
||||
|
||||
provider := span
|
||||
provider.Source = "theintrodb"
|
||||
if err := repos.MediaSegment.ReplaceForMedia(ctx, "m-1", "theintrodb", []model.MediaSegment{provider}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// 人工修正给出完全相同的区间:唯一索引含 source,两个来源必须能共存。
|
||||
manual := span
|
||||
manual.Source = "manual"
|
||||
if err := repos.MediaSegment.ReplaceForMedia(ctx, "m-1", "manual", []model.MediaSegment{manual}); err != nil {
|
||||
t.Fatalf("same range from another source: %v", err)
|
||||
}
|
||||
got, err := repos.MediaSegment.ListByMedia(ctx, "m-1")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(got) != 2 {
|
||||
t.Fatalf("rows = %d, want 2 (one per source)", len(got))
|
||||
}
|
||||
}
|
||||
|
||||
func TestReplaceForMediaClearsRowsWhenLookupReturnsNothing(t *testing.T) {
|
||||
repos := newSegmentTestRepos(t)
|
||||
ctx := t.Context()
|
||||
|
||||
rows := []model.MediaSegment{
|
||||
{MediaID: "m-1", Kind: model.SegmentKindIntro, StartMs: 1_000, EndMs: 2_000, Source: "theintrodb"},
|
||||
}
|
||||
if err := repos.MediaSegment.ReplaceForMedia(ctx, "m-1", "theintrodb", rows); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// 提供方后来把这段数据删掉了,本地必须跟着清空,否则会一直跳一个不存在的片头。
|
||||
if err := repos.MediaSegment.ReplaceForMedia(ctx, "m-1", "theintrodb", nil); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
got, err := repos.MediaSegment.ListByMedia(ctx, "m-1")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(got) != 0 {
|
||||
t.Fatalf("rows = %d, want 0 after an empty refresh", len(got))
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpsertFetchKeepsOneRowPerMediaAndSource(t *testing.T) {
|
||||
repos := newSegmentTestRepos(t)
|
||||
ctx := t.Context()
|
||||
now := time.Now()
|
||||
|
||||
if err := repos.MediaSegment.UpsertFetch(ctx, &model.MediaSegmentFetch{
|
||||
MediaID: "m-1", Source: "theintrodb", FetchedAt: now, Found: false,
|
||||
}); err != nil {
|
||||
t.Fatalf("first upsert: %v", err)
|
||||
}
|
||||
got, err := repos.MediaSegment.GetFetch(ctx, "m-1", "theintrodb")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got == nil || got.Found {
|
||||
t.Fatalf("first lookup should be recorded as a miss, got %#v", got)
|
||||
}
|
||||
|
||||
later := now.Add(time.Hour)
|
||||
if err := repos.MediaSegment.UpsertFetch(ctx, &model.MediaSegmentFetch{
|
||||
MediaID: "m-1", Source: "theintrodb", FetchedAt: later, Found: true,
|
||||
}); err != nil {
|
||||
t.Fatalf("second upsert: %v", err)
|
||||
}
|
||||
var count int64
|
||||
if err := repos.DB.Model(&model.MediaSegmentFetch{}).
|
||||
Where("media_id = ? AND source = ?", "m-1", "theintrodb").Count(&count).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if count != 1 {
|
||||
t.Fatalf("fetch ledger rows = %d, want 1", count)
|
||||
}
|
||||
got, err = repos.MediaSegment.GetFetch(ctx, "m-1", "theintrodb")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got == nil || !got.Found {
|
||||
t.Fatalf("ledger should be updated in place, got %#v", got)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user