using ChatBot.Services; using ChatBot.Services.Telegram.Commands; using ChatBot.Services.Telegram.Interfaces; using ChatBot.Services.Telegram.Services; using ChatBot.Tests.TestUtilities; using FluentAssertions; using Microsoft.Extensions.Logging; using Moq; using Telegram.Bot; using Telegram.Bot.Types; namespace ChatBot.Tests.Telegram.Commands; public class TelegramCommandProcessorTests : UnitTestBase { private readonly CommandRegistry _commandRegistry; private readonly ChatService _chatService; private readonly Mock> _loggerMock; private readonly BotInfoService _botInfoService; private readonly TelegramCommandProcessor _processor; public TelegramCommandProcessorTests() { _commandRegistry = new CommandRegistry( TestDataBuilder.Mocks.CreateLoggerMock().Object, Enumerable.Empty() ); _chatService = new ChatService( TestDataBuilder.Mocks.CreateLoggerMock().Object, TestDataBuilder.Mocks.CreateAIServiceMock().Object, TestDataBuilder.Mocks.CreateSessionStorageMock().Object, TestDataBuilder .Mocks.CreateOptionsMock(TestDataBuilder.Configurations.CreateAISettings()) .Object, TestDataBuilder.Mocks.CreateCompressionServiceMock().Object ); _loggerMock = TestDataBuilder.Mocks.CreateLoggerMock(); _botInfoService = new BotInfoService( TestDataBuilder.Mocks.CreateTelegramBotClient().Object, TestDataBuilder.Mocks.CreateLoggerMock().Object ); _processor = new TelegramCommandProcessor( _commandRegistry, _chatService, _loggerMock.Object, _botInfoService ); } [Fact] public void Constructor_ShouldInitializeServices() { // Arrange & Act var processor = new TelegramCommandProcessor( _commandRegistry, _chatService, _loggerMock.Object, _botInfoService ); // Assert processor.Should().NotBeNull(); } [Fact] public async Task ProcessMessageAsync_ShouldProcessAsRegularMessage_WhenNoCommandFound() { // Arrange var messageText = "Hello bot"; var chatId = 12345L; var username = "testuser"; var chatType = "private"; var chatTitle = "Test Chat"; // Act var result = await _processor.ProcessMessageAsync( messageText, chatId, username, chatType, chatTitle ); // Assert result.Should().NotBeNull(); result.Should().NotBeEmpty(); } [Fact] public async Task ProcessMessageAsync_ShouldHandleEmptyMessage() { // Arrange var messageText = ""; var chatId = 12345L; var username = "testuser"; var chatType = "private"; var chatTitle = "Test Chat"; // Act var result = await _processor.ProcessMessageAsync( messageText, chatId, username, chatType, chatTitle ); // Assert result.Should().NotBeNull(); } [Fact] public async Task ProcessMessageAsync_ShouldHandleVeryLongMessage() { // Arrange var messageText = new string('A', 10000); var chatId = 12345L; var username = "testuser"; var chatType = "private"; var chatTitle = "Test Chat"; // Act var result = await _processor.ProcessMessageAsync( messageText, chatId, username, chatType, chatTitle ); // Assert result.Should().NotBeNull(); } [Fact] public async Task ProcessMessageAsync_ShouldHandleEmptyUsername() { // Arrange var messageText = "Hello"; var chatId = 12345L; var username = ""; var chatType = "private"; var chatTitle = "Test Chat"; // Act var result = await _processor.ProcessMessageAsync( messageText, chatId, username, chatType, chatTitle ); // Assert result.Should().NotBeNull(); } [Fact] public async Task ProcessMessageAsync_ShouldHandleEmptyChatType() { // Arrange var messageText = "Hello"; var chatId = 12345L; var username = "testuser"; var chatType = ""; var chatTitle = "Test Chat"; // Act var result = await _processor.ProcessMessageAsync( messageText, chatId, username, chatType, chatTitle ); // Assert result.Should().NotBeNull(); } [Fact] public async Task ProcessMessageAsync_ShouldHandleEmptyChatTitle() { // Arrange var messageText = "Hello"; var chatId = 12345L; var username = "testuser"; var chatType = "private"; var chatTitle = ""; // Act var result = await _processor.ProcessMessageAsync( messageText, chatId, username, chatType, chatTitle ); // Assert result.Should().NotBeNull(); } [Fact] public async Task ProcessMessageAsync_ShouldHandleNegativeChatId() { // Arrange var messageText = "Hello"; var chatId = -12345L; var username = "testuser"; var chatType = "private"; var chatTitle = "Test Chat"; // Act var result = await _processor.ProcessMessageAsync( messageText, chatId, username, chatType, chatTitle ); // Assert result.Should().NotBeNull(); } [Fact] public async Task ProcessMessageAsync_ShouldHandleZeroChatId() { // Arrange var messageText = "Hello"; var chatId = 0L; var username = "testuser"; var chatType = "private"; var chatTitle = "Test Chat"; // Act Func act = async () => await _processor.ProcessMessageAsync( messageText, chatId, username, chatType, chatTitle ); // Assert await act.Should().ThrowAsync() .WithMessage("ChatId cannot be 0*"); } [Fact] public async Task ProcessMessageAsync_ShouldHandleVeryLongUsername() { // Arrange var messageText = "Hello"; var chatId = 12345L; var username = new string('A', 1000); var chatType = "private"; var chatTitle = "Test Chat"; // Act var result = await _processor.ProcessMessageAsync( messageText, chatId, username, chatType, chatTitle ); // Assert result.Should().NotBeNull(); } [Fact] public async Task ProcessMessageAsync_ShouldHandleSpecialCharactersInMessage() { // Arrange var messageText = "Hello! @#$%^&*()_+-=[]{}|;':\",./<>?"; var chatId = 12345L; var username = "testuser"; var chatType = "private"; var chatTitle = "Test Chat"; // Act var result = await _processor.ProcessMessageAsync( messageText, chatId, username, chatType, chatTitle ); // Assert result.Should().NotBeNull(); } [Fact] public async Task ProcessMessageAsync_ShouldHandleUnicodeCharacters() { // Arrange var messageText = "Привет! 🌍 Hello!"; var chatId = 12345L; var username = "testuser"; var chatType = "private"; var chatTitle = "Test Chat"; // Act var result = await _processor.ProcessMessageAsync( messageText, chatId, username, chatType, chatTitle ); // Assert result.Should().NotBeNull(); } [Fact] public async Task ProcessMessageAsync_ShouldHandleNullMessage() { // Arrange string messageText = null!; var chatId = 12345L; var username = "testuser"; var chatType = "private"; var chatTitle = "Test Chat"; // Act var result = await _processor.ProcessMessageAsync( messageText, chatId, username, chatType, chatTitle ); // Assert result.Should().NotBeNull(); } [Fact] public async Task ProcessMessageAsync_ShouldHandleNullUsername() { // Arrange var messageText = "Hello"; var chatId = 12345L; string username = null!; var chatType = "private"; var chatTitle = "Test Chat"; // Act var result = await _processor.ProcessMessageAsync( messageText, chatId, username, chatType, chatTitle ); // Assert result.Should().NotBeNull(); } [Fact] public async Task ProcessMessageAsync_ShouldHandleNullChatType() { // Arrange var messageText = "Hello"; var chatId = 12345L; var username = "testuser"; string chatType = null!; var chatTitle = "Test Chat"; // Act var result = await _processor.ProcessMessageAsync( messageText, chatId, username, chatType, chatTitle ); // Assert result.Should().NotBeNull(); } [Fact] public async Task ProcessMessageAsync_ShouldHandleNullChatTitle() { // Arrange var messageText = "Hello"; var chatId = 12345L; var username = "testuser"; var chatType = "private"; string chatTitle = null!; // Act var result = await _processor.ProcessMessageAsync( messageText, chatId, username, chatType, chatTitle ); // Assert result.Should().NotBeNull(); } [Fact] public async Task ProcessMessageAsync_ShouldHandleCancellationToken() { // Arrange var messageText = "Hello bot"; var chatId = 12345L; var username = "testuser"; var chatType = "private"; var chatTitle = "Test Chat"; using var cts = new CancellationTokenSource(); await cts.CancelAsync(); // Act var result = await _processor.ProcessMessageAsync( messageText, chatId, username, chatType, chatTitle, cancellationToken: cts.Token ); // Assert result.Should().NotBeNull(); } [Fact] public async Task ProcessMessageAsync_ShouldHandleReplyInfo() { // Arrange var messageText = "Hello bot"; var chatId = 12345L; var username = "testuser"; var chatType = "private"; var chatTitle = "Test Chat"; var replyInfo = new ReplyInfo { MessageId = 1, UserId = 123, Username = "testuser", }; // Act var result = await _processor.ProcessMessageAsync( messageText, chatId, username, chatType, chatTitle, replyInfo ); // Assert result.Should().NotBeNull(); } [Fact] public async Task ProcessMessageAsync_ShouldHandleNullReplyInfo() { // Arrange var messageText = "Hello bot"; var chatId = 12345L; var username = "testuser"; var chatType = "private"; var chatTitle = "Test Chat"; ReplyInfo? replyInfo = null; // Act var result = await _processor.ProcessMessageAsync( messageText, chatId, username, chatType, chatTitle, replyInfo ); // Assert result.Should().NotBeNull(); } [Fact] public async Task ProcessMessageAsync_WithReplyToBot_ShouldProcessMessage() { // Arrange var botUser = new User { Id = 999, Username = "testbot", IsBot = true, }; var botInfoServiceMock = new Mock( TestDataBuilder.Mocks.CreateTelegramBotClient().Object, TestDataBuilder.Mocks.CreateLoggerMock().Object ); botInfoServiceMock.Setup(x => x.GetBotInfoAsync(It.IsAny())) .ReturnsAsync(botUser); var processor = new TelegramCommandProcessor( _commandRegistry, _chatService, _loggerMock.Object, botInfoServiceMock.Object ); var replyInfo = new ReplyInfo { MessageId = 1, UserId = 999, Username = "testbot", }; // Act var result = await processor.ProcessMessageAsync( "Test reply", 12345L, "user", "private", "Test Chat", replyInfo ); // Assert result.Should().NotBeNullOrEmpty(); } [Fact] public async Task ProcessMessageAsync_WithReplyToOtherUser_ShouldReturnEmpty() { // Arrange var botUser = new User { Id = 999, Username = "testbot", IsBot = true, }; var botInfoServiceMock = new Mock( TestDataBuilder.Mocks.CreateTelegramBotClient().Object, TestDataBuilder.Mocks.CreateLoggerMock().Object ); botInfoServiceMock.Setup(x => x.GetBotInfoAsync(It.IsAny())) .ReturnsAsync(botUser); var processor = new TelegramCommandProcessor( _commandRegistry, _chatService, _loggerMock.Object, botInfoServiceMock.Object ); var replyInfo = new ReplyInfo { MessageId = 1, UserId = 123, Username = "otheruser", }; // Act var result = await processor.ProcessMessageAsync( "Test reply", 12345L, "user", "private", "Test Chat", replyInfo ); // Assert result.Should().BeEmpty(); } [Fact] public async Task ProcessMessageAsync_WithBotMention_ShouldProcessMessage() { // Arrange var botUser = new User { Id = 999, Username = "testbot", IsBot = true, }; var botInfoServiceMock = new Mock( TestDataBuilder.Mocks.CreateTelegramBotClient().Object, TestDataBuilder.Mocks.CreateLoggerMock().Object ); botInfoServiceMock.Setup(x => x.GetBotInfoAsync(It.IsAny())) .ReturnsAsync(botUser); var processor = new TelegramCommandProcessor( _commandRegistry, _chatService, _loggerMock.Object, botInfoServiceMock.Object ); // Act var result = await processor.ProcessMessageAsync( "Hello @testbot", 12345L, "user", "private", "Test Chat" ); // Assert result.Should().NotBeNullOrEmpty(); } [Fact] public async Task ProcessMessageAsync_WithOtherUserMention_ShouldReturnEmpty() { // Arrange var botUser = new User { Id = 999, Username = "testbot", IsBot = true, }; var botInfoServiceMock = new Mock( TestDataBuilder.Mocks.CreateTelegramBotClient().Object, TestDataBuilder.Mocks.CreateLoggerMock().Object ); botInfoServiceMock.Setup(x => x.GetBotInfoAsync(It.IsAny())) .ReturnsAsync(botUser); var processor = new TelegramCommandProcessor( _commandRegistry, _chatService, _loggerMock.Object, botInfoServiceMock.Object ); // Act var result = await processor.ProcessMessageAsync( "Hello @otheruser", 12345L, "user", "private", "Test Chat" ); // Assert result.Should().BeEmpty(); } [Fact] public async Task ProcessMessageAsync_WithCommand_ShouldExecuteCommand() { // Arrange var commandMock = new Mock(); commandMock.Setup(x => x.CommandName).Returns("/test"); commandMock .Setup(x => x.CanHandle(It.IsAny())) .Returns((string msg) => msg.StartsWith("/test")); commandMock .Setup(x => x.ExecuteAsync(It.IsAny(), It.IsAny()) ) .ReturnsAsync("Command executed"); var commandRegistry = new CommandRegistry( TestDataBuilder.Mocks.CreateLoggerMock().Object, new[] { commandMock.Object } ); var processor = new TelegramCommandProcessor( commandRegistry, _chatService, _loggerMock.Object, _botInfoService ); // Act var result = await processor.ProcessMessageAsync( "/test argument", 12345L, "user", "private", "Test Chat" ); // Assert result.Should().Be("Command executed"); commandMock.Verify( x => x.ExecuteAsync(It.IsAny(), It.IsAny()), Times.Once ); } [Fact] public async Task ProcessMessageAsync_WithException_ShouldReturnErrorMessage() { // Arrange var chatServiceMock = new Mock( TestDataBuilder.Mocks.CreateLoggerMock().Object, TestDataBuilder.Mocks.CreateAIServiceMock().Object, TestDataBuilder.Mocks.CreateSessionStorageMock().Object, TestDataBuilder .Mocks.CreateOptionsMock(TestDataBuilder.Configurations.CreateAISettings()) .Object, TestDataBuilder.Mocks.CreateCompressionServiceMock().Object ); chatServiceMock .Setup(x => x.ProcessMessageAsync( It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny() ) ) .ThrowsAsync(new Exception("Test error")); var processor = new TelegramCommandProcessor( _commandRegistry, chatServiceMock.Object, _loggerMock.Object, _botInfoService ); // Act var result = await processor.ProcessMessageAsync( "Test message", 12345L, "user", "private", "Test Chat" ); // Assert result.Should().Contain("Произошла непредвиденная ошибка"); } [Fact] public async Task ProcessMessageAsync_WithGroupChat_ShouldProcessCorrectly() { // Arrange var messageText = "Hello"; var chatId = -100123456789L; // Group chat ID var username = "testuser"; var chatType = "group"; var chatTitle = "Test Group"; // Act var result = await _processor.ProcessMessageAsync( messageText, chatId, username, chatType, chatTitle ); // Assert result.Should().NotBeNull(); } [Fact] public async Task ProcessMessageAsync_WithSupergroupChat_ShouldProcessCorrectly() { // Arrange var messageText = "Hello"; var chatId = -100123456789L; var username = "testuser"; var chatType = "supergroup"; var chatTitle = "Test Supergroup"; // Act var result = await _processor.ProcessMessageAsync( messageText, chatId, username, chatType, chatTitle ); // Assert result.Should().NotBeNull(); } [Fact] public async Task ProcessMessageAsync_WithMultipleMentions_IncludingBot_ShouldProcessMessage() { // Arrange var botUser = new User { Id = 999, Username = "testbot", IsBot = true, }; var botInfoServiceMock = new Mock( TestDataBuilder.Mocks.CreateTelegramBotClient().Object, TestDataBuilder.Mocks.CreateLoggerMock().Object ); botInfoServiceMock.Setup(x => x.GetBotInfoAsync(It.IsAny())) .ReturnsAsync(botUser); var processor = new TelegramCommandProcessor( _commandRegistry, _chatService, _loggerMock.Object, botInfoServiceMock.Object ); // Act var result = await processor.ProcessMessageAsync( "Hello @testbot and @otheruser", 12345L, "user", "group", "Test Group" ); // Assert result.Should().NotBeNullOrEmpty(); } }