using NSubstitute; using PnvPanel.Application.Admin.Inbounds; using PnvPanel.Application.Common.Interfaces; using PnvPanel.Application.Tests.TestSupport; using PnvPanel.Domain.Configs; using PnvPanel.Domain.Inbounds; using Xunit; namespace PnvPanel.Application.Tests.Admin.Inbounds; public class DeleteInboundCommandHandlerTests { private readonly IRealtimeNotifier _notifier = Substitute.For(); private readonly ICurrentUser _currentUser = Substitute.For(); private DeleteInboundCommandHandler CreateHandler(IAppDbContext dbContext) => new(dbContext, _notifier, _currentUser); [Fact] public async Task Handle_WhenInboundNotFound_ReturnsNotFound() { using var dbContext = InMemoryDbContextFactory.Create(); var result = await CreateHandler(dbContext) .Handle(new DeleteInboundCommand(Guid.NewGuid()), CancellationToken.None); Assert.False(result.IsSuccess); Assert.Equal(InboundErrors.NotFound, result.Error); } [Fact] public async Task Handle_WhenInboundStillAvailable_ReturnsStillAvailable() { using var dbContext = InMemoryDbContextFactory.Create(); var inbound = Inbound.FromRemote(Guid.NewGuid(), "1", VpnProtocol.Vless, "remark", 443); dbContext.Inbounds.Add(inbound); await dbContext.SaveChangesAsync(CancellationToken.None); var result = await CreateHandler(dbContext) .Handle(new DeleteInboundCommand(inbound.Id), CancellationToken.None); Assert.False(result.IsSuccess); Assert.Equal(InboundErrors.StillAvailable, result.Error); Assert.NotNull(await dbContext.Inbounds.FindAsync([inbound.Id], CancellationToken.None)); } [Fact] public async Task Handle_WhenUnavailable_RevokesActiveConfigsAndDeletesInbound() { using var dbContext = InMemoryDbContextFactory.Create(); var inbound = Inbound.FromRemote(Guid.NewGuid(), "1", VpnProtocol.Vless, "remark", 443); inbound.MarkUnavailable(); var userId = Guid.NewGuid(); var activeConfig = VpnConfig.Create(userId, inbound.Id, VpnProtocol.Vless, null); var alreadyRevoked = VpnConfig.Create(userId, inbound.Id, VpnProtocol.Vless, null); alreadyRevoked.Revoke(); dbContext.Inbounds.Add(inbound); dbContext.VpnConfigs.AddRange(activeConfig, alreadyRevoked); await dbContext.SaveChangesAsync(CancellationToken.None); var result = await CreateHandler(dbContext) .Handle(new DeleteInboundCommand(inbound.Id), CancellationToken.None); // UnitOfWorkBehavior делает это в реальном пайплайне — здесь хендлер вызывается напрямую. await dbContext.SaveChangesAsync(CancellationToken.None); Assert.True(result.IsSuccess); Assert.Equal(ConfigStatus.Revoked, activeConfig.Status); Assert.Null(await dbContext.Inbounds.FindAsync([inbound.Id], CancellationToken.None)); await _notifier .Received(1) .NotifyConfigStatusChangedAsync( userId, activeConfig.Id, ConfigStatus.Revoked, Arg.Any() ); await _notifier .DidNotReceive() .NotifyConfigStatusChangedAsync( userId, alreadyRevoked.Id, Arg.Any(), Arg.Any() ); } }