This commit is contained in:
2026-10-18 05:28:57 +07:00
parent bfd6841d7d
commit e96199afba
18 changed files with 2331 additions and 1193 deletions
+2
View File
@@ -18,6 +18,8 @@ func playlistErrorStatus(err error, fallback string) error {
errors.Is(err, playlist_service.ErrGameNotFound),
errors.Is(err, playlist_service.ErrScenarioNotFound):
return status.Error(codes.NotFound, err.Error())
case errors.Is(err, playlist_service.ErrTrackOrderChanged):
return status.Error(codes.Aborted, err.Error())
case errors.Is(err, playlist_service.ErrPlaylistInUse):
return status.Error(codes.FailedPrecondition, err.Error())
default:
+47
View File
@@ -0,0 +1,47 @@
package app
import (
"context"
"errors"
"testing"
"evening_detective_server/internal/services/playlist_service"
proto "evening_detective_server/proto"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
func TestPlaylistOrderErrorCodes(t *testing.T) {
cases := []struct {
name string
err error
code codes.Code
}{
{"invalid", playlist_service.ErrInvalidTrack, codes.InvalidArgument},
{"conflict", playlist_service.ErrTrackOrderChanged, codes.Aborted},
{"missing", playlist_service.ErrPlaylistNotFound, codes.NotFound},
{"internal", errors.New("secret SQL"), codes.Internal},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
err := playlistErrorStatus(tc.err, "Не удалось сохранить порядок")
if status.Code(err) != tc.code {
t.Fatal(err)
}
if tc.code == codes.Internal && status.Convert(err).Message() != "Не удалось сохранить порядок" {
t.Fatal("internal details exposed")
}
})
}
}
func TestPlaylistOrderRequiresAuthentication(t *testing.T) {
for _, ctx := range []context.Context{context.Background()} {
t.Run("anonymous", func(t *testing.T) {
_, err := (&server{}).ReorderPlaylistTracks(ctx, &proto.ReorderPlaylistTracksReq{PlaylistID: 1, TrackIDs: []int32{1}})
if status.Code(err) != codes.PermissionDenied {
t.Fatal(err)
}
})
}
}
+15
View File
@@ -159,3 +159,18 @@ func (s *server) SetGameMusic(ctx context.Context, req *proto.SetGameMusicReq) (
}
return &proto.SetGameMusicRsp{State: mapMusicState(state)}, nil
}
func (s *server) ReorderPlaylistTracks(ctx context.Context, req *proto.ReorderPlaylistTracksReq) (*proto.ReorderPlaylistTracksRsp, error) {
if !canManagePlaylists(ctx) {
return nil, status.Error(codes.PermissionDenied, "permission denied")
}
ids := make([]int, len(req.GetTrackIDs()))
for i, id := range req.GetTrackIDs() {
ids[i] = int(id)
}
item, err := s.playlistService.Reorder(ctx, int(req.GetPlaylistID()), ids)
if err != nil {
return nil, playlistErrorStatus(err, "не удалось сохранить порядок треков")
}
return &proto.ReorderPlaylistTracksRsp{Playlist: mapPlaylist(item)}, nil
}
+6 -3
View File
@@ -123,14 +123,17 @@ func (s *server) BindScreen(ctx context.Context, req *proto.BindScreenReq) (*pro
return nil, status.Error(codes.NotFound, "игра не найдена")
}
hash := hashScreenToken(req.AccessToken)
if err = s.screenService.Bind(ctx, id, int(req.GameID), hash); err != nil {
isNewBinding, err := s.screenService.Bind(ctx, id, int(req.GameID), hash, req.GetInitializeAudio())
if err != nil {
if errors.Is(err, screen_service.ErrScreenNotFound) {
return nil, status.Error(codes.Unauthenticated, "недействительная сессия экрана")
}
return nil, status.Error(codes.Internal, "не удалось привязать экран")
}
if err := s.screenPlanService.ApplyCurrent(ctx, int(req.GameID)); err != nil {
return nil, status.Error(codes.Internal, "не удалось применить план экранов")
if isNewBinding {
if err := s.screenPlanService.ApplyCurrent(ctx, int(req.GameID), id); err != nil {
return nil, status.Error(codes.Internal, "не удалось применить план экранов")
}
}
session, err := s.screenService.Get(ctx, id, hash)
if err != nil {