diff --git a/JSMR.Application/VoiceWorks/Commands/Delete/DeleteVoiceWorkHandler.cs b/JSMR.Application/VoiceWorks/Commands/Delete/DeleteVoiceWorkHandler.cs new file mode 100644 index 0000000..922d855 --- /dev/null +++ b/JSMR.Application/VoiceWorks/Commands/Delete/DeleteVoiceWorkHandler.cs @@ -0,0 +1,11 @@ +using JSMR.Application.VoiceWorks.Ports; + +namespace JSMR.Application.VoiceWorks.Commands.Delete; + +public sealed class DeleteVoiceWorkFavoriteHandler(IVoiceWorkWriter writer) +{ + public async Task HandleAsync(DeleteVoiceWorkRequest request, CancellationToken cancellationToken = default) + { + return await writer.DeleteAsync(request, cancellationToken); + } +} \ No newline at end of file diff --git a/JSMR.Application/VoiceWorks/Commands/Delete/DeleteVoiceWorkRequest.cs b/JSMR.Application/VoiceWorks/Commands/Delete/DeleteVoiceWorkRequest.cs new file mode 100644 index 0000000..2e937f7 --- /dev/null +++ b/JSMR.Application/VoiceWorks/Commands/Delete/DeleteVoiceWorkRequest.cs @@ -0,0 +1,3 @@ +namespace JSMR.Application.VoiceWorks.Commands.Delete; + +public sealed record DeleteVoiceWorkRequest(int[] VoiceWorkIds); \ No newline at end of file diff --git a/JSMR.Application/VoiceWorks/Commands/Delete/DeleteVoiceWorkResponse.cs b/JSMR.Application/VoiceWorks/Commands/Delete/DeleteVoiceWorkResponse.cs new file mode 100644 index 0000000..a48b637 --- /dev/null +++ b/JSMR.Application/VoiceWorks/Commands/Delete/DeleteVoiceWorkResponse.cs @@ -0,0 +1,3 @@ +namespace JSMR.Application.VoiceWorks.Commands.Delete; + +public sealed record DeleteVoiceWorkResponse(Dictionary IsSuccess); \ No newline at end of file diff --git a/JSMR.Application/VoiceWorks/Ports/IVoiceWorkWriter.cs b/JSMR.Application/VoiceWorks/Ports/IVoiceWorkWriter.cs index 50c70dd..00b62fd 100644 --- a/JSMR.Application/VoiceWorks/Ports/IVoiceWorkWriter.cs +++ b/JSMR.Application/VoiceWorks/Ports/IVoiceWorkWriter.cs @@ -1,8 +1,10 @@ -using JSMR.Application.VoiceWorks.Commands.SetFavorite; +using JSMR.Application.VoiceWorks.Commands.Delete; +using JSMR.Application.VoiceWorks.Commands.SetFavorite; namespace JSMR.Application.VoiceWorks.Ports; public interface IVoiceWorkWriter { Task SetFavoriteAsync(SetVoiceWorkFavoriteRequest request, CancellationToken cancellationToken); + Task DeleteAsync(DeleteVoiceWorkRequest request, CancellationToken cancellationToken); } \ No newline at end of file diff --git a/JSMR.Infrastructure/Data/Repositories/VoiceWorks/VoiceWorkWriter.cs b/JSMR.Infrastructure/Data/Repositories/VoiceWorks/VoiceWorkWriter.cs index db80b04..8d25a69 100644 --- a/JSMR.Infrastructure/Data/Repositories/VoiceWorks/VoiceWorkWriter.cs +++ b/JSMR.Infrastructure/Data/Repositories/VoiceWorks/VoiceWorkWriter.cs @@ -1,4 +1,5 @@ -using JSMR.Application.VoiceWorks.Commands.SetFavorite; +using JSMR.Application.VoiceWorks.Commands.Delete; +using JSMR.Application.VoiceWorks.Commands.SetFavorite; using JSMR.Application.VoiceWorks.Ports; using JSMR.Domain.Entities; using Microsoft.EntityFrameworkCore; @@ -17,6 +18,32 @@ public class VoiceWorkWriter(AppDbContext dbContext) : IVoiceWorkWriter return new SetVoiceWorkFavoriteResponse(request.VoiceWorkId, request.IsFavorite); } + public async Task DeleteAsync(DeleteVoiceWorkRequest request, CancellationToken cancellationToken) + { + Dictionary isSuccess = []; + + VoiceWork[] voiceWorks = [.. dbContext.VoiceWorks.Where(voiceWork => request.VoiceWorkIds.Contains(voiceWork.VoiceWorkId)) + .Include(x => x.Circle)]; + + foreach (VoiceWork voiceWork in voiceWorks) + { + isSuccess.Add(voiceWork.VoiceWorkId, false); + + if (voiceWork.Circle is null) + continue; + + if (voiceWork.IsValid == true && voiceWork.Circle.Spam == false) + continue; + + dbContext.Remove(voiceWork); + isSuccess[voiceWork.VoiceWorkId] = true; + } + + dbContext.SaveChanges(); + + return new DeleteVoiceWorkResponse(isSuccess); + } + private async Task GetVoiceWorkAsync(int voiceWorkId, CancellationToken cancellationToken) { return await dbContext.VoiceWorks.FirstOrDefaultAsync(voiceWork => voiceWork.VoiceWorkId == voiceWorkId, cancellationToken) diff --git a/JSMR.Tests/Data/Repositories/VoiceWorks/Delete_Voice_Work_Tests.cs b/JSMR.Tests/Data/Repositories/VoiceWorks/Delete_Voice_Work_Tests.cs new file mode 100644 index 0000000..d7c7a5f --- /dev/null +++ b/JSMR.Tests/Data/Repositories/VoiceWorks/Delete_Voice_Work_Tests.cs @@ -0,0 +1,40 @@ +using JSMR.Application.VoiceWorks.Commands.Delete; +using JSMR.Infrastructure.Data; +using JSMR.Infrastructure.Data.Repositories.VoiceWorks; +using JSMR.Tests.Fixtures; +using Shouldly; + +namespace JSMR.Tests.Data.Repositories.VoiceWorks; + +public class Delete_Voice_Work_Tests(MariaDbContainerFixture container) : VoiceWorkRepositoryTests(container) +{ + [Fact] + public async Task Try_Delete_Invalid_Voice_Work() + { + await using AppDbContext dbContext = await GetAppDbContextAsync(); + VoiceWorkWriter writer = new(dbContext); + + int voiceWorkId = 3; + DeleteVoiceWorkRequest request = new([voiceWorkId]); + + DeleteVoiceWorkResponse response = await writer.DeleteAsync(request, TestContext.Current.CancellationToken); + response.IsSuccess.Count.ShouldBe(1); + response.IsSuccess.ShouldContainKey(voiceWorkId); + response.IsSuccess[voiceWorkId].ShouldBeTrue(); + } + + [Fact] + public async Task Try_Delete_Valid_Voice_Work() + { + await using AppDbContext dbContext = await GetAppDbContextAsync(); + VoiceWorkWriter writer = new(dbContext); + + int voiceWorkId = 1; + DeleteVoiceWorkRequest request = new([voiceWorkId]); + + DeleteVoiceWorkResponse response = await writer.DeleteAsync(request, TestContext.Current.CancellationToken); + response.IsSuccess.Count.ShouldBe(1); + response.IsSuccess.ShouldContainKey(voiceWorkId); + response.IsSuccess[voiceWorkId].ShouldBeFalse(); + } +} \ No newline at end of file diff --git a/JSMR.Tests/Data/Repositories/VoiceWorks/VoiceWorkRepositorySeedData.cs b/JSMR.Tests/Data/Repositories/VoiceWorks/VoiceWorkRepositorySeedData.cs index ab89cc5..8d53d57 100644 --- a/JSMR.Tests/Data/Repositories/VoiceWorks/VoiceWorkRepositorySeedData.cs +++ b/JSMR.Tests/Data/Repositories/VoiceWorks/VoiceWorkRepositorySeedData.cs @@ -19,11 +19,65 @@ public static class VoiceWorkRepositorySeedData ); context.VoiceWorks.AddRange( - new() { VoiceWorkId = 1, CircleId = 1, ProductId = "RJ0000001", ProductName = "Today Sounds", Description = "An average product.", Status = (byte)VoiceWorkStatus.Available, SalesDate = new(2025, 1, 1), Downloads = 500, WishlistCount = 750, StarRating = 35 }, - new() { VoiceWorkId = 2, CircleId = 2, ProductId = "RJ0000002", ProductName = "Super Comfy ASMR", Description = "An amazing product!", Status = (byte)VoiceWorkStatus.NewRelease, SalesDate = new(2025, 1, 3), Downloads = 5000, WishlistCount = 12000, StarRating = 50, Favorite = true }, - new() { VoiceWorkId = 3, CircleId = 3, ProductId = "RJ0000003", ProductName = "Low Effort", Description = "A bad product.", Status = (byte)VoiceWorkStatus.Available, SalesDate = new(2025, 1, 2), Downloads = 50, WishlistCount = 100, StarRating = 20 }, - new() { VoiceWorkId = 4, CircleId = 1, ProductId = "RJ0000004", ProductName = "Tomorrow Sounds", Description = "A average upcoming product.", Status = (byte)VoiceWorkStatus.Upcoming, ExpectedDate = new(2025, 1, 1), WishlistCount = 300 }, - new() { VoiceWorkId = 5, CircleId = 2, ProductId = "RJ0000005", ProductName = "Super Comfy ASMR+", Description = "All your favorite sounds, plus more!", Status = (byte)VoiceWorkStatus.NewAndUpcoming, ExpectedDate = new(2025, 1, 11), WishlistCount = 10000 } + new() { VoiceWorkId = 1, CircleId = 1, ProductId = "RJ0000001", ProductName = "Today Sounds", Description = "An average product.", Status = (byte)VoiceWorkStatus.Available, SalesDate = new(2025, 1, 1), Downloads = 500, WishlistCount = 750, StarRating = 35, IsValid = true }, + new() { VoiceWorkId = 2, CircleId = 2, ProductId = "RJ0000002", ProductName = "Super Comfy ASMR", Description = "An amazing product!", Status = (byte)VoiceWorkStatus.NewRelease, SalesDate = new(2025, 1, 3), Downloads = 5000, WishlistCount = 12000, StarRating = 50, Favorite = true, IsValid = true }, + new() { VoiceWorkId = 3, CircleId = 3, ProductId = "RJ0000003", ProductName = "Low Effort", Description = "A bad product.", Status = (byte)VoiceWorkStatus.Available, SalesDate = new(2025, 1, 2), Downloads = 50, WishlistCount = 100, StarRating = 20, IsValid = false }, + new() { VoiceWorkId = 4, CircleId = 1, ProductId = "RJ0000004", ProductName = "Tomorrow Sounds", Description = "A average upcoming product.", Status = (byte)VoiceWorkStatus.Upcoming, ExpectedDate = new(2025, 1, 1), WishlistCount = 300, IsValid = true }, + new() { VoiceWorkId = 5, CircleId = 2, ProductId = "RJ0000005", ProductName = "Super Comfy ASMR+", Description = "All your favorite sounds, plus more!", Status = (byte)VoiceWorkStatus.NewAndUpcoming, ExpectedDate = new(2025, 1, 11), WishlistCount = 10000, IsValid = true } + ); + + context.Creators.AddRange( + new() { CreatorId = 1, Name = "Average Creator" }, + new() { CreatorId = 2, Name = "Good Creator" }, + new() { CreatorId = 3, Name = "Bad Creator" } + ); + + context.VoiceWorkCreators.AddRange( + new() { VoiceWorkId = 1, CreatorId = 1 }, + new() { VoiceWorkId = 2, CreatorId = 2 }, + new() { VoiceWorkId = 3, CreatorId = 3 } + ); + + context.Tags.AddRange( + new() { TagId = 1, Name = "ASMR" }, + new() { TagId = 2, Name = "Tsundere" }, + new() { TagId = 3, Name = "Office Lady" } + ); + + context.VoiceWorkTags.AddRange( + new() { VoiceWorkId = 1, TagId = 1 }, + new() { VoiceWorkId = 2, TagId = 2 }, + new() { VoiceWorkId = 3, TagId = 3 } + ); + + context.VoiceWorkSupportedLanguages.AddRange( + new() { VoiceWorkSupportedLanguageId = 1, VoiceWorkId = 1, Language = "JPN" }, + new() { VoiceWorkSupportedLanguageId = 2, VoiceWorkId = 2, Language = "JPN" }, + new() { VoiceWorkSupportedLanguageId = 3, VoiceWorkId = 3, Language = "JPN" } + ); + + context.EnglishVoiceWorks.AddRange( + new() { EnglishVoiceWorkId = 1, VoiceWorkId = 1, ProductName = "Today Sounds", Description = "An average product." }, + new() { EnglishVoiceWorkId = 2, VoiceWorkId = 2, ProductName = "Super Comfy ASMR", Description = "An amazing product!" }, + new() { EnglishVoiceWorkId = 3, VoiceWorkId = 3, ProductName = "Low Effort", Description = "A bad product." }, + new() { EnglishVoiceWorkId = 4, VoiceWorkId = 4, ProductName = "Tomorrow Sounds", Description = "A average upcoming product." }, + new() { EnglishVoiceWorkId = 5, VoiceWorkId = 5, ProductName = "Super Comfy ASMR+", Description = "All your favorite sounds, plus more!" } + ); + + context.VoiceWorkLocalizations.AddRange( + new() { VoiceWorkLocalizationId = 1, VoiceWorkId = 1, Language = "JPN", ProductName = "Today Sounds", Description = "An average product." }, + new() { VoiceWorkLocalizationId = 2, VoiceWorkId = 2, Language = "JPN", ProductName = "Super Comfy ASMR", Description = "An amazing product!" }, + new() { VoiceWorkLocalizationId = 3, VoiceWorkId = 3, Language = "JPN", ProductName = "Low Effort", Description = "A bad product." }, + new() { VoiceWorkLocalizationId = 4, VoiceWorkId = 4, Language = "JPN", ProductName = "Tomorrow Sounds", Description = "A average upcoming product." }, + new() { VoiceWorkLocalizationId = 5, VoiceWorkId = 5, Language = "JPN", ProductName = "Super Comfy ASMR+", Description = "All your favorite sounds, plus more!" } + ); + + context.VoiceWorkSearches.AddRange( + new() { VoiceWorkId = 1, SearchText = "Search 1" }, + new() { VoiceWorkId = 2, SearchText = "Search 2" }, + new() { VoiceWorkId = 3, SearchText = "Search 3" }, + new() { VoiceWorkId = 4, SearchText = "Search 4" }, + new() { VoiceWorkId = 5, SearchText = "Search 5" } ); await context.SaveChangesAsync();