add tests

This commit is contained in:
Leonid Pershin
2025-10-17 05:47:18 +03:00
parent f7e3024e7e
commit 03eb0f22a2
41 changed files with 4001 additions and 30 deletions

View File

@@ -0,0 +1,90 @@
using ChatBot.Models;
using ChatBot.Models.Configuration;
using ChatBot.Services;
using ChatBot.Services.Interfaces;
using ChatBot.Services.Telegram.Commands;
using ChatBot.Tests.TestUtilities;
using FluentAssertions;
using Microsoft.Extensions.Logging;
using Moq;
namespace ChatBot.Tests.Telegram.Commands;
public class ClearCommandTests : UnitTestBase
{
private readonly Mock<ChatService> _chatServiceMock;
private readonly Mock<ModelService> _modelServiceMock;
private readonly ClearCommand _clearCommand;
public ClearCommandTests()
{
_chatServiceMock = new Mock<ChatService>(
TestDataBuilder.Mocks.CreateLoggerMock<ChatService>().Object,
TestDataBuilder.Mocks.CreateAIServiceMock().Object,
TestDataBuilder.Mocks.CreateSessionStorageMock().Object,
TestDataBuilder
.Mocks.CreateOptionsMock(TestDataBuilder.Configurations.CreateAISettings())
.Object,
TestDataBuilder.Mocks.CreateCompressionServiceMock().Object
);
_modelServiceMock = new Mock<ModelService>(
TestDataBuilder.Mocks.CreateLoggerMock<ModelService>().Object,
TestDataBuilder
.Mocks.CreateOptionsMock(TestDataBuilder.Configurations.CreateOllamaSettings())
.Object
);
_clearCommand = new ClearCommand(_chatServiceMock.Object, _modelServiceMock.Object);
}
[Fact]
public async Task ExecuteAsync_ShouldClearSession_WhenSessionExists()
{
// Arrange
var chatId = 12345L;
_chatServiceMock.Setup(x => x.ClearHistoryAsync(chatId)).Returns(Task.CompletedTask);
var context = new TelegramCommandContext
{
ChatId = chatId,
Username = "testuser",
MessageText = "/clear",
ChatType = "private",
ChatTitle = "Test Chat",
};
// Act
var result = await _clearCommand.ExecuteAsync(context);
// Assert
result.Should().NotBeNull();
result.Should().Contain("очищена");
_chatServiceMock.Verify(x => x.ClearHistoryAsync(chatId), Times.Once);
}
[Fact]
public async Task ExecuteAsync_ShouldReturnMessage_WhenSessionDoesNotExist()
{
// Arrange
var chatId = 12345L;
_chatServiceMock
.Setup(x => x.ClearHistoryAsync(chatId))
.ThrowsAsync(new InvalidOperationException("Session not found"));
var context = new TelegramCommandContext
{
ChatId = chatId,
Username = "testuser",
MessageText = "/clear",
ChatType = "private",
ChatTitle = "Test Chat",
};
// Act
var result = await _clearCommand.ExecuteAsync(context);
// Assert
result.Should().NotBeNull();
result.Should().Contain("очищена");
_chatServiceMock.Verify(x => x.ClearHistoryAsync(chatId), Times.Once);
}
}

View File

