Files
truewhile 9ccf60427a 优化
2026-10-22 15:14:00 +08:00

166 lines
4.9 KiB
Go

package service
import (
"testing"
"go.uber.org/zap"
"github.com/truewhile/MeBox/internal/model"
"github.com/truewhile/MeBox/internal/repository"
)
func newManualMarkFixture(t *testing.T) (*MediaSegmentService, *repository.Container, *model.Media) {
t.Helper()
repos := repository.New(newServiceTestDB(t))
svc := NewMediaSegmentService(zap.NewNop(), repos)
m := &model.Media{
Base: model.Base{ID: "ep-1"},
Title: "Show",
Path: "/tv/show/s01e01.mkv",
LibraryID: "lib-1",
SeasonNum: 1,
EpisodeNum: 1,
}
if err := repos.DB.Create(m).Error; err != nil {
t.Fatal(err)
}
return svc, repos, m
}
func TestSetManualMarkWritesIntroBoundary(t *testing.T) {
svc, repos, m := newManualMarkFixture(t)
if err := svc.SetManualMark(t.Context(), m, ManualMarkRequest{
Kind: model.SegmentKindIntro, PositionMs: 90_000,
}); err != nil {
t.Fatalf("set mark: %v", err)
}
rows, err := repos.MediaSegment.ListByMedia(t.Context(), m.ID)
if err != nil {
t.Fatal(err)
}
if len(rows) != 1 {
t.Fatalf("rows = %#v, want 1", rows)
}
if rows[0].Kind != model.SegmentKindIntro || rows[0].Source != SegmentSourceManual {
t.Fatalf("row = %#v, want manual intro", rows[0])
}
if rows[0].StartMs != 0 || rows[0].EndMs != 90_000 {
t.Fatalf("row = %#v, want 0-90000ms", rows[0])
}
}
func TestSetManualMarkClearRemovesOnlyThatKind(t *testing.T) {
svc, repos, m := newManualMarkFixture(t)
ctx := t.Context()
if err := svc.SetManualMark(ctx, m, ManualMarkRequest{Kind: model.SegmentKindIntro, PositionMs: 90_000}); err != nil {
t.Fatal(err)
}
if err := svc.SetManualMark(ctx, m, ManualMarkRequest{Kind: model.SegmentKindCredits, PositionMs: 1_200_000}); err != nil {
t.Fatal(err)
}
if err := svc.SetManualMark(ctx, m, ManualMarkRequest{Kind: model.SegmentKindIntro, Clear: true}); err != nil {
t.Fatal(err)
}
rows, err := repos.MediaSegment.ListByMedia(ctx, m.ID)
if err != nil {
t.Fatal(err)
}
if len(rows) != 1 || rows[0].Kind != model.SegmentKindCredits {
t.Fatalf("rows = %#v, want only the credits mark to survive", rows)
}
}
func TestSetManualMarkRejectsInvalidRequests(t *testing.T) {
svc, _, m := newManualMarkFixture(t)
cases := []ManualMarkRequest{
{Kind: "recap", PositionMs: 1000}, // 只支持 intro/credits
{Kind: "preview", PositionMs: 1000}, // 同上
{Kind: model.SegmentKindIntro, PositionMs: 0},
{Kind: model.SegmentKindIntro, PositionMs: -5},
}
for _, req := range cases {
if err := svc.SetManualMark(t.Context(), m, req); err == nil {
t.Fatalf("req %#v: want an error", req)
}
}
}
// 同一季的其它集应自动继承手动标记,但用户已显式标过的集不能被覆盖。
func TestSetManualMarkPropagatesToSeasonSiblings(t *testing.T) {
svc, repos, m := newManualMarkFixture(t)
ctx := t.Context()
sibling := &model.Media{
Base: model.Base{ID: "ep-2"},
Title: "Show",
Path: "/tv/show/s01e02.mkv",
LibraryID: "lib-1",
SeasonNum: 1,
EpisodeNum: 2,
}
other := &model.Media{
Base: model.Base{ID: "ep-3"},
Title: "Show",
Path: "/tv/show/s01e03.mkv",
LibraryID: "lib-1",
SeasonNum: 1,
EpisodeNum: 3,
}
if err := repos.DB.Create(sibling).Error; err != nil {
t.Fatal(err)
}
if err := repos.DB.Create(other).Error; err != nil {
t.Fatal(err)
}
// other 已经有自己的手动片头标记,不该被覆盖。
if err := svc.SetManualMark(ctx, other, ManualMarkRequest{
Kind: model.SegmentKindIntro, PositionMs: 45_000,
}); err != nil {
t.Fatal(err)
}
if err := svc.SetManualMark(ctx, m, ManualMarkRequest{
Kind: model.SegmentKindIntro, PositionMs: 90_000,
}); err != nil {
t.Fatal(err)
}
sibRows, err := repos.MediaSegment.ListByMedia(ctx, sibling.ID)
if err != nil {
t.Fatal(err)
}
if len(sibRows) != 1 || sibRows[0].EndMs != 90_000 {
t.Fatalf("sibling rows = %#v, want the propagated 90000ms mark", sibRows)
}
otherRows, err := repos.MediaSegment.ListByMedia(ctx, other.ID)
if err != nil {
t.Fatal(err)
}
if len(otherRows) != 1 || otherRows[0].EndMs != 45_000 {
t.Fatalf("other rows = %#v, want its own 45000ms mark preserved", otherRows)
}
}
// 手动标记优先级最高:即使外部源给了不同位置,播放时也只输出手动那条。
func TestManualMarkOutranksExternalSource(t *testing.T) {
svc, repos, m := newManualMarkFixture(t)
ctx := t.Context()
if err := repos.MediaSegment.ReplaceForMedia(ctx, m.ID, IntroDBSource, []model.MediaSegment{{
MediaID: m.ID, Kind: model.SegmentKindIntro, StartMs: 0, EndMs: 30_000, Source: IntroDBSource,
}}); err != nil {
t.Fatal(err)
}
if err := svc.SetManualMark(ctx, m, ManualMarkRequest{
Kind: model.SegmentKindIntro, PositionMs: 90_000,
}); err != nil {
t.Fatal(err)
}
rows, err := svc.mergeForPlayback(ctx, m.ID)
if err != nil {
t.Fatal(err)
}
if len(rows) != 1 || rows[0].Source != SegmentSourceManual || rows[0].EndMs != 90_000 {
t.Fatalf("rows = %#v, want the manual mark to win", rows)
}
}