generated from VLADIMIR/template
up
This commit is contained in:
@@ -36,7 +36,7 @@ func TestExportSnapshotAndEditableGame(t *testing.T) {
|
||||
requireNoError(t, err)
|
||||
selected, err := music.Create(ctx, "Selected by screen")
|
||||
requireNoError(t, err)
|
||||
requireNoError(t, music.AddTrack(ctx, selected.ID, "Grouped", "grouped.mp3"))
|
||||
requireNoError(t, music.AddTrack(ctx, selected.ID, 0, "Grouped", "grouped.mp3"))
|
||||
selected, err = music.SaveGroup(ctx, selected.ID, 0, "Final", "stop")
|
||||
requireNoError(t, err)
|
||||
selected, err = music.SetTrackGroup(ctx, selected.ID, selected.Tracks[0].ID, selected.Groups[0].ID)
|
||||
|
||||
@@ -16,7 +16,7 @@ func TestGroupEditingProtectsLivePlayback(t *testing.T) {
|
||||
p, err := f.repo.Create(ctx, "Music")
|
||||
requireNoError(t, err)
|
||||
for _, name := range []string{"one", "two", "three"} {
|
||||
requireNoError(t, f.repo.AddTrack(ctx, p.ID, name, name+".mp3"))
|
||||
requireNoError(t, f.repo.AddTrack(ctx, p.ID, 0, name, name+".mp3"))
|
||||
}
|
||||
p, err = f.repo.SaveGroup(ctx, p.ID, 0, "First", "repeat")
|
||||
requireNoError(t, err)
|
||||
@@ -101,7 +101,7 @@ func TestGroupAssignmentsAndPlaybackRejectForeignIDs(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
p, err := f.repo.Create(ctx, "One")
|
||||
requireNoError(t, err)
|
||||
requireNoError(t, f.repo.AddTrack(ctx, p.ID, "Track", "track.mp3"))
|
||||
requireNoError(t, f.repo.AddTrack(ctx, p.ID, 0, "Track", "track.mp3"))
|
||||
p, err = f.repo.SaveGroup(ctx, p.ID, 0, "Empty", "repeat")
|
||||
requireNoError(t, err)
|
||||
other, err := f.repo.Create(ctx, "Other")
|
||||
@@ -132,7 +132,7 @@ func TestConcurrentCompletionAdvancesOnce(t *testing.T) {
|
||||
p, err := f.repo.Create(ctx, "Concurrent completion")
|
||||
requireNoError(t, err)
|
||||
for _, name := range []string{"one", "two"} {
|
||||
requireNoError(t, f.repo.AddTrack(ctx, p.ID, name, name+".mp3"))
|
||||
requireNoError(t, f.repo.AddTrack(ctx, p.ID, 0, name, name+".mp3"))
|
||||
}
|
||||
before, err := f.repo.SetMusic(ctx, f.gameID, p.ID, 0, 0, 50, false, playback, 0)
|
||||
requireNoError(t, err)
|
||||
|
||||
@@ -20,7 +20,7 @@ func TestReorderPreservesPlaybackAndRejectsChangedMembership(t *testing.T) {
|
||||
p, err := f.repo.Create(ctx, "Music")
|
||||
requireNoError(t, err)
|
||||
for _, name := range []string{"One", "Two", "Three"} {
|
||||
requireNoError(t, f.repo.AddTrack(ctx, p.ID, name, name+".mp3"))
|
||||
requireNoError(t, f.repo.AddTrack(ctx, p.ID, 0, name, name+".mp3"))
|
||||
}
|
||||
p, err = f.repo.Get(ctx, p.ID)
|
||||
requireNoError(t, err)
|
||||
@@ -90,7 +90,7 @@ func TestConcurrentAdditionsKeepDistinctPositions(t *testing.T) {
|
||||
var wg sync.WaitGroup
|
||||
failures := make(chan error, count)
|
||||
for i := 0; i < count; i++ {
|
||||
wg.Go(func() { failures <- f.repo.AddTrack(ctx, p.ID, "Track", fmt.Sprintf("track-%d.mp3", i)) })
|
||||
wg.Go(func() { failures <- f.repo.AddTrack(ctx, p.ID, 0, "Track", fmt.Sprintf("track-%d.mp3", i)) })
|
||||
}
|
||||
wg.Wait()
|
||||
close(failures)
|
||||
@@ -172,7 +172,7 @@ func TestReorderReadersSeeConsistentTrackAndIndex(t *testing.T) {
|
||||
p, err := f.repo.Create(ctx, "Snapshot")
|
||||
requireNoError(t, err)
|
||||
for _, name := range []string{"a", "b", "c"} {
|
||||
requireNoError(t, f.repo.AddTrack(ctx, p.ID, name, name+".mp3"))
|
||||
requireNoError(t, f.repo.AddTrack(ctx, p.ID, 0, name, name+".mp3"))
|
||||
}
|
||||
p, err = f.repo.Get(ctx, p.ID)
|
||||
requireNoError(t, err)
|
||||
|
||||
@@ -242,7 +242,7 @@ func (r *Repo) Delete(ctx context.Context, id int) error {
|
||||
}
|
||||
|
||||
// AddTrack adds a track to a playlist.
|
||||
func (r *Repo) AddTrack(ctx context.Context, playlistID int, name, filename string) error {
|
||||
func (r *Repo) AddTrack(ctx context.Context, playlistID, groupID int, name, filename string) error {
|
||||
tx, err := r.pool.Begin(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -251,6 +251,14 @@ func (r *Repo) AddTrack(ctx context.Context, playlistID int, name, filename stri
|
||||
if err := lockPlaylist(ctx, tx, playlistID); err != nil {
|
||||
return err
|
||||
}
|
||||
if groupID != 0 {
|
||||
if _, err := readGroup(ctx, tx, playlistID, groupID); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := groupInUse(ctx, tx, playlistID, groupID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
var position int
|
||||
if err := tx.QueryRow(
|
||||
ctx,
|
||||
@@ -266,13 +274,14 @@ func (r *Repo) AddTrack(ctx context.Context, playlistID int, name, filename stri
|
||||
_, err = tx.Exec(
|
||||
ctx,
|
||||
`
|
||||
INSERT INTO playlist_tracks (playlist_id, name, filename, position)
|
||||
VALUES ($1, $2, $3, $4)
|
||||
INSERT INTO playlist_tracks (playlist_id, name, filename, position, group_id)
|
||||
VALUES ($1, $2, $3, $4, NULLIF($5, 0))
|
||||
`,
|
||||
playlistID,
|
||||
name,
|
||||
filename,
|
||||
position,
|
||||
groupID,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
package playlists_repo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAddTrackSavesTargetGroupAtomically(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
target string
|
||||
playback int32
|
||||
activeGroup bool
|
||||
want error
|
||||
}{
|
||||
{name: "ungrouped", target: "none"},
|
||||
{name: "empty group", target: "second"},
|
||||
{name: "populated group", target: "first"},
|
||||
{name: "missing group", target: "missing", want: ErrGroupNotFound},
|
||||
{name: "foreign group", target: "foreign", want: ErrGroupNotFound},
|
||||
{name: "deleted group", target: "deleted", want: ErrGroupNotFound},
|
||||
{name: "playing group", target: "first", playback: 2, activeGroup: true, want: ErrPlaylistInUse},
|
||||
{name: "paused group", target: "first", playback: 3, activeGroup: true, want: ErrPlaylistInUse},
|
||||
{name: "another group playing", target: "second", playback: 2, activeGroup: true},
|
||||
{name: "whole playlist playing", target: "second", playback: 2, want: ErrPlaylistInUse},
|
||||
{name: "legacy ungrouped upload during playback", target: "none", playback: 2},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
f := newPlaylistFixture(t)
|
||||
ctx := context.Background()
|
||||
p, err := f.repo.Create(ctx, "Upload")
|
||||
requireNoError(t, err)
|
||||
p, err = f.repo.SaveGroup(ctx, p.ID, 0, "First", "repeat")
|
||||
requireNoError(t, err)
|
||||
first := p.Groups[0].ID
|
||||
p, err = f.repo.SaveGroup(ctx, p.ID, 0, "Second", "stop")
|
||||
requireNoError(t, err)
|
||||
second := p.Groups[1].ID
|
||||
requireNoError(t, f.repo.AddTrack(ctx, p.ID, first, "Existing", "existing.mp3"))
|
||||
groupID := first
|
||||
switch tc.target {
|
||||
case "none":
|
||||
groupID = 0
|
||||
case "second", "deleted":
|
||||
groupID = second
|
||||
case "missing":
|
||||
groupID = second + 1000000
|
||||
case "foreign":
|
||||
other, err := f.repo.Create(ctx, "Other")
|
||||
requireNoError(t, err)
|
||||
other, err = f.repo.SaveGroup(ctx, other.ID, 0, "Foreign", "repeat")
|
||||
requireNoError(t, err)
|
||||
groupID = other.Groups[0].ID
|
||||
}
|
||||
if tc.target == "deleted" {
|
||||
_, err := f.repo.DeleteGroup(ctx, p.ID, groupID)
|
||||
requireNoError(t, err)
|
||||
}
|
||||
if tc.playback != 0 {
|
||||
activeGroup := 0
|
||||
if tc.activeGroup {
|
||||
activeGroup = first
|
||||
}
|
||||
_, err := f.repo.SetMusic(ctx, f.gameID, p.ID, 0, 0, 50, false, tc.playback, activeGroup)
|
||||
requireNoError(t, err)
|
||||
}
|
||||
before, err := f.repo.Get(ctx, p.ID)
|
||||
requireNoError(t, err)
|
||||
err = f.repo.AddTrack(ctx, p.ID, groupID, "New", "new.mp3")
|
||||
if !errors.Is(err, tc.want) {
|
||||
t.Fatalf("expected %v, got %v", tc.want, err)
|
||||
}
|
||||
after, err := f.repo.Get(ctx, p.ID)
|
||||
requireNoError(t, err)
|
||||
if tc.want != nil {
|
||||
if !reflect.DeepEqual(before.Tracks, after.Tracks) {
|
||||
t.Fatal("rejected upload changed playlist tracks")
|
||||
}
|
||||
return
|
||||
}
|
||||
if len(after.Tracks) != len(before.Tracks)+1 {
|
||||
t.Fatal("upload did not add exactly one track")
|
||||
}
|
||||
track := after.Tracks[len(after.Tracks)-1]
|
||||
if track.GroupID != groupID || track.Name != "New" || track.Position != before.Tracks[len(before.Tracks)-1].Position+1 {
|
||||
t.Fatalf("track was not appended to the target group: %+v", track)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user