@@ -0,0 +1,103 @@
using ChatBot.Services;
using ChatBot.Services.Telegram.Commands;
using ChatBot.Services.Telegram.Interfaces;
using ChatBot.Tests.TestUtilities;
using FluentAssertions;
using Microsoft.Extensions.Logging;
using Moq;
namespace ChatBot.Tests.Telegram.Commands;
public class CommandRegistryTests : UnitTestBase
{
private readonly Mock<ILogger<CommandRegistry>> _loggerMock;
private readonly CommandRegistry _commandRegistry;
public CommandRegistryTests()
{
_loggerMock = TestDataBuilder.Mocks.CreateLoggerMock<CommandRegistry>();
_commandRegistry = new CommandRegistry(_loggerMock.Object, new List<ITelegramCommand>());
}
[Fact]
public void Constructor_ShouldRegisterCommands()
{
// Arrange
var command = new Mock<ITelegramCommand>();
command.Setup(x => x.CommandName).Returns("test");
// Act
var registry = new CommandRegistry(_loggerMock.Object, new[] { command.Object });
// Assert
var registeredCommand = registry.GetCommand("test");
registeredCommand.Should().Be(command.Object);
}
[Fact]
public void GetCommand_ShouldReturnCommand_WhenCommandExists()
{
// Arrange
var command = new Mock<ITelegramCommand>();
command.Setup(x => x.CommandName).Returns("test");
var registry = new CommandRegistry(_loggerMock.Object, new[] { command.Object });
// Act
var result = registry.GetCommand("test");
// Assert
result.Should().Be(command.Object);
}
[Fact]
public void GetCommand_ShouldReturnNull_WhenCommandDoesNotExist()
{
// Act
var result = _commandRegistry.GetCommand("nonexistent");
// Assert
result.Should().BeNull();
}
[Fact]
public void GetAllCommands_ShouldReturnAllRegisteredCommands()
{
// Arrange
var command1 = new Mock<ITelegramCommand>();
command1.Setup(x => x.CommandName).Returns("test1");
var command2 = new Mock<ITelegramCommand>();
command2.Setup(x => x.CommandName).Returns("test2");
var registry = new CommandRegistry(
_loggerMock.Object,
new[] { command1.Object, command2.Object }
);
// Act
var result = registry.GetAllCommands();
// Assert
result.Should().HaveCount(2);
result.Should().Contain(command1.Object);
result.Should().Contain(command2.Object);
}
[Fact]
public void Constructor_ShouldNotOverwriteExistingCommand_WhenCommandWithSameNameExists()
{
// Arrange
var command1 = new Mock<ITelegramCommand>();
command1.Setup(x => x.CommandName).Returns("test");
var command2 = new Mock<ITelegramCommand>();
command2.Setup(x => x.CommandName).Returns("test");
// Act
var registry = new CommandRegistry(
_loggerMock.Object,
new[] { command1.Object, command2.Object }
);
// Assert
var result = registry.GetCommand("test");
result.Should().Be(command1.Object); // First command should be kept
}
}

View File

@@ -0,0 +1,108 @@
using ChatBot.Models.Configuration;
using ChatBot.Services;
using ChatBot.Services.Telegram.Commands;
using ChatBot.Services.Telegram.Interfaces;
using ChatBot.Tests.TestUtilities;
using FluentAssertions;
using Microsoft.Extensions.Logging;
using Moq;
using Telegram.Bot.Types;
namespace ChatBot.Tests.Telegram.Commands;
public class HelpCommandTests : UnitTestBase
{
private readonly HelpCommand _helpCommand;
public HelpCommandTests()
{
var chatServiceMock = new Mock<ChatService>(
TestDataBuilder.Mocks.CreateLoggerMock<ChatService>().Object,
TestDataBuilder.Mocks.CreateAIServiceMock().Object,
TestDataBuilder.Mocks.CreateSessionStorageMock().Object,
TestDataBuilder
.Mocks.CreateOptionsMock(TestDataBuilder.Configurations.CreateAISettings())
.Object,
TestDataBuilder.Mocks.CreateCompressionServiceMock().Object
);
var modelServiceMock = new Mock<ModelService>(
TestDataBuilder.Mocks.CreateLoggerMock<ModelService>().Object,
TestDataBuilder
.Mocks.CreateOptionsMock(TestDataBuilder.Configurations.CreateOllamaSettings())
.Object
);
var commandRegistryMock = new Mock<CommandRegistry>(
Mock.Of<ILogger<CommandRegistry>>(),
new List<ITelegramCommand>()
);
commandRegistryMock
.Setup(x => x.GetCommandsWithDescriptions())
.Returns(
new List<(string, string)>
{
("/start", "Начать работу с ботом"),
("/help", "Показать справку по всем командам"),
("/clear", "Очистить историю чата"),
("/settings", "Показать настройки чата"),
("/status", "Показать статус системы и API"),
}
);
var serviceProviderMock = new Mock<IServiceProvider>();
serviceProviderMock
.Setup(x => x.GetService(typeof(CommandRegistry)))
.Returns(commandRegistryMock.Object);
_helpCommand = new HelpCommand(
chatServiceMock.Object,
modelServiceMock.Object,
serviceProviderMock.Object
);
}
[Fact]
public async Task ExecuteAsync_ShouldReturnHelpMessage()
{
// Arrange
var context = new TelegramCommandContext
{
ChatId = 12345,
Username = "testuser",
MessageText = "/help",
ChatType = "private",
ChatTitle = "Test Chat",
};
// Act
var result = await _helpCommand.ExecuteAsync(context);
// Assert
result.Should().NotBeNull();
result.Should().Contain("Доступные");
result.Should().Contain("/start");
result.Should().Contain("/help");
}
[Fact]
public async Task ExecuteAsync_ShouldReturnCommandList()
{
// Arrange
var context = new TelegramCommandContext
{
ChatId = 12345,
Username = "testuser",
MessageText = "/help",
ChatType = "private",
ChatTitle = "Test Chat",
};
// Act
var result = await _helpCommand.ExecuteAsync(context);
// Assert
result.Should().NotBeNull();
result.Should().Contain("/status");
result.Should().Contain("/clear");
result.Should().Contain("/settings");
}
}

