using NSubstitute; using PnvPanel.Application.Admin.Maintenance; using PnvPanel.Application.Common.Interfaces; using PnvPanel.Application.Tests.TestSupport; using PnvPanel.Domain.Support; using Xunit; namespace PnvPanel.Application.Tests.Admin.Maintenance; public class DeleteClosedTicketsCommandHandlerTests { private readonly IFileStorage _fileStorage = Substitute.For(); [Fact] public async Task Handle_DeletesClosedTicketsWithCommentsAndAttachments_KeepsOthers() { using var dbContext = InMemoryDbContextFactory.Create(); var userId = Guid.NewGuid(); var closedTicket = SupportTicket.CreateBugReport(userId); closedTicket.Close(); var openTicket = SupportTicket.CreateBugReport(userId); dbContext.SupportTickets.AddRange(closedTicket, openTicket); var closedComment = TicketComment.Create( closedTicket.Id, userId, "закрытый тикет, есть скриншот" ); var openComment = TicketComment.Create(openTicket.Id, userId, "открытый тикет"); dbContext.TicketComments.AddRange(closedComment, openComment); var attachment = TicketAttachment.Create( closedComment.Id, "screenshot.png", "stored-name", "image/png", 1024 ); dbContext.TicketAttachments.Add(attachment); await dbContext.SaveChangesAsync(CancellationToken.None); var currentUser = FakeCurrentUser.Authenticated(Guid.NewGuid(), "admin"); var handler = new DeleteClosedTicketsCommandHandler(dbContext, _fileStorage, currentUser); var result = await handler.Handle(new DeleteClosedTicketsCommand(), CancellationToken.None); // Хендлер не коммитит сам (в проде это делает UnitOfWorkBehavior после диспетчера) — коммитим явно. await dbContext.SaveChangesAsync(CancellationToken.None); Assert.True(result.IsSuccess); Assert.Equal(1, result.Value); Assert.False(dbContext.SupportTickets.Any(t => t.Id == closedTicket.Id)); Assert.False(dbContext.TicketComments.Any(c => c.Id == closedComment.Id)); Assert.False(dbContext.TicketAttachments.Any(a => a.Id == attachment.Id)); Assert.True(dbContext.SupportTickets.Any(t => t.Id == openTicket.Id)); Assert.True(dbContext.TicketComments.Any(c => c.Id == openComment.Id)); await _fileStorage.Received(1).DeleteAsync("stored-name", Arg.Any()); } [Fact] public async Task Handle_WhenNoClosedTickets_ReturnsZeroAndDeletesNothing() { using var dbContext = InMemoryDbContextFactory.Create(); var userId = Guid.NewGuid(); var openTicket = SupportTicket.CreateBugReport(userId); dbContext.SupportTickets.Add(openTicket); await dbContext.SaveChangesAsync(CancellationToken.None); var currentUser = FakeCurrentUser.Authenticated(Guid.NewGuid(), "admin"); var handler = new DeleteClosedTicketsCommandHandler(dbContext, _fileStorage, currentUser); var result = await handler.Handle(new DeleteClosedTicketsCommand(), CancellationToken.None); Assert.True(result.IsSuccess); Assert.Equal(0, result.Value); Assert.True(dbContext.SupportTickets.Any(t => t.Id == openTicket.Id)); await _fileStorage .DidNotReceive() .DeleteAsync(Arg.Any(), Arg.Any()); } }