package service_test import ( "context" "errors" "testing" "time" "watch-party-backend/internal/repo" "watch-party-backend/internal/service" ) // ---- test fakes ---- type fakeRepo struct { cur repo.Episode getErr error setErr error moveRes repo.MoveResult moveErr error setCalls []struct { id int64 start string } moveCalls [][]int64 listRes []repo.Episode listErr error } func (f *fakeRepo) GetCurrent(ctx context.Context) (repo.Episode, error) { return f.cur, f.getErr } func (f *fakeRepo) SetCurrent(ctx context.Context, id int64, start string) error { f.setCalls = append(f.setCalls, struct { id int64 start string }{id, start}) return f.setErr } func (f *fakeRepo) MoveToArchive(ctx context.Context, ids []int64) (repo.MoveResult, error) { f.moveCalls = append(f.moveCalls, ids) return f.moveRes, f.moveErr } func (f *fakeRepo) ListAll(ctx context.Context) ([]repo.Episode, error) { return f.listRes, f.listErr } // ---- tests ---- func TestEpisodeService_GetCurrent_OK(t *testing.T) { now := time.Now().UTC() fr := &fakeRepo{ cur: repo.Episode{ EpNum: 1, EpTitle: "Pilot", SeasonName: "S1", StartTime: "20:00:00", PlaybackLength: "00:24:00", DateCreated: now, }, } svc := service.NewEpisodeService(fr) got, err := svc.GetCurrent(context.Background()) if err != nil { t.Fatalf("unexpected err: %v", err) } if got.EpNum != 1 || got.EpTitle != "Pilot" { t.Fatalf("got %+v", got) } } func TestEpisodeService_GetCurrent_PropagatesError(t *testing.T) { fr := &fakeRepo{getErr: errors.New("db down")} svc := service.NewEpisodeService(fr) if _, err := svc.GetCurrent(context.Background()); err == nil { t.Fatal("expected error") } } func TestEpisodeService_SetCurrent_RejectsBadTime(t *testing.T) { fr := &fakeRepo{} svc := service.NewEpisodeService(fr) _, err := svc.SetCurrent(context.Background(), 42, "7:0:0") // not zero-padded if err == nil || !errors.Is(err, service.ErrInvalidTime) { t.Fatalf("expected ErrInvalidTime, got %v", err) } if len(fr.setCalls) != 0 { t.Fatalf("repo should not be called on invalid time") } } func TestEpisodeService_SetCurrent_OK(t *testing.T) { now := time.Now().UTC() fr := &fakeRepo{ cur: repo.Episode{ EpNum: 42, EpTitle: "Answer", SeasonName: "S1", StartTime: "21:07:05", PlaybackLength: "00:24:00", DateCreated: now, }, } svc := service.NewEpisodeService(fr) got, err := svc.SetCurrent(context.Background(), 42, "21:07:05") if err != nil { t.Fatalf("unexpected err: %v", err) } if got.EpNum != 42 || got.StartTime != "21:07:05" { t.Fatalf("unexpected %+v", got) } if len(fr.setCalls) != 1 || fr.setCalls[0].id != 42 || fr.setCalls[0].start != "21:07:05" { t.Fatalf("repo.SetCurrent not called as expected: %+v", fr.setCalls) } } func TestEpisodeService_MoveToArchive_EmptyIDs(t *testing.T) { fr := &fakeRepo{} svc := service.NewEpisodeService(fr) _, err := svc.MoveToArchive(context.Background(), nil) if err == nil || !errors.Is(err, service.ErrEmptyIDs) { t.Fatalf("expected ErrEmptyIDs, got %v", err) } } func TestEpisodeService_MoveToArchive_DedupAndOK(t *testing.T) { fr := &fakeRepo{ moveRes: repo.MoveResult{ MovedIDs: []int64{1, 2}, DeletedIDs: []int64{1, 2}, SkippedIDs: []int64{999}, }, } svc := service.NewEpisodeService(fr) res, err := svc.MoveToArchive(context.Background(), []int64{1, 2, 1}) // dedupe if err != nil { t.Fatalf("unexpected: %v", err) } if len(fr.moveCalls) != 1 || len(fr.moveCalls[0]) != 2 { t.Fatalf("expected deduped call, got %+v", fr.moveCalls) } if len(res.MovedIDs) != 2 || len(res.DeletedIDs) != 2 { t.Fatalf("unexpected response: %+v", res) } } func TestEpisodeService_ListAll_OK(t *testing.T) { fr := &fakeRepo{listRes: []repo.Episode{{Id: 3}, {Id: 1}}} svc := service.NewEpisodeService(fr) got, err := svc.ListAll(context.Background()) if err != nil { t.Fatalf("unexpected: %v", err) } if len(got) != 2 || got[0].Id != 3 { t.Fatalf("bad: %+v", got) } } func TestEpisodeService_ListAll_Error(t *testing.T) { fr := &fakeRepo{listErr: errors.New("boom")} svc := service.NewEpisodeService(fr) if _, err := svc.ListAll(context.Background()); err == nil { t.Fatal("expected error") } }