View File

@@ -0,0 +1,94 @@
using ChatBot.Models.Configuration;
using ChatBot.Services;
using ChatBot.Services.Interfaces;
using ChatBot.Services.Telegram.Commands;
using ChatBot.Tests.TestUtilities;
using FluentAssertions;
using Microsoft.Extensions.Logging;
using Moq;
using Telegram.Bot.Types;
namespace ChatBot.Tests.Telegram.Commands;
public class SettingsCommandTests : UnitTestBase
{
private readonly Mock<ISessionStorage> _sessionStorageMock;
private readonly SettingsCommand _settingsCommand;
public SettingsCommandTests()
{
_sessionStorageMock = TestDataBuilder.Mocks.CreateSessionStorageMock();
var chatServiceMock = new Mock<ChatService>(
TestDataBuilder.Mocks.CreateLoggerMock<ChatService>().Object,
TestDataBuilder.Mocks.CreateAIServiceMock().Object,
_sessionStorageMock.Object,
TestDataBuilder
.Mocks.CreateOptionsMock(TestDataBuilder.Configurations.CreateAISettings())
.Object,
TestDataBuilder.Mocks.CreateCompressionServiceMock().Object
);
var modelServiceMock = new Mock<ModelService>(
TestDataBuilder.Mocks.CreateLoggerMock<ModelService>().Object,
TestDataBuilder
.Mocks.CreateOptionsMock(TestDataBuilder.Configurations.CreateOllamaSettings())
.Object
);
var aiSettingsMock = TestDataBuilder.Mocks.CreateOptionsMock(new AISettings());
_settingsCommand = new SettingsCommand(
chatServiceMock.Object,
modelServiceMock.Object,
aiSettingsMock.Object
);
}
[Fact]
public async Task ExecuteAsync_ShouldReturnSettings_WhenSessionExists()
{
// Arrange
var chatId = 12345L;
var session = TestDataBuilder.ChatSessions.CreateBasicSession(chatId);
_sessionStorageMock.Setup(x => x.Get(chatId)).Returns(session);
var context = new TelegramCommandContext
{
ChatId = chatId,
Username = "testuser",
MessageText = "/settings",
ChatType = "private",
ChatTitle = "Test Chat",
};
// Act
var result = await _settingsCommand.ExecuteAsync(context);
// Assert
result.Should().NotBeNull();
result.Should().Contain("Настройки");
result.Should().Contain("Модель");
}
[Fact]
public async Task ExecuteAsync_ShouldReturnDefaultSettings_WhenSessionDoesNotExist()
{
// Arrange
var chatId = 12345L;
_sessionStorageMock.Setup(x => x.Get(chatId)).Returns((ChatBot.Models.ChatSession?)null);
var context = new TelegramCommandContext
{
ChatId = chatId,
Username = "testuser",
MessageText = "/settings",
ChatType = "private",
ChatTitle = "Test Chat",
};
// Act
var result = await _settingsCommand.ExecuteAsync(context);
// Assert
result.Should().NotBeNull();
result.Should().Contain("Сессия");
result.Should().Contain("найдена");
}
}

