Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
48f7ceff62 |
@@ -74,7 +74,12 @@ public class ReplacePlaylistItemsHandler(IDbContextFactory<TvContext> dbContextF
|
||||
CancellationToken cancellationToken) =>
|
||||
PlaylistMustExist(dbContext, request.PlaylistId, cancellationToken)
|
||||
.BindT(playlist => CollectionTypesMustBeValid(request, playlist))
|
||||
.BindT(playlist => PlaybackOrdersMustBeSupported(request, playlist));
|
||||
.BindT(playlist => PlaybackOrdersMustBeSupported(request, playlist))
|
||||
.BindT(playlist => ValidateName(request).Map(_ => playlist));
|
||||
|
||||
private static Validation<BaseError, string> ValidateName(ReplacePlaylistItems request) =>
|
||||
request.NotEmpty(x => x.Name)
|
||||
.Bind(_ => request.NotLongerThan(50)(x => x.Name));
|
||||
|
||||
private static Validation<BaseError, Playlist> PlaybackOrdersMustBeSupported(
|
||||
ReplacePlaylistItems request,
|
||||
|
||||
+120
@@ -0,0 +1,120 @@
|
||||
using ErsatzTV.Application;
|
||||
using ErsatzTV.Application.MediaCollections;
|
||||
using ErsatzTV.Core;
|
||||
using ErsatzTV.Core.Domain;
|
||||
using ErsatzTV.Infrastructure.Data;
|
||||
using ErsatzTV.Tests.Support;
|
||||
using LanguageExt;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NUnit.Framework;
|
||||
using Shouldly;
|
||||
using static LanguageExt.Prelude;
|
||||
|
||||
namespace ErsatzTV.Tests.Application.MediaCollections;
|
||||
|
||||
/// <summary>
|
||||
/// Issue #327: ReplacePlaylistItemsHandler (playlist rename) did no name validation, so a playlist
|
||||
/// could be renamed to an empty/whitespace or over-long name even though CreatePlaylistHandler
|
||||
/// already refuses those on create. Mirrors RenamePlaylistGroupHandler's ValidateName combinator
|
||||
/// (NotEmpty + NotLongerThan(50)) so create and rename enforce the same rule.
|
||||
/// </summary>
|
||||
[TestFixture]
|
||||
public class ReplacePlaylistItemsHandlerNameValidationTests
|
||||
{
|
||||
private InMemoryTvContext _db = null!;
|
||||
|
||||
[SetUp]
|
||||
public async Task SetUp() => _db = await InMemoryTvContext.CreateAsync();
|
||||
|
||||
[TearDown]
|
||||
public async Task TearDown() => await _db.DisposeAsync();
|
||||
|
||||
private async Task SeedPlaylistAsync()
|
||||
{
|
||||
await using TvContext ctx = _db.CreateContext();
|
||||
ctx.Playlists.Add(
|
||||
new Playlist
|
||||
{
|
||||
Id = 1,
|
||||
PlaylistGroupId = 1,
|
||||
Name = "Kids",
|
||||
IsSystem = false,
|
||||
Version = 1,
|
||||
Items = new List<PlaylistItem>()
|
||||
});
|
||||
await ctx.SaveChangesAsync();
|
||||
}
|
||||
|
||||
private static ReplacePlaylistItems Command(string name) =>
|
||||
new(
|
||||
1,
|
||||
name,
|
||||
new List<ReplacePlaylistItem>
|
||||
{
|
||||
new(0, CollectionType.Movie, null, null, null, 55, PlaybackOrder.Shuffle, null, false, true)
|
||||
},
|
||||
None);
|
||||
|
||||
private async Task<string> ReadNameAsync()
|
||||
{
|
||||
await using TvContext ctx = _db.CreateContext();
|
||||
return await ctx.Playlists.Where(p => p.Id == 1).Select(p => p.Name).SingleAsync();
|
||||
}
|
||||
|
||||
private static BaseError? LeftOrNull<T>(Either<BaseError, T> result) =>
|
||||
result.Match<BaseError?>(Right: _ => null, Left: e => e);
|
||||
|
||||
[Test]
|
||||
public async Task Empty_Name_Should_Be_Rejected_And_Not_Mutate()
|
||||
{
|
||||
await SeedPlaylistAsync();
|
||||
var handler = new ReplacePlaylistItemsHandler(_db.Factory);
|
||||
|
||||
Either<BaseError, List<PlaylistItemViewModel>> result =
|
||||
await handler.Handle(Command(string.Empty), CancellationToken.None);
|
||||
|
||||
LeftOrNull(result).ShouldNotBeNull();
|
||||
(await ReadNameAsync()).ShouldBe("Kids");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Whitespace_Name_Should_Be_Rejected_And_Not_Mutate()
|
||||
{
|
||||
await SeedPlaylistAsync();
|
||||
var handler = new ReplacePlaylistItemsHandler(_db.Factory);
|
||||
|
||||
Either<BaseError, List<PlaylistItemViewModel>> result =
|
||||
await handler.Handle(Command(" "), CancellationToken.None);
|
||||
|
||||
LeftOrNull(result).ShouldNotBeNull();
|
||||
(await ReadNameAsync()).ShouldBe("Kids");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Over_Long_Name_Should_Be_Rejected_And_Not_Mutate()
|
||||
{
|
||||
await SeedPlaylistAsync();
|
||||
var handler = new ReplacePlaylistItemsHandler(_db.Factory);
|
||||
|
||||
string tooLong = new string('a', 51);
|
||||
Either<BaseError, List<PlaylistItemViewModel>> result =
|
||||
await handler.Handle(Command(tooLong), CancellationToken.None);
|
||||
|
||||
LeftOrNull(result).ShouldNotBeNull();
|
||||
(await ReadNameAsync()).ShouldBe("Kids");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Valid_Rename_Should_Succeed()
|
||||
{
|
||||
await SeedPlaylistAsync();
|
||||
var handler = new ReplacePlaylistItemsHandler(_db.Factory);
|
||||
|
||||
string maxLength = new string('a', 50);
|
||||
Either<BaseError, List<PlaylistItemViewModel>> result =
|
||||
await handler.Handle(Command(maxLength), CancellationToken.None);
|
||||
|
||||
result.IsRight.ShouldBeTrue();
|
||||
(await ReadNameAsync()).ShouldBe(maxLength);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user