summaryrefslogtreecommitdiff
path: root/internal
diff options
context:
space:
mode:
Diffstat (limited to 'internal')
-rw-r--r--internal/api/handlers_test.go12
-rw-r--r--internal/repository/mock.go78
-rw-r--r--internal/repository/playback_accumulator.go9
-rw-r--r--internal/repository/playback_progress.go55
-rw-r--r--internal/repository/repository.go8
-rw-r--r--internal/repository/sqlite_test.go105
6 files changed, 255 insertions, 12 deletions
diff --git a/internal/api/handlers_test.go b/internal/api/handlers_test.go
index cf109c3..4ae98e6 100644
--- a/internal/api/handlers_test.go
+++ b/internal/api/handlers_test.go
@@ -1455,15 +1455,27 @@ func (m *mockPingStore) UpsertProgress(ctx context.Context, progress *model.Play
func (m *mockPingStore) GetProgress(ctx context.Context, userID, mediaID int64) (*model.PlaybackProgress, error) {
return m.store.GetProgress(ctx, userID, mediaID)
}
+func (m *mockPingStore) DeleteProgress(ctx context.Context, userID, mediaID int64) error {
+ return m.store.DeleteProgress(ctx, userID, mediaID)
+}
+func (m *mockPingStore) MarkFinished(ctx context.Context, userID, mediaID int64) error {
+ return m.store.MarkFinished(ctx, userID, mediaID)
+}
func (m *mockPingStore) ListProgressByUser(ctx context.Context, userID int64) ([]model.PlaybackProgress, error) {
return m.store.ListProgressByUser(ctx, userID)
}
+func (m *mockPingStore) ListInProgressMedia(ctx context.Context, userID int64, filter repository.MediaFilter) ([]model.Media, error) {
+ return m.store.ListInProgressMedia(ctx, userID, filter)
+}
func (m *mockPingStore) UpsertAccumulator(ctx context.Context, acc *model.PlaybackAccumulator) error {
return m.store.UpsertAccumulator(ctx, acc)
}
func (m *mockPingStore) GetAccumulator(ctx context.Context, sessionID string, mediaID int64) (*model.PlaybackAccumulator, error) {
return m.store.GetAccumulator(ctx, sessionID, mediaID)
}
+func (m *mockPingStore) DeleteAccumulatorByMedia(ctx context.Context, mediaID int64) error {
+ return m.store.DeleteAccumulatorByMedia(ctx, mediaID)
+}
func (m *mockPingStore) CreateSession(ctx context.Context, session *model.Session) error {
return m.store.CreateSession(ctx, session)
}
diff --git a/internal/repository/mock.go b/internal/repository/mock.go
index 19e5bf2..994fca5 100644
--- a/internal/repository/mock.go
+++ b/internal/repository/mock.go
@@ -246,11 +246,26 @@ func (m *MockStore) GetProgress(ctx context.Context, userID, mediaID int64) (*mo
return m.PlaybackProgressRepo.GetProgress(ctx, userID, mediaID)
}
+// DeleteProgress implements PlaybackProgressRepo.
+func (m *MockStore) DeleteProgress(ctx context.Context, userID, mediaID int64) error {
+ return m.PlaybackProgressRepo.DeleteProgress(ctx, userID, mediaID)
+}
+
+// MarkFinished implements PlaybackProgressRepo.
+func (m *MockStore) MarkFinished(ctx context.Context, userID, mediaID int64) error {
+ return m.PlaybackProgressRepo.MarkFinished(ctx, userID, mediaID)
+}
+
// ListProgressByUser implements PlaybackProgressRepo.
func (m *MockStore) ListProgressByUser(ctx context.Context, userID int64) ([]model.PlaybackProgress, error) {
return m.PlaybackProgressRepo.ListProgressByUser(ctx, userID)
}
+// ListInProgressMedia implements PlaybackProgressRepo.
+func (m *MockStore) ListInProgressMedia(ctx context.Context, userID int64, filter MediaFilter) ([]model.Media, error) {
+ return m.PlaybackProgressRepo.ListInProgressMedia(ctx, userID, filter)
+}
+
// UpsertAccumulator implements PlaybackAccumulatorRepo.
func (m *MockStore) UpsertAccumulator(ctx context.Context, acc *model.PlaybackAccumulator) error {
return m.PlaybackAccumulatorRepo.UpsertAccumulator(ctx, acc)
@@ -261,6 +276,11 @@ func (m *MockStore) GetAccumulator(ctx context.Context, sessionID string, mediaI
return m.PlaybackAccumulatorRepo.GetAccumulator(ctx, sessionID, mediaID)
}
+// DeleteAccumulatorByMedia implements PlaybackAccumulatorRepo.
+func (m *MockStore) DeleteAccumulatorByMedia(ctx context.Context, mediaID int64) error {
+ return m.PlaybackAccumulatorRepo.DeleteAccumulatorByMedia(ctx, mediaID)
+}
+
// CreateSession implements SessionRepo.
func (m *MockStore) CreateSession(ctx context.Context, session *model.Session) error {
return m.SessionRepo.CreateSession(ctx, session)
@@ -690,9 +710,12 @@ func (m *MockFavoriteRepo) ListFavoritesByUser(ctx context.Context, userID int64
// MockPlaybackProgressRepo is a fake PlaybackProgressRepo.
type MockPlaybackProgressRepo struct {
- UpsertProgressFunc func(ctx context.Context, progress *model.PlaybackProgress) error
- GetProgressFunc func(ctx context.Context, userID, mediaID int64) (*model.PlaybackProgress, error)
- ListProgressByUserFunc func(ctx context.Context, userID int64) ([]model.PlaybackProgress, error)
+ UpsertProgressFunc func(ctx context.Context, progress *model.PlaybackProgress) error
+ GetProgressFunc func(ctx context.Context, userID, mediaID int64) (*model.PlaybackProgress, error)
+ DeleteProgressFunc func(ctx context.Context, userID, mediaID int64) error
+ MarkFinishedFunc func(ctx context.Context, userID, mediaID int64) error
+ ListProgressByUserFunc func(ctx context.Context, userID int64) ([]model.PlaybackProgress, error)
+ ListInProgressMediaFunc func(ctx context.Context, userID int64, filter MediaFilter) ([]model.Media, error)
}
// UpsertProgress calls UpsertProgressFunc or returns nil.
@@ -711,6 +734,22 @@ func (m *MockPlaybackProgressRepo) GetProgress(ctx context.Context, userID, medi
return nil, nil
}
+// DeleteProgress calls DeleteProgressFunc or returns nil.
+func (m *MockPlaybackProgressRepo) DeleteProgress(ctx context.Context, userID, mediaID int64) error {
+ if m.DeleteProgressFunc != nil {
+ return m.DeleteProgressFunc(ctx, userID, mediaID)
+ }
+ return nil
+}
+
+// MarkFinished calls MarkFinishedFunc or returns nil.
+func (m *MockPlaybackProgressRepo) MarkFinished(ctx context.Context, userID, mediaID int64) error {
+ if m.MarkFinishedFunc != nil {
+ return m.MarkFinishedFunc(ctx, userID, mediaID)
+ }
+ return nil
+}
+
// ListProgressByUser calls ListProgressByUserFunc or returns nil.
func (m *MockPlaybackProgressRepo) ListProgressByUser(ctx context.Context, userID int64) ([]model.PlaybackProgress, error) {
if m.ListProgressByUserFunc != nil {
@@ -719,10 +758,19 @@ func (m *MockPlaybackProgressRepo) ListProgressByUser(ctx context.Context, userI
return nil, nil
}
+// ListInProgressMedia calls ListInProgressMediaFunc or returns nil.
+func (m *MockPlaybackProgressRepo) ListInProgressMedia(ctx context.Context, userID int64, filter MediaFilter) ([]model.Media, error) {
+ if m.ListInProgressMediaFunc != nil {
+ return m.ListInProgressMediaFunc(ctx, userID, filter)
+ }
+ return nil, nil
+}
+
// MockPlaybackAccumulatorRepo is a fake PlaybackAccumulatorRepo.
type MockPlaybackAccumulatorRepo struct {
- UpsertAccumulatorFunc func(ctx context.Context, acc *model.PlaybackAccumulator) error
- GetAccumulatorFunc func(ctx context.Context, sessionID string, mediaID int64) (*model.PlaybackAccumulator, error)
+ UpsertAccumulatorFunc func(ctx context.Context, acc *model.PlaybackAccumulator) error
+ GetAccumulatorFunc func(ctx context.Context, sessionID string, mediaID int64) (*model.PlaybackAccumulator, error)
+ DeleteAccumulatorByMediaFunc func(ctx context.Context, mediaID int64) error
}
// UpsertAccumulator calls UpsertAccumulatorFunc or returns nil.
@@ -741,6 +789,14 @@ func (m *MockPlaybackAccumulatorRepo) GetAccumulator(ctx context.Context, sessio
return nil, nil
}
+// DeleteAccumulatorByMedia calls DeleteAccumulatorByMediaFunc or returns nil.
+func (m *MockPlaybackAccumulatorRepo) DeleteAccumulatorByMedia(ctx context.Context, mediaID int64) error {
+ if m.DeleteAccumulatorByMediaFunc != nil {
+ return m.DeleteAccumulatorByMediaFunc(ctx, mediaID)
+ }
+ return nil
+}
+
// MockSessionRepo is a fake SessionRepo.
type MockSessionRepo struct {
CreateSessionFunc func(ctx context.Context, session *model.Session) error
@@ -980,13 +1036,13 @@ type MockPodcastRepo struct {
ListFeedsFunc func(ctx context.Context) ([]model.PodcastFeed, error)
ListFeedsNeedingCheckFunc func(ctx context.Context, now, before time.Time) ([]model.PodcastFeed, error)
- CreateEpisodeFunc func(ctx context.Context, episode *model.PodcastEpisode) (int64, error)
- GetEpisodeByIDFunc func(ctx context.Context, id int64) (*model.PodcastEpisode, error)
- GetEpisodeByGUIDFunc func(ctx context.Context, feedID int64, guid string) (*model.PodcastEpisode, error)
- ListEpisodesByFeedFunc func(ctx context.Context, feedID int64, limit, offset int) ([]model.PodcastEpisode, error)
+ CreateEpisodeFunc func(ctx context.Context, episode *model.PodcastEpisode) (int64, error)
+ GetEpisodeByIDFunc func(ctx context.Context, id int64) (*model.PodcastEpisode, error)
+ GetEpisodeByGUIDFunc func(ctx context.Context, feedID int64, guid string) (*model.PodcastEpisode, error)
+ ListEpisodesByFeedFunc func(ctx context.Context, feedID int64, limit, offset int) ([]model.PodcastEpisode, error)
ListEpisodesByFeedIDsWithStatusFunc func(ctx context.Context, userID int64, feedIDs []int64, limit, offset int) ([]model.PodcastEpisodeWithStatus, error)
- UpdateEpisodeMediaFunc func(ctx context.Context, episodeID, mediaID int64, fileName string) error
- DeleteEpisodesByFeedFunc func(ctx context.Context, feedID int64) error
+ UpdateEpisodeMediaFunc func(ctx context.Context, episodeID, mediaID int64, fileName string) error
+ DeleteEpisodesByFeedFunc func(ctx context.Context, feedID int64) error
UpsertEpisodeProgressFunc func(ctx context.Context, status *model.PodcastStatus) error
GetEpisodeProgressFunc func(ctx context.Context, userID, episodeID int64) (*model.PodcastStatus, error)
diff --git a/internal/repository/playback_accumulator.go b/internal/repository/playback_accumulator.go
index bb1fde4..ea2bcf0 100644
--- a/internal/repository/playback_accumulator.go
+++ b/internal/repository/playback_accumulator.go
@@ -36,3 +36,12 @@ func (s *SQLite) GetAccumulator(ctx context.Context, sessionID string, mediaID i
a.Counted = counted != 0
return &a, nil
}
+
+// DeleteAccumulatorByMedia removes all playback accumulators for a media item.
+func (s *SQLite) DeleteAccumulatorByMedia(ctx context.Context, mediaID int64) error {
+ _, err := s.db.ExecContext(ctx, `DELETE FROM playback_accumulator WHERE media_id = ?`, mediaID)
+ if err != nil {
+ return fmt.Errorf("delete accumulator by media: %w", err)
+ }
+ return nil
+}
diff --git a/internal/repository/playback_progress.go b/internal/repository/playback_progress.go
index 046598a..a2f753e 100644
--- a/internal/repository/playback_progress.go
+++ b/internal/repository/playback_progress.go
@@ -4,6 +4,7 @@ import (
"context"
"database/sql"
"fmt"
+ "strings"
"codeberg.org/snonux/player/internal/model"
)
@@ -35,6 +36,27 @@ func (s *SQLite) GetProgress(ctx context.Context, userID, mediaID int64) (*model
return &p, nil
}
+// DeleteProgress removes playback progress for a user and media.
+func (s *SQLite) DeleteProgress(ctx context.Context, userID, mediaID int64) error {
+ _, err := s.db.ExecContext(ctx, `DELETE FROM playback_progress WHERE user_id = ? AND media_id = ?`, userID, mediaID)
+ if err != nil {
+ return fmt.Errorf("delete progress: %w", err)
+ }
+ return nil
+}
+
+// MarkFinished marks playback progress finished for a user and media.
+func (s *SQLite) MarkFinished(ctx context.Context, userID, mediaID int64) error {
+ _, err := s.db.ExecContext(ctx,
+ `UPDATE playback_progress SET finished = 1, updated_at = CURRENT_TIMESTAMP WHERE user_id = ? AND media_id = ?`,
+ userID, mediaID,
+ )
+ if err != nil {
+ return fmt.Errorf("mark finished: %w", err)
+ }
+ return nil
+}
+
// ListProgressByUser returns all progress records for a user.
func (s *SQLite) ListProgressByUser(ctx context.Context, userID int64) ([]model.PlaybackProgress, error) {
rows, err := s.db.QueryContext(ctx,
@@ -53,3 +75,36 @@ func (s *SQLite) ListProgressByUser(ctx context.Context, userID int64) ([]model.
}
return pp, rows.Err()
}
+
+// ListInProgressMedia returns unfinished, non-deleted media with saved progress for a user.
+func (s *SQLite) ListInProgressMedia(ctx context.Context, userID int64, filter MediaFilter) ([]model.Media, error) {
+ args := []any{userID}
+ conds := []string{`pp.user_id = ?`, `pp.finished = 0`, `media.deleted_at IS NULL`}
+ query := `SELECT media.id, media.set_id, media.rel_path, media.file_name, media.abs_path, media.type, media.duration, media.codec, media.resolution, media.bitrate, media.file_size_bytes, media.width, media.height, media.exif_camera, media.exif_lens, media.exif_date, media.exif_iso, media.exif_f_number, media.exif_exposure, media.exif_focal_length, media.thumbnail_path, media.play_count, media.deleted_at, media.created_at FROM playback_progress pp INNER JOIN media ON media.id = pp.media_id`
+
+ if len(filter.AllowedSetIDs) > 0 {
+ conds = append(conds, "media.set_id IN ("+placeholders(len(filter.AllowedSetIDs))+")")
+ for _, id := range filter.AllowedSetIDs {
+ args = append(args, id)
+ }
+ }
+
+ query += " WHERE " + strings.Join(conds, " AND ")
+ query += " ORDER BY pp.updated_at DESC"
+
+ rows, err := s.db.QueryContext(ctx, query, args...)
+ if err != nil {
+ return nil, fmt.Errorf("list in-progress media: %w", err)
+ }
+ defer rows.Close()
+
+ var media []model.Media
+ for rows.Next() {
+ m, err := scanMedia(rows)
+ if err != nil {
+ return nil, err
+ }
+ media = append(media, *m)
+ }
+ return media, rows.Err()
+}
diff --git a/internal/repository/repository.go b/internal/repository/repository.go
index 4e863b3..e7ff672 100644
--- a/internal/repository/repository.go
+++ b/internal/repository/repository.go
@@ -260,8 +260,14 @@ type PlaybackProgressRepo interface {
UpsertProgress(ctx context.Context, progress *model.PlaybackProgress) error
// GetProgress returns a user's playback position for a media item.
GetProgress(ctx context.Context, userID, mediaID int64) (*model.PlaybackProgress, error)
+ // DeleteProgress removes a user's playback position for a media item.
+ DeleteProgress(ctx context.Context, userID, mediaID int64) error
+ // MarkFinished marks a user's playback position as finished.
+ MarkFinished(ctx context.Context, userID, mediaID int64) error
// ListProgressByUser returns all saved playback positions for a user.
ListProgressByUser(ctx context.Context, userID int64) ([]model.PlaybackProgress, error)
+ // ListInProgressMedia returns unfinished media with saved playback positions.
+ ListInProgressMedia(ctx context.Context, userID int64, filter MediaFilter) ([]model.Media, error)
}
// PlaybackAccumulatorRepo manages the 60s playback counter rule.
@@ -270,6 +276,8 @@ type PlaybackAccumulatorRepo interface {
UpsertAccumulator(ctx context.Context, acc *model.PlaybackAccumulator) error
// GetAccumulator returns the accumulator for a session and media item.
GetAccumulator(ctx context.Context, sessionID string, mediaID int64) (*model.PlaybackAccumulator, error)
+ // DeleteAccumulatorByMedia removes all accumulators for a media item.
+ DeleteAccumulatorByMedia(ctx context.Context, mediaID int64) error
}
// SessionRepo manages browser sessions.
diff --git a/internal/repository/sqlite_test.go b/internal/repository/sqlite_test.go
index bbe1330..adb24d7 100644
--- a/internal/repository/sqlite_test.go
+++ b/internal/repository/sqlite_test.go
@@ -464,7 +464,7 @@ func TestSQLite_PlaybackProgressRepo(t *testing.T) {
uid, _ := s.CreateUser(ctx, &model.User{Username: "u", PasswordHash: "h", CreatedAt: now})
sid, _ := s.CreateSet(ctx, &model.Set{Name: "s", RootPath: "/s", CreatedAt: now})
mid, _ := s.CreateMedia(ctx, &model.Media{SetID: sid, RelPath: "a.mp4", FileName: "a.mp4", AbsPath: "/s/a.mp4", Type: model.MediaTypeVideo, CreatedAt: now})
- if err := s.UpsertProgress(ctx, &model.PlaybackProgress{UserID: uid, MediaID: mid, PositionSeconds: 42, Finished: true, UpdatedAt: now}); err != nil {
+ if err := s.UpsertProgress(ctx, &model.PlaybackProgress{UserID: uid, MediaID: mid, PositionSeconds: 42, UpdatedAt: now}); err != nil {
t.Fatalf("upsert: %v", err)
}
p, err := s.GetProgress(ctx, uid, mid)
@@ -474,6 +474,16 @@ func TestSQLite_PlaybackProgressRepo(t *testing.T) {
if p.PositionSeconds != 42 {
t.Fatalf("expected 42, got %f", p.PositionSeconds)
}
+ if p.Finished {
+ t.Fatal("expected unfinished")
+ }
+ if err := s.MarkFinished(ctx, uid, mid); err != nil {
+ t.Fatalf("mark finished: %v", err)
+ }
+ p, err = s.GetProgress(ctx, uid, mid)
+ if err != nil {
+ t.Fatalf("get after mark finished: %v", err)
+ }
if !p.Finished {
t.Fatal("expected finished")
}
@@ -484,6 +494,55 @@ func TestSQLite_PlaybackProgressRepo(t *testing.T) {
if !pp[0].Finished {
t.Fatal("expected listed progress to be finished")
}
+ if err := s.DeleteProgress(ctx, uid, mid); err != nil {
+ t.Fatalf("delete progress: %v", err)
+ }
+ p, err = s.GetProgress(ctx, uid, mid)
+ if err != nil {
+ t.Fatalf("get after delete: %v", err)
+ }
+ if p != nil {
+ t.Fatal("expected nil progress after delete")
+ }
+ },
+ },
+ {
+ name: "list in-progress media respects allowed sets and excludes finished and deleted",
+ run: func(t *testing.T, ctx context.Context, s *SQLite) {
+ now := time.Now().Truncate(time.Second)
+ uid, _ := s.CreateUser(ctx, &model.User{Username: "u", PasswordHash: "h", CreatedAt: now})
+ setAllowed, _ := s.CreateSet(ctx, &model.Set{Name: "allowed", RootPath: "/allowed", CreatedAt: now})
+ setOther, _ := s.CreateSet(ctx, &model.Set{Name: "other", RootPath: "/other", CreatedAt: now})
+ keepID, _ := s.CreateMedia(ctx, &model.Media{SetID: setAllowed, RelPath: "keep.mp4", FileName: "keep.mp4", AbsPath: "/allowed/keep.mp4", Type: model.MediaTypeVideo, CreatedAt: now})
+ finishedID, _ := s.CreateMedia(ctx, &model.Media{SetID: setAllowed, RelPath: "finished.mp4", FileName: "finished.mp4", AbsPath: "/allowed/finished.mp4", Type: model.MediaTypeVideo, CreatedAt: now})
+ deletedID, _ := s.CreateMedia(ctx, &model.Media{SetID: setAllowed, RelPath: "deleted.mp4", FileName: "deleted.mp4", AbsPath: "/allowed/deleted.mp4", Type: model.MediaTypeVideo, CreatedAt: now})
+ otherID, _ := s.CreateMedia(ctx, &model.Media{SetID: setOther, RelPath: "other.mp4", FileName: "other.mp4", AbsPath: "/other/other.mp4", Type: model.MediaTypeVideo, CreatedAt: now})
+
+ progress := []model.PlaybackProgress{
+ {UserID: uid, MediaID: keepID, PositionSeconds: 10, UpdatedAt: now.Add(3 * time.Second)},
+ {UserID: uid, MediaID: finishedID, PositionSeconds: 20, Finished: true, UpdatedAt: now.Add(2 * time.Second)},
+ {UserID: uid, MediaID: deletedID, PositionSeconds: 30, UpdatedAt: now.Add(time.Second)},
+ {UserID: uid, MediaID: otherID, PositionSeconds: 40, UpdatedAt: now},
+ }
+ for i := range progress {
+ if err := s.UpsertProgress(ctx, &progress[i]); err != nil {
+ t.Fatalf("upsert progress %d: %v", i, err)
+ }
+ }
+ if err := s.SoftDeleteMedia(ctx, deletedID); err != nil {
+ t.Fatalf("soft delete: %v", err)
+ }
+
+ media, err := s.ListInProgressMedia(ctx, uid, MediaFilter{AllowedSetIDs: []int64{setAllowed}})
+ if err != nil {
+ t.Fatalf("list in-progress media: %v", err)
+ }
+ if len(media) != 1 {
+ t.Fatalf("expected 1 in-progress media, got %d: %+v", len(media), media)
+ }
+ if media[0].ID != keepID {
+ t.Fatalf("expected media %d, got %d", keepID, media[0].ID)
+ }
},
},
}
@@ -523,6 +582,50 @@ func TestSQLite_PlaybackAccumulatorRepo(t *testing.T) {
}
},
},
+ {
+ name: "delete by media deletes all sessions for media only",
+ run: func(t *testing.T, ctx context.Context, s *SQLite) {
+ now := time.Now().Truncate(time.Second)
+ uid, _ := s.CreateUser(ctx, &model.User{Username: "u", PasswordHash: "h", CreatedAt: now})
+ for _, sessionID := range []string{"sess1", "sess2"} {
+ if err := s.CreateSession(ctx, &model.Session{ID: sessionID, UserID: uid, ExpiresAt: now.Add(time.Hour), CreatedAt: now}); err != nil {
+ t.Fatalf("create session %s: %v", sessionID, err)
+ }
+ }
+ sid, _ := s.CreateSet(ctx, &model.Set{Name: "s", RootPath: "/s", CreatedAt: now})
+ mediaID, _ := s.CreateMedia(ctx, &model.Media{SetID: sid, RelPath: "a.mp4", FileName: "a.mp4", AbsPath: "/s/a.mp4", Type: model.MediaTypeVideo, CreatedAt: now})
+ otherMediaID, _ := s.CreateMedia(ctx, &model.Media{SetID: sid, RelPath: "b.mp4", FileName: "b.mp4", AbsPath: "/s/b.mp4", Type: model.MediaTypeVideo, CreatedAt: now})
+ accs := []model.PlaybackAccumulator{
+ {SessionID: "sess1", MediaID: mediaID, LastPosition: 10, AccumulatedSeconds: 20, UpdatedAt: now},
+ {SessionID: "sess2", MediaID: mediaID, LastPosition: 11, AccumulatedSeconds: 21, UpdatedAt: now},
+ {SessionID: "sess1", MediaID: otherMediaID, LastPosition: 12, AccumulatedSeconds: 22, UpdatedAt: now},
+ }
+ for i := range accs {
+ if err := s.UpsertAccumulator(ctx, &accs[i]); err != nil {
+ t.Fatalf("upsert accumulator %d: %v", i, err)
+ }
+ }
+ if err := s.DeleteAccumulatorByMedia(ctx, mediaID); err != nil {
+ t.Fatalf("delete accumulator by media: %v", err)
+ }
+ for _, sessionID := range []string{"sess1", "sess2"} {
+ acc, err := s.GetAccumulator(ctx, sessionID, mediaID)
+ if err != nil {
+ t.Fatalf("get deleted accumulator %s: %v", sessionID, err)
+ }
+ if acc != nil {
+ t.Fatalf("expected accumulator %s/%d to be deleted", sessionID, mediaID)
+ }
+ }
+ acc, err := s.GetAccumulator(ctx, "sess1", otherMediaID)
+ if err != nil {
+ t.Fatalf("get other accumulator: %v", err)
+ }
+ if acc == nil {
+ t.Fatal("expected other media accumulator to remain")
+ }
+ },
+ },
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {