generated from VLADIMIR/template
up
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
package playlist_service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"evening_detective_server/internal/repos/playlists_repo"
|
||||
)
|
||||
|
||||
func TestReorderRejectsInvalidIDsBeforeRepository(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
playlistID int
|
||||
ids []int
|
||||
}{
|
||||
{"playlist", 0, []int{1}}, {"negative", 1, []int{-1}}, {"duplicate", 1, []int{1, 1}}, {"zero", 1, []int{0}},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
_, err := New(nil, nil).Reorder(context.Background(), tc.playlistID, tc.ids)
|
||||
if !errors.Is(err, ErrInvalidTrack) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestOrderConflictMapping(t *testing.T) {
|
||||
for _, tc := range []struct{ input, expected error }{{playlists_repo.ErrTrackOrderChanged, ErrTrackOrderChanged}} {
|
||||
if !errors.Is(mapError(tc.input), tc.expected) {
|
||||
t.Fatal("lost conflict")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,13 +11,14 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
ErrInvalidName = errors.New("некорректное имя плейлиста")
|
||||
ErrInvalidTrack = errors.New("некорректный трек")
|
||||
ErrPlaylistNotFound = errors.New("плейлист не найден")
|
||||
ErrPlaylistInUse = errors.New("плейлист используется")
|
||||
ErrTrackNotFound = errors.New("трек не найден")
|
||||
ErrGameNotFound = errors.New("игра не найдена")
|
||||
ErrScenarioNotFound = errors.New("сценарий не найден")
|
||||
ErrTrackOrderChanged = errors.New("состав треков изменился; обновите список")
|
||||
ErrInvalidName = errors.New("некорректное имя плейлиста")
|
||||
ErrInvalidTrack = errors.New("некорректный трек")
|
||||
ErrPlaylistNotFound = errors.New("плейлист не найден")
|
||||
ErrPlaylistInUse = errors.New("плейлист используется")
|
||||
ErrTrackNotFound = errors.New("трек не найден")
|
||||
ErrGameNotFound = errors.New("игра не найдена")
|
||||
ErrScenarioNotFound = errors.New("сценарий не найден")
|
||||
)
|
||||
|
||||
// Service implements playlist and game music use cases.
|
||||
@@ -150,6 +151,8 @@ func (s *Service) GetGame(ctx context.Context, gameID int) (*MusicState, error)
|
||||
|
||||
func mapError(err error) error {
|
||||
switch {
|
||||
case errors.Is(err, playlists_repo.ErrTrackOrderChanged):
|
||||
return ErrTrackOrderChanged
|
||||
case errors.Is(err, playlists_repo.ErrPlaylistNotFound):
|
||||
return ErrPlaylistNotFound
|
||||
case errors.Is(err, playlists_repo.ErrPlaylistInUse):
|
||||
@@ -164,3 +167,22 @@ func mapError(err error) error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Reorder validates a complete track order and preserves live playback.
|
||||
func (s *Service) Reorder(ctx context.Context, playlistID int, trackIDs []int) (*Playlist, error) {
|
||||
if playlistID <= 0 {
|
||||
return nil, ErrInvalidTrack
|
||||
}
|
||||
seen := make(map[int]bool, len(trackIDs))
|
||||
for _, id := range trackIDs {
|
||||
if id <= 0 || seen[id] {
|
||||
return nil, ErrInvalidTrack
|
||||
}
|
||||
seen[id] = true
|
||||
}
|
||||
item, err := s.repo.Reorder(ctx, playlistID, trackIDs)
|
||||
if err != nil {
|
||||
return nil, mapError(err)
|
||||
}
|
||||
return mapPlaylist(item), nil
|
||||
}
|
||||
|
||||
@@ -100,15 +100,16 @@ func (s *ScreenPlanService) SetStep(ctx context.Context, gameID, currentStep int
|
||||
}
|
||||
|
||||
// ApplyCurrent applies the current step when a screen joins a game.
|
||||
func (s *ScreenPlanService) ApplyCurrent(ctx context.Context, gameID int) error {
|
||||
func (s *ScreenPlanService) ApplyCurrent(ctx context.Context, gameID, screenID int) error {
|
||||
plan, err := s.Get(ctx, gameID)
|
||||
if errors.Is(err, ErrPlanNotFound) {
|
||||
return nil
|
||||
return s.screenService.InitializeScreen(ctx, screenID, gameID, 1, "", "Перерыв", 600)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return s.applyStep(ctx, gameID, plan)
|
||||
step := plan.Steps[plan.CurrentStep]
|
||||
return s.screenService.InitializeScreen(ctx, screenID, gameID, step.Mode, step.Text, step.TimerLabel, step.TimerDurationSeconds)
|
||||
}
|
||||
|
||||
func normalizePlan(steps []Step, currentStep int) ([]Step, error) {
|
||||
|
||||
@@ -73,8 +73,9 @@ func (s *ScreenService) SetCommand(ctx context.Context, gameID int, command int3
|
||||
}
|
||||
|
||||
// Bind attaches a screen session to a game.
|
||||
func (s *ScreenService) Bind(ctx context.Context, id, gameID int, tokenHash string) error {
|
||||
return mapError(s.repo.Bind(ctx, id, gameID, tokenHash))
|
||||
func (s *ScreenService) Bind(ctx context.Context, id, gameID int, tokenHash string, initializeAudio bool) (bool, error) {
|
||||
isNewBinding, err := s.repo.BindWithAudio(ctx, id, gameID, tokenHash, initializeAudio)
|
||||
return isNewBinding, mapError(err)
|
||||
}
|
||||
|
||||
// Unbind detaches a screen session from its game.
|
||||
@@ -93,3 +94,8 @@ func mapError(err error) error {
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// InitializeScreen joins the current display without resetting other screens.
|
||||
func (s *ScreenService) InitializeScreen(ctx context.Context, id, gameID int, mode int32, text, label string, duration int64) error {
|
||||
return s.repo.InitializeScreen(ctx, id, gameID, mode, text, label, duration)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user