View File

@@ -0,0 +1,78 @@
using ChatBot.Models.Configuration;
using ChatBot.Services;
using ChatBot.Services.Telegram.Commands;
using ChatBot.Tests.TestUtilities;
using FluentAssertions;
using Microsoft.Extensions.Logging;
using Moq;
using Telegram.Bot.Types;
namespace ChatBot.Tests.Telegram.Commands;
public class StartCommandTests : UnitTestBase
{
private readonly StartCommand _startCommand;
public StartCommandTests()
{
var chatServiceMock = new Mock<ChatService>(
TestDataBuilder.Mocks.CreateLoggerMock<ChatService>().Object,
TestDataBuilder.Mocks.CreateAIServiceMock().Object,
TestDataBuilder.Mocks.CreateSessionStorageMock().Object,
TestDataBuilder
.Mocks.CreateOptionsMock(TestDataBuilder.Configurations.CreateAISettings())
.Object,
TestDataBuilder.Mocks.CreateCompressionServiceMock().Object
);
var modelServiceMock = new Mock<ModelService>(
TestDataBuilder.Mocks.CreateLoggerMock<ModelService>().Object,
TestDataBuilder
.Mocks.CreateOptionsMock(TestDataBuilder.Configurations.CreateOllamaSettings())
.Object
);
_startCommand = new StartCommand(chatServiceMock.Object, modelServiceMock.Object);
}
[Fact]
public async Task ExecuteAsync_ShouldReturnWelcomeMessage()
{
// Arrange
var context = new TelegramCommandContext
{
ChatId = 12345,
Username = "testuser",
MessageText = "/start",
ChatType = "private",
ChatTitle = "Test Chat",
};
// Act
var result = await _startCommand.ExecuteAsync(context);
// Assert
result.Should().NotBeNull();
result.Should().Contain("Привет");
result.Should().Contain("Никита");
}
[Fact]
public async Task ExecuteAsync_ShouldReturnHelpInformation()
{
// Arrange
var context = new TelegramCommandContext
{
ChatId = 12345,
Username = "testuser",
MessageText = "/start",
ChatType = "private",
ChatTitle = "Test Chat",
};
// Act
var result = await _startCommand.ExecuteAsync(context);
// Assert
result.Should().NotBeNull();
result.Should().Contain("вопросы");
}
}

View File

@@ -0,0 +1,160 @@
using System.Linq;
using ChatBot.Models.Configuration;
using ChatBot.Services;
using ChatBot.Services.Interfaces;
using ChatBot.Services.Telegram.Commands;
using ChatBot.Tests.TestUtilities;
using FluentAssertions;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Moq;
using OllamaSharp.Models.Chat;
namespace ChatBot.Tests.Telegram.Commands;
public class StatusCommandTests : UnitTestBase
{
private readonly Mock<IOptions<OllamaSettings>> _ollamaOptionsMock;
private readonly Mock<IOllamaClient> _ollamaClientMock;
private readonly StatusCommand _statusCommand;
public StatusCommandTests()
{
var ollamaSettings = TestDataBuilder.Configurations.CreateOllamaSettings();
_ollamaOptionsMock = TestDataBuilder.Mocks.CreateOptionsMock(ollamaSettings);
_ollamaClientMock = TestDataBuilder.Mocks.CreateOllamaClientMock();
var chatServiceMock = new Mock<ChatService>(
TestDataBuilder.Mocks.CreateLoggerMock<ChatService>().Object,
TestDataBuilder.Mocks.CreateAIServiceMock().Object,
TestDataBuilder.Mocks.CreateSessionStorageMock().Object,
TestDataBuilder
.Mocks.CreateOptionsMock(TestDataBuilder.Configurations.CreateAISettings())
.Object,
TestDataBuilder.Mocks.CreateCompressionServiceMock().Object
);
var modelServiceMock = new Mock<ModelService>(
TestDataBuilder.Mocks.CreateLoggerMock<ModelService>().Object,
_ollamaOptionsMock.Object
);
var aiSettingsMock = TestDataBuilder.Mocks.CreateOptionsMock(new AISettings());
_statusCommand = new StatusCommand(
chatServiceMock.Object,
modelServiceMock.Object,
aiSettingsMock.Object,
_ollamaClientMock.Object
);
}
[Fact]
public async Task ExecuteAsync_ShouldReturnStatusMessage_WhenBothServicesAreHealthy()
{
// Arrange
var context = new TelegramCommandContext
{
ChatId = 12345,
Username = "testuser",
MessageText = "/status",
ChatType = "private",
ChatTitle = "Test Chat",
};
// Mock Ollama health check
_ollamaClientMock
.Setup(x => x.ChatAsync(It.IsAny<OllamaSharp.Models.Chat.ChatRequest>()))
.Returns(
TestDataBuilder.Mocks.CreateAsyncEnumerable(
new List<OllamaSharp.Models.Chat.ChatResponseStream>
{
new OllamaSharp.Models.Chat.ChatResponseStream
{
Message = new OllamaSharp.Models.Chat.Message(
ChatRole.Assistant,
"Test response"
),
},
}
)
);
// Act
var result = await _statusCommand.ExecuteAsync(context);
// Assert
result.Should().NotBeNull();
result.Should().Contain("Статус");
result.Should().Contain("API");
result.Should().Contain("системы");
}
[Fact]
public async Task ExecuteAsync_ShouldReturnErrorStatus_WhenOllamaIsUnavailable()
{
// Arrange
var context = new TelegramCommandContext
{
ChatId = 12345,
Username = "testuser",
MessageText = "/status",
ChatType = "private",
ChatTitle = "Test Chat",
};
// Mock Ollama failure
_ollamaClientMock
.Setup(x => x.ChatAsync(It.IsAny<OllamaSharp.Models.Chat.ChatRequest>()))
.Throws(new Exception("Ollama unavailable"));
// Act
var result = await _statusCommand.ExecuteAsync(context);
// Assert
result.Should().NotBeNull();
result.Should().Contain("Статус");
result.Should().Contain("API");
result.Should().Contain("Ошибка");
}
[Fact]
public async Task ExecuteAsync_ShouldReturnErrorStatus_WhenTelegramIsUnavailable()
{
// Arrange
var context = new TelegramCommandContext
{
ChatId = 12345,
Username = "testuser",
MessageText = "/status",
ChatType = "private",
ChatTitle = "Test Chat",
};
// Mock Ollama health check
_ollamaClientMock
.Setup(x => x.ChatAsync(It.IsAny<OllamaSharp.Models.Chat.ChatRequest>()))
.Returns(
TestDataBuilder.Mocks.CreateAsyncEnumerable(
new List<OllamaSharp.Models.Chat.ChatResponseStream>
{
new OllamaSharp.Models.Chat.ChatResponseStream
{
Message = new OllamaSharp.Models.Chat.Message(
ChatRole.Assistant,
"Test response"
),
},
}
)
);
// Act
var result = await _statusCommand.ExecuteAsync(context);
// Assert
result.Should().NotBeNull();
result.Should().Contain("Статус");
result.Should().Contain("системы");
result.Should().Contain("Доступен");
}
}