using NSubstitute; using PnvPanel.Application.Common.Interfaces; using PnvPanel.Application.Common.Models; using PnvPanel.Application.Telegram; using PnvPanel.Application.Telegram.Bot; using PnvPanel.Application.Tests.TestSupport; using PnvPanel.Domain.Telegram; using Xunit; namespace PnvPanel.Application.Tests.Telegram.Bot; public class LinkTelegramCommandHandlerTests { private readonly IIdentityService _identityService = Substitute.For(); [Fact] public async Task Handle_WhenTokenValid_LinksUserAndConsumesToken() { using var dbContext = InMemoryDbContextFactory.Create(); var userId = Guid.NewGuid(); var token = TelegramLinkToken.Create(userId, TimeSpan.FromMinutes(5)); dbContext.TelegramLinkTokens.Add(token); await dbContext.SaveChangesAsync(CancellationToken.None); _identityService .LinkTelegramAsync(userId, 123456L, "alice_tg", Arg.Any()) .Returns(Result.Success()); var handler = new LinkTelegramCommandHandler(dbContext, _identityService); var result = await handler.Handle( new LinkTelegramCommand(token.Token, 123456L, "alice_tg"), CancellationToken.None ); Assert.True(result.IsSuccess); Assert.Equal(userId, result.Value); Assert.False(token.IsValid); } [Fact] public async Task Handle_WhenTokenNotFound_ReturnsNotFound() { using var dbContext = InMemoryDbContextFactory.Create(); var handler = new LinkTelegramCommandHandler(dbContext, _identityService); var result = await handler.Handle( new LinkTelegramCommand("missing-token", 123456L, null), CancellationToken.None ); Assert.False(result.IsSuccess); Assert.Equal(TelegramErrors.LinkTokenNotFound, result.Error); } [Fact] public async Task Handle_WhenTokenAlreadyConsumed_ReturnsNotFound() { using var dbContext = InMemoryDbContextFactory.Create(); var userId = Guid.NewGuid(); var token = TelegramLinkToken.Create(userId, TimeSpan.FromMinutes(5)); token.Consume(); dbContext.TelegramLinkTokens.Add(token); await dbContext.SaveChangesAsync(CancellationToken.None); var handler = new LinkTelegramCommandHandler(dbContext, _identityService); var result = await handler.Handle( new LinkTelegramCommand(token.Token, 123456L, null), CancellationToken.None ); Assert.False(result.IsSuccess); Assert.Equal(TelegramErrors.LinkTokenNotFound, result.Error); await _identityService .DidNotReceive() .LinkTelegramAsync( Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any() ); } [Fact] public async Task Handle_WhenTokenExpired_ReturnsNotFound() { using var dbContext = InMemoryDbContextFactory.Create(); var userId = Guid.NewGuid(); var token = TelegramLinkToken.Create(userId, TimeSpan.FromMinutes(-5)); dbContext.TelegramLinkTokens.Add(token); await dbContext.SaveChangesAsync(CancellationToken.None); var handler = new LinkTelegramCommandHandler(dbContext, _identityService); var result = await handler.Handle( new LinkTelegramCommand(token.Token, 123456L, null), CancellationToken.None ); Assert.False(result.IsSuccess); Assert.Equal(TelegramErrors.LinkTokenNotFound, result.Error); } [Fact] public async Task Handle_WhenIdentityServiceFails_ReturnsFailureWithoutConsumingToken() { using var dbContext = InMemoryDbContextFactory.Create(); var userId = Guid.NewGuid(); var token = TelegramLinkToken.Create(userId, TimeSpan.FromMinutes(5)); dbContext.TelegramLinkTokens.Add(token); await dbContext.SaveChangesAsync(CancellationToken.None); var error = Error.Conflict( "Telegram.AlreadyLinked", "Этот Telegram уже привязан к другому аккаунту." ); _identityService .LinkTelegramAsync(userId, 123456L, null, Arg.Any()) .Returns(Result.Failure(error)); var handler = new LinkTelegramCommandHandler(dbContext, _identityService); var result = await handler.Handle( new LinkTelegramCommand(token.Token, 123456L, null), CancellationToken.None ); Assert.False(result.IsSuccess); Assert.Equal(error, result.Error); Assert.True(token.IsValid); } }