using NSubstitute; using PnvPanel.Application.Common.Interfaces; using PnvPanel.Application.Common.Models; using PnvPanel.Application.Support; using PnvPanel.Application.Support.AddComment; using PnvPanel.Application.Tests.TestSupport; using PnvPanel.Domain.Support; using Xunit; namespace PnvPanel.Application.Tests.Support; public class AddTicketCommentCommandHandlerTests { private readonly IIdentityService _identityService = Substitute.For(); private readonly IFileStorage _fileStorage = Substitute.For(); private readonly IRealtimeNotifier _notifier = Substitute.For(); private static CurrentUserProfile Profile(Guid userId, string role) => new( userId, "user", Guid.NewGuid(), role, IsActivated: true, IsBlocked: false, MaxConfigs: 3, MaxIpLimit: RoleQuota.Unlimited, SubscriptionToken: "token", BillingEnabled: false, BillingPaidUntil: null, BillingSuspended: false ); [Fact] public async Task Handle_WhenOwnerComments_AddsCommentAndDoesNotNotifySelf() { using var dbContext = InMemoryDbContextFactory.Create(); var userId = Guid.NewGuid(); var ticket = SupportTicket.CreateBugReport(userId); dbContext.SupportTickets.Add(ticket); await dbContext.SaveChangesAsync(CancellationToken.None); _identityService .GetProfileAsync(userId, Arg.Any()) .Returns(Profile(userId, "user")); var currentUser = FakeCurrentUser.Authenticated(userId, "alice"); var handler = new AddTicketCommentCommandHandler( dbContext, _identityService, _fileStorage, _notifier, currentUser ); var result = await handler.Handle( new AddTicketCommentCommand(ticket.Id, "апдейт", []), CancellationToken.None ); Assert.True(result.IsSuccess); Assert.Equal("апдейт", result.Value.Body); await _notifier .DidNotReceive() .NotifyTicketUpdatedAsync( Arg.Any(), Arg.Any(), Arg.Any() ); } [Fact] public async Task Handle_WhenAdminComments_NotifiesOwner() { using var dbContext = InMemoryDbContextFactory.Create(); var ownerId = Guid.NewGuid(); var adminId = Guid.NewGuid(); var ticket = SupportTicket.CreateBugReport(ownerId); dbContext.SupportTickets.Add(ticket); await dbContext.SaveChangesAsync(CancellationToken.None); _identityService .GetProfileAsync(adminId, Arg.Any()) .Returns(Profile(adminId, "admin")); var currentUser = FakeCurrentUser.Authenticated(adminId, "admin"); var handler = new AddTicketCommentCommandHandler( dbContext, _identityService, _fileStorage, _notifier, currentUser ); var result = await handler.Handle( new AddTicketCommentCommand(ticket.Id, "ответ админа", []), CancellationToken.None ); Assert.True(result.IsSuccess); await _notifier .Received(1) .NotifyTicketUpdatedAsync(ticket.Id, ownerId, Arg.Any()); } [Fact] public async Task Handle_WhenNotOwnerAndNotAdmin_ReturnsNotFound() { using var dbContext = InMemoryDbContextFactory.Create(); var ownerId = Guid.NewGuid(); var strangerId = Guid.NewGuid(); var ticket = SupportTicket.CreateBugReport(ownerId); dbContext.SupportTickets.Add(ticket); await dbContext.SaveChangesAsync(CancellationToken.None); _identityService .GetProfileAsync(strangerId, Arg.Any()) .Returns(Profile(strangerId, "user")); var currentUser = FakeCurrentUser.Authenticated(strangerId); var handler = new AddTicketCommentCommandHandler( dbContext, _identityService, _fileStorage, _notifier, currentUser ); var result = await handler.Handle( new AddTicketCommentCommand(ticket.Id, "текст", []), CancellationToken.None ); Assert.False(result.IsSuccess); Assert.Equal(SupportErrors.NotFound, result.Error); } [Fact] public async Task Handle_WhenTicketClosed_ReturnsTicketClosedError() { using var dbContext = InMemoryDbContextFactory.Create(); var userId = Guid.NewGuid(); var ticket = SupportTicket.CreateBugReport(userId); ticket.Close(); dbContext.SupportTickets.Add(ticket); await dbContext.SaveChangesAsync(CancellationToken.None); _identityService .GetProfileAsync(userId, Arg.Any()) .Returns(Profile(userId, "user")); var currentUser = FakeCurrentUser.Authenticated(userId); var handler = new AddTicketCommentCommandHandler( dbContext, _identityService, _fileStorage, _notifier, currentUser ); var result = await handler.Handle( new AddTicketCommentCommand(ticket.Id, "текст", []), CancellationToken.None ); Assert.False(result.IsSuccess); Assert.Equal(SupportErrors.TicketClosed, result.Error); } }