From 1996fec14f3462b32f55fb8a7573a91b1b076f2d Mon Sep 17 00:00:00 2001 From: Leonid Pershin Date: Tue, 21 Oct 2025 12:07:56 +0300 Subject: [PATCH] Add promt fix tests --- .windsurf/rules/basesettings.md | 12 ++ .../ChatServiceIntegrationTests.cs | 18 +-- ChatBot.Tests/Services/ChatServiceTests.cs | 82 +++++------ .../Services/DatabaseSessionStorageTests.cs | 80 +++++------ .../Services/InMemorySessionStorageTests.cs | 128 ++++++++++-------- .../Interfaces/ISessionStorageTests.cs | 106 +++++++-------- .../Telegram/Commands/SettingsCommandTests.cs | 6 +- .../Telegram/Commands/StatusCommandTests.cs | 2 +- .../Commands/TelegramCommandProcessorTests.cs | 7 +- .../TestUtilities/TestDataBuilder.cs | 38 +++--- ChatBot/Models/ChatSession.cs | 97 +++++++------ ChatBot/Services/ChatService.cs | 26 ++-- ChatBot/Services/DatabaseSessionStorage.cs | 37 +++-- ChatBot/Services/InMemorySessionStorage.cs | 20 +-- .../Services/Interfaces/ISessionStorage.cs | 10 +- .../Telegram/Commands/SettingsCommand.cs | 24 ++-- .../Telegram/Commands/StatusCommand.cs | 2 +- .../Commands/TelegramCommandProcessor.cs | 36 ++++- 18 files changed, 398 insertions(+), 333 deletions(-) create mode 100644 .windsurf/rules/basesettings.md diff --git a/.windsurf/rules/basesettings.md b/.windsurf/rules/basesettings.md new file mode 100644 index 0000000..9adada6 --- /dev/null +++ b/.windsurf/rules/basesettings.md @@ -0,0 +1,12 @@ +--- +trigger: always_on +--- + +MCP предоставляет ассистенту доступ к данным SonarQube. Используй инструменты для: +Поиска проблем: search_sonar_issues_in_projects, search_dependency_risks +Проверки статуса: get_project_quality_gate_status, get_system_status, get_system_health +Анализа кода: analyze_code_snippet, get_raw_source +Работы с задачами: change_sonar_issue_status +Получения метрик: get_component_measures, search_metrics +Не гадай — запрашивай данные. Уточняй ключи проектов и issue. Действуй точно, опираясь на информацию из SonarQube. +Текущий проект ChatBot \ No newline at end of file diff --git a/ChatBot.Tests/Integration/ChatServiceIntegrationTests.cs b/ChatBot.Tests/Integration/ChatServiceIntegrationTests.cs index 97d8791..5dcd16e 100644 --- a/ChatBot.Tests/Integration/ChatServiceIntegrationTests.cs +++ b/ChatBot.Tests/Integration/ChatServiceIntegrationTests.cs @@ -54,7 +54,7 @@ public class ChatServiceIntegrationTests : TestBase var expectedResponse = "I'm doing well, thank you!"; var session = TestDataBuilder.ChatSessions.CreateBasicSession(chatId); - _sessionStorageMock.Setup(x => x.GetOrCreate(chatId, "private", "")).Returns(session); + _sessionStorageMock.Setup(x => x.GetOrCreateAsync(chatId, "private", "")).ReturnsAsync(session); _aiServiceMock .Setup(x => @@ -89,8 +89,8 @@ public class ChatServiceIntegrationTests : TestBase var expectedResponse = "Hi there!"; _sessionStorageMock - .Setup(x => x.GetOrCreate(chatId, "private", "")) - .Returns(TestDataBuilder.ChatSessions.CreateBasicSession(chatId)); + .Setup(x => x.GetOrCreateAsync(chatId, "private", "")) + .ReturnsAsync(TestDataBuilder.ChatSessions.CreateBasicSession(chatId)); _aiServiceMock .Setup(x => @@ -106,7 +106,7 @@ public class ChatServiceIntegrationTests : TestBase // Assert result.Should().Be(expectedResponse); - _sessionStorageMock.Verify(x => x.GetOrCreate(chatId, "private", ""), Times.Once); + _sessionStorageMock.Verify(x => x.GetOrCreateAsync(chatId, "private", ""), Times.Once); _sessionStorageMock.Verify( x => x.SaveSessionAsync(It.IsAny()), Times.Exactly(2) @@ -123,7 +123,7 @@ public class ChatServiceIntegrationTests : TestBase var expectedResponse = "I didn't receive a message. Could you please try again?"; var session = TestDataBuilder.ChatSessions.CreateBasicSession(chatId); - _sessionStorageMock.Setup(x => x.GetOrCreate(chatId, "private", "")).Returns(session); + _sessionStorageMock.Setup(x => x.GetOrCreateAsync(chatId, "private", "")).ReturnsAsync(session); _aiServiceMock .Setup(x => @@ -151,7 +151,7 @@ public class ChatServiceIntegrationTests : TestBase var message = "Hello"; var session = TestDataBuilder.ChatSessions.CreateBasicSession(chatId); - _sessionStorageMock.Setup(x => x.GetOrCreate(chatId, "private", "")).Returns(session); + _sessionStorageMock.Setup(x => x.GetOrCreateAsync(chatId, "private", "")).ReturnsAsync(session); _aiServiceMock .Setup(x => @@ -179,7 +179,7 @@ public class ChatServiceIntegrationTests : TestBase var expectedResponse = "Hi there!"; var session = TestDataBuilder.ChatSessions.CreateSessionWithMessages(chatId, 10); // 10 messages - _sessionStorageMock.Setup(x => x.GetOrCreate(chatId, "private", "")).Returns(session); + _sessionStorageMock.Setup(x => x.GetOrCreateAsync(chatId, "private", "")).ReturnsAsync(session); _aiServiceMock .Setup(x => @@ -211,7 +211,7 @@ public class ChatServiceIntegrationTests : TestBase // Arrange var chatId = 12345L; var session = TestDataBuilder.ChatSessions.CreateSessionWithMessages(chatId, 5); - _sessionStorageMock.Setup(x => x.Get(chatId)).Returns(session); + _sessionStorageMock.Setup(x => x.GetAsync(chatId)).ReturnsAsync(session); // Act await _chatService.ClearHistoryAsync(chatId); @@ -226,7 +226,7 @@ public class ChatServiceIntegrationTests : TestBase { // Arrange var chatId = 12345L; - _sessionStorageMock.Setup(x => x.Get(chatId)).Returns((ChatBot.Models.ChatSession?)null); + _sessionStorageMock.Setup(x => x.GetAsync(chatId)).ReturnsAsync((ChatBot.Models.ChatSession?)null); // Act await _chatService.ClearHistoryAsync(chatId); diff --git a/ChatBot.Tests/Services/ChatServiceTests.cs b/ChatBot.Tests/Services/ChatServiceTests.cs index 04be05b..d9dd9e8 100644 --- a/ChatBot.Tests/Services/ChatServiceTests.cs +++ b/ChatBot.Tests/Services/ChatServiceTests.cs @@ -37,7 +37,7 @@ public class ChatServiceTests : UnitTestBase } [Fact] - public void GetOrCreateSession_ShouldCreateNewSession_WhenSessionDoesNotExist() + public async Task GetOrCreateSession_ShouldCreateNewSession_WhenSessionDoesNotExist() { // Arrange var chatId = 12345L; @@ -45,7 +45,7 @@ public class ChatServiceTests : UnitTestBase var chatTitle = "Test Chat"; // Act - var session = _chatService.GetOrCreateSession(chatId, chatType, chatTitle); + var session = await _chatService.GetOrCreateSessionAsync(chatId, chatType, chatTitle); // Assert session.Should().NotBeNull(); @@ -53,22 +53,22 @@ public class ChatServiceTests : UnitTestBase session.ChatType.Should().Be(chatType); session.ChatTitle.Should().Be(chatTitle); - _sessionStorageMock.Verify(x => x.GetOrCreate(chatId, chatType, chatTitle), Times.Once); + _sessionStorageMock.Verify(x => x.GetOrCreateAsync(chatId, chatType, chatTitle), Times.Once); } [Fact] - public void GetOrCreateSession_ShouldSetCompressionService_WhenCompressionIsEnabled() + public async Task GetOrCreateSession_ShouldSetCompressionService_WhenCompressionIsEnabled() { // Arrange var chatId = 12345L; _aiSettings.EnableHistoryCompression = true; // Act - var session = _chatService.GetOrCreateSession(chatId); + var session = await _chatService.GetOrCreateSessionAsync(chatId); // Assert session.Should().NotBeNull(); - _sessionStorageMock.Verify(x => x.GetOrCreate(chatId, "private", ""), Times.Once); + _sessionStorageMock.Verify(x => x.GetOrCreateAsync(chatId, "private", ""), Times.Once); } [Fact] @@ -191,7 +191,7 @@ public class ChatServiceTests : UnitTestBase var newModel = "llama3.2"; var session = TestDataBuilder.ChatSessions.CreateBasicSession(chatId); - _sessionStorageMock.Setup(x => x.Get(chatId)).Returns(session); + _sessionStorageMock.Setup(x => x.GetAsync(chatId)).ReturnsAsync(session); // Act await _chatService.UpdateSessionParametersAsync(chatId, newModel); @@ -208,7 +208,7 @@ public class ChatServiceTests : UnitTestBase var chatId = 12345L; var newModel = "llama3.2"; - _sessionStorageMock.Setup(x => x.Get(chatId)).Returns((ChatBot.Models.ChatSession?)null); + _sessionStorageMock.Setup(x => x.GetAsync(chatId)).ReturnsAsync((ChatBot.Models.ChatSession?)null); // Act await _chatService.UpdateSessionParametersAsync(chatId, newModel); @@ -227,7 +227,7 @@ public class ChatServiceTests : UnitTestBase var chatId = 12345L; var session = TestDataBuilder.ChatSessions.CreateSessionWithMessages(chatId, 5); - _sessionStorageMock.Setup(x => x.Get(chatId)).Returns(session); + _sessionStorageMock.Setup(x => x.GetAsync(chatId)).ReturnsAsync(session); // Act await _chatService.ClearHistoryAsync(chatId); @@ -238,82 +238,82 @@ public class ChatServiceTests : UnitTestBase } [Fact] - public void GetSession_ShouldReturnSession_WhenSessionExists() + public async Task GetSession_ShouldReturnSession_WhenSessionExists() { // Arrange var chatId = 12345L; var session = TestDataBuilder.ChatSessions.CreateBasicSession(chatId); - _sessionStorageMock.Setup(x => x.Get(chatId)).Returns(session); + _sessionStorageMock.Setup(x => x.GetAsync(chatId)).ReturnsAsync(session); // Act - var result = _chatService.GetSession(chatId); + var result = await _chatService.GetSessionAsync(chatId); // Assert result.Should().Be(session); } [Fact] - public void GetSession_ShouldReturnNull_WhenSessionDoesNotExist() + public async Task GetSession_ShouldReturnNull_WhenSessionDoesNotExist() { // Arrange var chatId = 12345L; - _sessionStorageMock.Setup(x => x.Get(chatId)).Returns((ChatBot.Models.ChatSession?)null); + _sessionStorageMock.Setup(x => x.GetAsync(chatId)).ReturnsAsync((ChatBot.Models.ChatSession?)null); // Act - var result = _chatService.GetSession(chatId); + var result = await _chatService.GetSessionAsync(chatId); // Assert result.Should().BeNull(); } [Fact] - public void RemoveSession_ShouldReturnTrue_WhenSessionExists() + public async Task RemoveSession_ShouldReturnTrue_WhenSessionExists() { // Arrange var chatId = 12345L; - _sessionStorageMock.Setup(x => x.Remove(chatId)).Returns(true); + _sessionStorageMock.Setup(x => x.RemoveAsync(chatId)).ReturnsAsync(true); // Act - var result = _chatService.RemoveSession(chatId); + var result = await _chatService.RemoveSessionAsync(chatId); // Assert result.Should().BeTrue(); - _sessionStorageMock.Verify(x => x.Remove(chatId), Times.Once); + _sessionStorageMock.Verify(x => x.RemoveAsync(chatId), Times.Once); } [Fact] - public void GetActiveSessionsCount_ShouldReturnCorrectCount() + public async Task GetActiveSessionsCount_ShouldReturnCorrectCount() { // Arrange var expectedCount = 5; - _sessionStorageMock.Setup(x => x.GetActiveSessionsCount()).Returns(expectedCount); + _sessionStorageMock.Setup(x => x.GetActiveSessionsCountAsync()).ReturnsAsync(expectedCount); // Act - var result = _chatService.GetActiveSessionsCount(); + var result = await _chatService.GetActiveSessionsCountAsync(); // Assert result.Should().Be(expectedCount); } [Fact] - public void CleanupOldSessions_ShouldReturnCleanedCount() + public async Task CleanupOldSessions_ShouldReturnCleanedCount() { // Arrange var hoursOld = 24; var expectedCleaned = 3; - _sessionStorageMock.Setup(x => x.CleanupOldSessions(hoursOld)).Returns(expectedCleaned); + _sessionStorageMock.Setup(x => x.CleanupOldSessionsAsync(hoursOld)).ReturnsAsync(expectedCleaned); // Act - var result = _chatService.CleanupOldSessions(hoursOld); + var result = await _chatService.CleanupOldSessionsAsync(hoursOld); // Assert result.Should().Be(expectedCleaned); - _sessionStorageMock.Verify(x => x.CleanupOldSessions(hoursOld), Times.Once); + _sessionStorageMock.Verify(x => x.CleanupOldSessionsAsync(hoursOld), Times.Once); } [Theory] @@ -395,8 +395,8 @@ public class ChatServiceTests : UnitTestBase var message = "Hello, bot!"; _sessionStorageMock - .Setup(x => x.GetOrCreate(It.IsAny(), It.IsAny(), It.IsAny())) - .Throws(new Exception("Database connection failed")); + .Setup(x => x.GetOrCreateAsync(It.IsAny(), It.IsAny(), It.IsAny())) + .ThrowsAsync(new Exception("Database connection failed")); // Act var result = await _chatService.ProcessMessageAsync(chatId, username, message); @@ -585,9 +585,9 @@ public class ChatServiceTests : UnitTestBase session.MaxHistoryLength = 5; // force small history limit _sessionStorageMock - .Setup(x => x.GetOrCreate(chatId, It.IsAny(), It.IsAny())) - .Returns(session); - _sessionStorageMock.Setup(x => x.Get(chatId)).Returns(session); + .Setup(x => x.GetOrCreateAsync(chatId, It.IsAny(), It.IsAny())) + .ReturnsAsync(session); + _sessionStorageMock.Setup(x => x.GetAsync(chatId)).ReturnsAsync(session); _aiServiceMock .Setup(x => x.GenerateChatCompletionWithCompressionAsync( @@ -620,9 +620,9 @@ public class ChatServiceTests : UnitTestBase session.MaxHistoryLength = 50; // avoid trimming impacting compression assertion _sessionStorageMock - .Setup(x => x.GetOrCreate(chatId, It.IsAny(), It.IsAny())) - .Returns(session); - _sessionStorageMock.Setup(x => x.Get(chatId)).Returns(session); + .Setup(x => x.GetOrCreateAsync(chatId, It.IsAny(), It.IsAny())) + .ReturnsAsync(session); + _sessionStorageMock.Setup(x => x.GetAsync(chatId)).ReturnsAsync(session); _aiServiceMock .Setup(x => x.GenerateChatCompletionWithCompressionAsync( @@ -692,7 +692,7 @@ public class ChatServiceTests : UnitTestBase var newModel = "llama3.2"; var session = TestDataBuilder.ChatSessions.CreateBasicSession(chatId); - _sessionStorageMock.Setup(x => x.Get(chatId)).Returns(session); + _sessionStorageMock.Setup(x => x.GetAsync(chatId)).ReturnsAsync(session); _sessionStorageMock .Setup(x => x.SaveSessionAsync(It.IsAny())) .ThrowsAsync(new Exception("Database save failed")); @@ -709,7 +709,7 @@ public class ChatServiceTests : UnitTestBase var chatId = 12345L; var session = TestDataBuilder.ChatSessions.CreateSessionWithMessages(chatId, 5); - _sessionStorageMock.Setup(x => x.Get(chatId)).Returns(session); + _sessionStorageMock.Setup(x => x.GetAsync(chatId)).ReturnsAsync(session); _sessionStorageMock .Setup(x => x.SaveSessionAsync(It.IsAny())) .ThrowsAsync(new Exception("Database save failed")); @@ -723,18 +723,18 @@ public class ChatServiceTests : UnitTestBase [InlineData(0)] [InlineData(-1)] [InlineData(int.MinValue)] - public void CleanupOldSessions_ShouldHandleInvalidHoursOld(int hoursOld) + public async Task CleanupOldSessions_ShouldHandleInvalidHoursOld(int hoursOld) { // Arrange var expectedCleaned = 0; - _sessionStorageMock.Setup(x => x.CleanupOldSessions(hoursOld)).Returns(expectedCleaned); + _sessionStorageMock.Setup(x => x.CleanupOldSessionsAsync(hoursOld)).ReturnsAsync(expectedCleaned); // Act - var result = _chatService.CleanupOldSessions(hoursOld); + var result = await _chatService.CleanupOldSessionsAsync(hoursOld); // Assert result.Should().Be(expectedCleaned); - _sessionStorageMock.Verify(x => x.CleanupOldSessions(hoursOld), Times.Once); + _sessionStorageMock.Verify(x => x.CleanupOldSessionsAsync(hoursOld), Times.Once); } [Theory] @@ -763,7 +763,7 @@ public class ChatServiceTests : UnitTestBase // Assert result.Should().Be(expectedResponse); - _sessionStorageMock.Verify(x => x.GetOrCreate(chatId, "private", ""), Times.Once); + _sessionStorageMock.Verify(x => x.GetOrCreateAsync(chatId, "private", ""), Times.Once); } [Fact] diff --git a/ChatBot.Tests/Services/DatabaseSessionStorageTests.cs b/ChatBot.Tests/Services/DatabaseSessionStorageTests.cs index 9008e17..46af25d 100644 --- a/ChatBot.Tests/Services/DatabaseSessionStorageTests.cs +++ b/ChatBot.Tests/Services/DatabaseSessionStorageTests.cs @@ -5,6 +5,7 @@ using ChatBot.Services; using ChatBot.Tests.TestUtilities; using FluentAssertions; using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Diagnostics; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Moq; @@ -27,6 +28,7 @@ public class DatabaseSessionStorageTests : TestBase // Add in-memory database services.AddDbContext(options => options.UseInMemoryDatabase("TestDatabase") + .ConfigureWarnings(w => w.Ignore(InMemoryEventId.TransactionIgnoredWarning)) ); // Add mocked repository @@ -52,7 +54,7 @@ public class DatabaseSessionStorageTests : TestBase } [Fact] - public void GetOrCreate_ShouldReturnExistingSession_WhenSessionExists() + public async Task GetOrCreateAsync_ShouldReturnExistingSession_WhenSessionExists() { // Arrange var existingSession = TestDataBuilder.Mocks.CreateChatSessionEntity(); @@ -61,7 +63,7 @@ public class DatabaseSessionStorageTests : TestBase .ReturnsAsync(existingSession); // Act - var result = _sessionStorage.GetOrCreate(12345, "private", "Test Chat"); + var result = await _sessionStorage.GetOrCreateAsync(12345, "private", "Test Chat"); // Assert result.Should().NotBeNull(); @@ -70,14 +72,14 @@ public class DatabaseSessionStorageTests : TestBase } [Fact] - public void Get_ShouldReturnSession_WhenSessionExists() + public async Task GetAsync_ShouldReturnSession_WhenSessionExists() { // Arrange var sessionEntity = TestDataBuilder.Mocks.CreateChatSessionEntity(); _repositoryMock.Setup(x => x.GetByChatIdAsync(12345)).ReturnsAsync(sessionEntity); // Act - var result = _sessionStorage.Get(12345); + var result = await _sessionStorage.GetAsync(12345); // Assert result.Should().NotBeNull(); @@ -86,7 +88,7 @@ public class DatabaseSessionStorageTests : TestBase } [Fact] - public void Get_ShouldReturnNull_WhenSessionDoesNotExist() + public async Task GetAsync_ShouldReturnNull_WhenSessionDoesNotExist() { // Arrange _repositoryMock @@ -94,7 +96,7 @@ public class DatabaseSessionStorageTests : TestBase .ReturnsAsync((ChatSessionEntity?)null); // Act - var result = _sessionStorage.Get(12345); + var result = await _sessionStorage.GetAsync(12345); // Assert result.Should().BeNull(); @@ -120,13 +122,13 @@ public class DatabaseSessionStorageTests : TestBase } [Fact] - public void Remove_ShouldReturnTrue_WhenSessionExists() + public async Task RemoveAsync_ShouldReturnTrue_WhenSessionExists() { // Arrange _repositoryMock.Setup(x => x.DeleteAsync(12345)).ReturnsAsync(true); // Act - var result = _sessionStorage.Remove(12345); + var result = await _sessionStorage.RemoveAsync(12345); // Assert result.Should().BeTrue(); @@ -134,13 +136,13 @@ public class DatabaseSessionStorageTests : TestBase } [Fact] - public void Remove_ShouldReturnFalse_WhenSessionDoesNotExist() + public async Task RemoveAsync_ShouldReturnFalse_WhenSessionDoesNotExist() { // Arrange _repositoryMock.Setup(x => x.DeleteAsync(12345)).ReturnsAsync(false); // Act - var result = _sessionStorage.Remove(12345); + var result = await _sessionStorage.RemoveAsync(12345); // Assert result.Should().BeFalse(); @@ -148,14 +150,14 @@ public class DatabaseSessionStorageTests : TestBase } [Fact] - public void GetActiveSessionsCount_ShouldReturnCorrectCount() + public async Task GetActiveSessionsCountAsync_ShouldReturnCorrectCount() { // Arrange var expectedCount = 5; _repositoryMock.Setup(x => x.GetActiveSessionsCountAsync()).ReturnsAsync(expectedCount); // Act - var result = _sessionStorage.GetActiveSessionsCount(); + var result = await _sessionStorage.GetActiveSessionsCountAsync(); // Assert result.Should().Be(expectedCount); @@ -163,14 +165,14 @@ public class DatabaseSessionStorageTests : TestBase } [Fact] - public void CleanupOldSessions_ShouldReturnCorrectCount() + public async Task CleanupOldSessionsAsync_ShouldReturnCorrectCount() { // Arrange var expectedCount = 3; _repositoryMock.Setup(x => x.CleanupOldSessionsAsync(24)).ReturnsAsync(expectedCount); // Act - var result = _sessionStorage.CleanupOldSessions(24); + var result = await _sessionStorage.CleanupOldSessionsAsync(24); // Assert result.Should().Be(expectedCount); @@ -178,34 +180,32 @@ public class DatabaseSessionStorageTests : TestBase } [Fact] - public void GetOrCreate_ShouldThrowInvalidOperationException_WhenRepositoryThrows() + public async Task GetOrCreateAsync_ShouldThrowInvalidOperationException_WhenRepositoryThrows() { // Arrange _repositoryMock - .Setup(x => x.GetOrCreateAsync(12345, "private", "Test Chat")) + .Setup(x => x.GetOrCreateAsync(It.IsAny(), It.IsAny(), It.IsAny())) .ThrowsAsync(new Exception("Database error")); // Act - var act = () => _sessionStorage.GetOrCreate(12345, "private", "Test Chat"); + Func act = async () => await _sessionStorage.GetOrCreateAsync(12345, "private", "Test Chat"); // Assert - act.Should() - .Throw() - .WithMessage("Failed to get or create session for chat 12345") - .WithInnerException() - .WithMessage("Database error"); + await act.Should() + .ThrowAsync() + .WithMessage("Failed to get or create session for chat 12345"); } [Fact] - public void Get_ShouldReturnNull_WhenRepositoryThrows() + public async Task GetAsync_ShouldReturnNull_WhenRepositoryThrows() { // Arrange _repositoryMock - .Setup(x => x.GetByChatIdAsync(12345)) + .Setup(x => x.GetByChatIdAsync(It.IsAny())) .ThrowsAsync(new Exception("Database error")); // Act - var result = _sessionStorage.Get(12345); + var result = await _sessionStorage.GetAsync(12345); // Assert result.Should().BeNull(); @@ -238,7 +238,7 @@ public class DatabaseSessionStorageTests : TestBase .ThrowsAsync(new Exception("Database error")); // Act - var act = async () => await _sessionStorage.SaveSessionAsync(session); + Func act = async () => await _sessionStorage.SaveSessionAsync(session); // Assert var exception = await act.Should() @@ -284,22 +284,22 @@ public class DatabaseSessionStorageTests : TestBase } [Fact] - public void Remove_ShouldReturnFalse_WhenRepositoryThrows() + public async Task RemoveAsync_ShouldReturnFalse_WhenRepositoryThrows() { // Arrange _repositoryMock - .Setup(x => x.DeleteAsync(12345)) + .Setup(x => x.DeleteAsync(It.IsAny())) .ThrowsAsync(new Exception("Database error")); // Act - var result = _sessionStorage.Remove(12345); + var result = await _sessionStorage.RemoveAsync(12345); // Assert result.Should().BeFalse(); } [Fact] - public void GetActiveSessionsCount_ShouldReturnZero_WhenRepositoryThrows() + public async Task GetActiveSessionsCountAsync_ShouldReturnZero_WhenRepositoryThrows() { // Arrange _repositoryMock @@ -307,14 +307,14 @@ public class DatabaseSessionStorageTests : TestBase .ThrowsAsync(new Exception("Database error")); // Act - var result = _sessionStorage.GetActiveSessionsCount(); + var result = await _sessionStorage.GetActiveSessionsCountAsync(); // Assert result.Should().Be(0); } [Fact] - public void CleanupOldSessions_ShouldReturnZero_WhenRepositoryThrows() + public async Task CleanupOldSessionsAsync_ShouldReturnZero_WhenRepositoryThrows() { // Arrange _repositoryMock @@ -322,20 +322,21 @@ public class DatabaseSessionStorageTests : TestBase .ThrowsAsync(new Exception("Database error")); // Act - var result = _sessionStorage.CleanupOldSessions(24); + var result = await _sessionStorage.CleanupOldSessionsAsync(24); // Assert result.Should().Be(0); } [Fact] - public void GetOrCreate_WithCompressionService_ShouldSetCompressionService() + public async Task GetOrCreateAsync_WithCompressionService_ShouldSetCompressionService() { // Arrange var compressionServiceMock = TestDataBuilder.Mocks.CreateCompressionServiceMock(); var storageWithCompression = new DatabaseSessionStorage( _repositoryMock.Object, Mock.Of>(), + _dbContext, compressionServiceMock.Object ); @@ -345,7 +346,7 @@ public class DatabaseSessionStorageTests : TestBase .ReturnsAsync(sessionEntity); // Act - var result = storageWithCompression.GetOrCreate(12345, "private", "Test Chat"); + var result = await storageWithCompression.GetOrCreateAsync(12345, "private", "Test Chat"); // Assert result.Should().NotBeNull(); @@ -353,7 +354,7 @@ public class DatabaseSessionStorageTests : TestBase } [Fact] - public void Get_WithCompressionService_ShouldSetCompressionService() + public async Task GetAsync_WithCompressionService_ShouldSetCompressionService() { // Arrange var loggerMock = new Mock>(); @@ -361,6 +362,7 @@ public class DatabaseSessionStorageTests : TestBase var storageWithCompression = new DatabaseSessionStorage( _repositoryMock.Object, loggerMock.Object, + _dbContext, compressionServiceMock.Object ); @@ -380,7 +382,7 @@ public class DatabaseSessionStorageTests : TestBase _repositoryMock.Setup(x => x.GetByChatIdAsync(12345)).ReturnsAsync(sessionEntity); // Act - var result = storageWithCompression.Get(12345); + var result = await storageWithCompression.GetAsync(12345); // Assert _repositoryMock.Verify(x => x.GetByChatIdAsync(12345), Times.Once); @@ -422,7 +424,7 @@ public class DatabaseSessionStorageTests : TestBase } [Fact] - public void GetOrCreate_WithDefaultParameters_ShouldUseDefaults() + public async Task GetOrCreateAsync_WithDefaultParameters_ShouldUseDefaults() { // Arrange var sessionEntity = TestDataBuilder.Mocks.CreateChatSessionEntity(); @@ -431,7 +433,7 @@ public class DatabaseSessionStorageTests : TestBase .ReturnsAsync(sessionEntity); // Act - var result = _sessionStorage.GetOrCreate(12345); + var result = await _sessionStorage.GetOrCreateAsync(12345); // Assert result.Should().NotBeNull(); diff --git a/ChatBot.Tests/Services/InMemorySessionStorageTests.cs b/ChatBot.Tests/Services/InMemorySessionStorageTests.cs index c122e9a..1a2f85d 100644 --- a/ChatBot.Tests/Services/InMemorySessionStorageTests.cs +++ b/ChatBot.Tests/Services/InMemorySessionStorageTests.cs @@ -17,13 +17,13 @@ public class InMemorySessionStorageTests } [Fact] - public void GetOrCreate_ShouldReturnExistingSession_WhenSessionExists() + public async Task GetOrCreateAsync_ShouldReturnExistingSession_WhenSessionExists() { // Arrange - _sessionStorage.GetOrCreate(12345, "private", "Test Chat"); + await _sessionStorage.GetOrCreateAsync(12345, "private", "Test Chat"); // Act - var result = _sessionStorage.GetOrCreate(12345, "private", "Test Chat"); + var result = await _sessionStorage.GetOrCreateAsync(12345, "private", "Test Chat"); // Assert result.Should().NotBeNull(); @@ -32,10 +32,10 @@ public class InMemorySessionStorageTests } [Fact] - public void GetOrCreate_ShouldCreateNewSession_WhenSessionDoesNotExist() + public async Task GetOrCreateAsync_ShouldCreateNewSession_WhenSessionDoesNotExist() { // Act - var result = _sessionStorage.GetOrCreate(12345, "group", "Test Group"); + var result = await _sessionStorage.GetOrCreateAsync(12345, "group", "Test Group"); // Assert result.Should().NotBeNull(); @@ -45,10 +45,10 @@ public class InMemorySessionStorageTests } [Fact] - public void GetOrCreate_ShouldUseDefaultValues_WhenParametersNotProvided() + public async Task GetOrCreateAsync_ShouldUseDefaultValues_WhenParametersNotProvided() { // Act - var result = _sessionStorage.GetOrCreate(12345); + var result = await _sessionStorage.GetOrCreateAsync(12345); // Assert result.Should().NotBeNull(); @@ -58,23 +58,23 @@ public class InMemorySessionStorageTests } [Fact] - public void Get_ShouldReturnSession_WhenSessionExists() + public async Task GetAsync_ShouldReturnSession_WhenSessionExists() { // Arrange - var session = _sessionStorage.GetOrCreate(12345, "private", "Test Chat"); + var session = await _sessionStorage.GetOrCreateAsync(12345, "private", "Test Chat"); // Act - var result = _sessionStorage.Get(12345); + var result = await _sessionStorage.GetAsync(12345); // Assert result.Should().BeSameAs(session); } [Fact] - public void Get_ShouldReturnNull_WhenSessionDoesNotExist() + public async Task GetAsync_ShouldReturnNull_WhenSessionDoesNotExist() { // Act - var result = _sessionStorage.Get(99999); + var result = await _sessionStorage.GetAsync(99999); // Assert result.Should().BeNull(); @@ -84,7 +84,7 @@ public class InMemorySessionStorageTests public async Task SaveSessionAsync_ShouldUpdateExistingSession() { // Arrange - var session = _sessionStorage.GetOrCreate(12345, "private", "Original Title"); + var session = await _sessionStorage.GetOrCreateAsync(12345, "private", "Original Title"); session.ChatTitle = "Updated Title"; session.LastUpdatedAt = DateTime.UtcNow; @@ -92,7 +92,7 @@ public class InMemorySessionStorageTests await _sessionStorage.SaveSessionAsync(session); // Assert - var savedSession = _sessionStorage.Get(12345); + var savedSession = await _sessionStorage.GetAsync(12345); savedSession.Should().NotBeNull(); savedSession!.ChatTitle.Should().Be("Updated Title"); } @@ -101,121 +101,123 @@ public class InMemorySessionStorageTests public async Task SaveSessionAsync_ShouldAddNewSession() { // Arrange - var session = _sessionStorage.GetOrCreate(12345, "private", "Original Title"); + var session = await _sessionStorage.GetOrCreateAsync(12345, "private", "Original Title"); session.ChatTitle = "New Session"; // Act await _sessionStorage.SaveSessionAsync(session); // Assert - var savedSession = _sessionStorage.Get(12345); + var savedSession = await _sessionStorage.GetAsync(12345); savedSession.Should().NotBeNull(); savedSession!.ChatTitle.Should().Be("New Session"); } [Fact] - public void Remove_ShouldReturnTrue_WhenSessionExists() + public async Task RemoveAsync_ShouldReturnTrue_WhenSessionExists() { // Arrange - _sessionStorage.GetOrCreate(12345, "private", "Test Chat"); + await _sessionStorage.GetOrCreateAsync(12345, "private", "Test Chat"); // Act - var result = _sessionStorage.Remove(12345); + var result = await _sessionStorage.RemoveAsync(12345); // Assert result.Should().BeTrue(); - _sessionStorage.Get(12345).Should().BeNull(); + (await _sessionStorage.GetAsync(12345)).Should().BeNull(); } [Fact] - public void Remove_ShouldReturnFalse_WhenSessionDoesNotExist() + public async Task RemoveAsync_ShouldReturnFalse_WhenSessionDoesNotExist() { // Act - var result = _sessionStorage.Remove(99999); + var result = await _sessionStorage.RemoveAsync(99999); // Assert result.Should().BeFalse(); } [Fact] - public void GetActiveSessionsCount_ShouldReturnCorrectCount() + public async Task GetActiveSessionsCountAsync_ShouldReturnCorrectCount() { // Arrange - _sessionStorage.GetOrCreate(12345, "private", "Chat 1"); - _sessionStorage.GetOrCreate(67890, "group", "Chat 2"); - _sessionStorage.GetOrCreate(11111, "private", "Chat 3"); + await _sessionStorage.GetOrCreateAsync(12345, "private", "Chat 1"); + await _sessionStorage.GetOrCreateAsync(67890, "group", "Chat 2"); + await _sessionStorage.GetOrCreateAsync(11111, "private", "Chat 3"); // Act - var count = _sessionStorage.GetActiveSessionsCount(); + var count = await _sessionStorage.GetActiveSessionsCountAsync(); // Assert count.Should().Be(3); } [Fact] - public void GetActiveSessionsCount_ShouldReturnZero_WhenNoSessions() + public async Task GetActiveSessionsCountAsync_ShouldReturnZero_WhenNoSessions() { // Act - var count = _sessionStorage.GetActiveSessionsCount(); + var count = await _sessionStorage.GetActiveSessionsCountAsync(); // Assert count.Should().Be(0); } [Fact] - public void CleanupOldSessions_ShouldDeleteOldSessions() + public async Task CleanupOldSessionsAsync_ShouldDeleteOldSessions() { // Arrange - var oldSession = _sessionStorage.GetOrCreate(99999, "private", "Old Chat"); + var oldSession = await _sessionStorage.GetOrCreateAsync(99999, "private", "Old Chat"); // Manually set CreatedAt to 2 days ago using test method oldSession.SetCreatedAtForTesting(DateTime.UtcNow.AddDays(-2)); - var recentSession = _sessionStorage.GetOrCreate(88888, "private", "Recent Chat"); + var recentSession = await _sessionStorage.GetOrCreateAsync(88888, "private", "Recent Chat"); // Manually set CreatedAt to 30 minutes ago using test method recentSession.SetCreatedAtForTesting(DateTime.UtcNow.AddMinutes(-30)); // Act - _sessionStorage.CleanupOldSessions(1); // Delete sessions older than 1 day + await _sessionStorage.CleanupOldSessionsAsync(1); // Delete sessions older than 1 day // Assert - _sessionStorage.Get(99999).Should().BeNull(); // Old session should be deleted - _sessionStorage.Get(88888).Should().NotBeNull(); // Recent session should remain + (await _sessionStorage.GetAsync(99999)) + .Should() + .BeNull(); // Old session should be deleted + (await _sessionStorage.GetAsync(88888)).Should().NotBeNull(); // Recent session should remain } [Fact] - public void CleanupOldSessions_ShouldNotDeleteRecentSessions() + public async Task CleanupOldSessionsAsync_ShouldNotDeleteRecentSessions() { // Arrange - var recentSession1 = _sessionStorage.GetOrCreate(12345, "private", "Recent 1"); + var recentSession1 = await _sessionStorage.GetOrCreateAsync(12345, "private", "Recent 1"); recentSession1.CreatedAt = DateTime.UtcNow.AddHours(-1); - var recentSession2 = _sessionStorage.GetOrCreate(67890, "private", "Recent 2"); + var recentSession2 = await _sessionStorage.GetOrCreateAsync(67890, "private", "Recent 2"); recentSession2.CreatedAt = DateTime.UtcNow.AddMinutes(-30); // Act - var deletedCount = _sessionStorage.CleanupOldSessions(24); // Delete sessions older than 24 hours + var deletedCount = await _sessionStorage.CleanupOldSessionsAsync(24); // Delete sessions older than 24 hours // Assert deletedCount.Should().Be(0); - _sessionStorage.Get(12345).Should().NotBeNull(); - _sessionStorage.Get(67890).Should().NotBeNull(); + (await _sessionStorage.GetAsync(12345)).Should().NotBeNull(); + (await _sessionStorage.GetAsync(67890)).Should().NotBeNull(); } [Fact] - public void CleanupOldSessions_ShouldReturnZero_WhenNoSessions() + public async Task CleanupOldSessionsAsync_ShouldReturnZero_WhenNoSessions() { // Act - var deletedCount = _sessionStorage.CleanupOldSessions(1); + var deletedCount = await _sessionStorage.CleanupOldSessionsAsync(1); // Assert deletedCount.Should().Be(0); } [Fact] - public void GetOrCreate_ShouldCreateSessionWithCorrectTimestamp() + public async Task GetOrCreateAsync_ShouldCreateSessionWithCorrectTimestamp() { // Act - var session = _sessionStorage.GetOrCreate(12345, "private", "Test Chat"); + var session = await _sessionStorage.GetOrCreateAsync(12345, "private", "Test Chat"); // Assert session.CreatedAt.Should().BeCloseTo(DateTime.UtcNow, TimeSpan.FromMinutes(1)); @@ -226,7 +228,7 @@ public class InMemorySessionStorageTests public async Task SaveSessionAsync_ShouldUpdateLastUpdatedAt() { // Arrange - var session = _sessionStorage.GetOrCreate(12345, "private", "Test Chat"); + var session = await _sessionStorage.GetOrCreateAsync(12345, "private", "Test Chat"); var originalTime = session.LastUpdatedAt; // Wait a bit to ensure time difference @@ -238,12 +240,12 @@ public class InMemorySessionStorageTests await _sessionStorage.SaveSessionAsync(session); // Assert - var savedSession = _sessionStorage.Get(12345); + var savedSession = await _sessionStorage.GetAsync(12345); savedSession!.LastUpdatedAt.Should().BeAfter(originalTime); } [Fact] - public async Task GetOrCreate_ShouldHandleConcurrentAccess() + public async Task GetOrCreateAsync_ShouldHandleConcurrentAccess() { // Arrange var tasks = new List>(); @@ -253,40 +255,46 @@ public class InMemorySessionStorageTests { var chatId = 1000 + i; tasks.Add( - Task.Run(() => _sessionStorage.GetOrCreate(chatId, "private", $"Chat {chatId}")) + Task.Run(async () => + await _sessionStorage.GetOrCreateAsync(chatId, "private", $"Chat {chatId}") + ) ); } await Task.WhenAll(tasks); // Assert - _sessionStorage.GetActiveSessionsCount().Should().Be(100); + (await _sessionStorage.GetActiveSessionsCountAsync()) + .Should() + .Be(100); // Verify all sessions were created for (int i = 0; i < 100; i++) { var chatId = 1000 + i; - var session = _sessionStorage.Get(chatId); + var session = await _sessionStorage.GetAsync(chatId); session.Should().NotBeNull(); session!.ChatId.Should().Be(chatId); } } [Fact] - public void Remove_ShouldDecreaseActiveSessionsCount() + public async Task RemoveAsync_ShouldDecreaseActiveSessionsCount() { // Arrange - _sessionStorage.GetOrCreate(12345, "private", "Chat 1"); - _sessionStorage.GetOrCreate(67890, "private", "Chat 2"); - _sessionStorage.GetOrCreate(11111, "private", "Chat 3"); + await _sessionStorage.GetOrCreateAsync(12345, "private", "Chat 1"); + await _sessionStorage.GetOrCreateAsync(67890, "private", "Chat 2"); + await _sessionStorage.GetOrCreateAsync(11111, "private", "Chat 3"); // Act - _sessionStorage.Remove(67890); + await _sessionStorage.RemoveAsync(67890); // Assert - _sessionStorage.GetActiveSessionsCount().Should().Be(2); - _sessionStorage.Get(12345).Should().NotBeNull(); - _sessionStorage.Get(67890).Should().BeNull(); - _sessionStorage.Get(11111).Should().NotBeNull(); + (await _sessionStorage.GetActiveSessionsCountAsync()) + .Should() + .Be(2); + (await _sessionStorage.GetAsync(12345)).Should().NotBeNull(); + (await _sessionStorage.GetAsync(67890)).Should().BeNull(); + (await _sessionStorage.GetAsync(11111)).Should().NotBeNull(); } } diff --git a/ChatBot.Tests/Services/Interfaces/ISessionStorageTests.cs b/ChatBot.Tests/Services/Interfaces/ISessionStorageTests.cs index e72a414..f2b8586 100644 --- a/ChatBot.Tests/Services/Interfaces/ISessionStorageTests.cs +++ b/ChatBot.Tests/Services/Interfaces/ISessionStorageTests.cs @@ -19,41 +19,41 @@ public class ISessionStorageTests : UnitTestBase // Assert methods.Should().HaveCount(6); - // GetOrCreate method - var getOrCreateMethod = methods.FirstOrDefault(m => m.Name == "GetOrCreate"); + // GetOrCreateAsync method + var getOrCreateMethod = methods.FirstOrDefault(m => m.Name == "GetOrCreateAsync"); getOrCreateMethod.Should().NotBeNull(); - getOrCreateMethod!.ReturnType.Should().Be(); + getOrCreateMethod!.ReturnType.Should().Be(typeof(Task)); getOrCreateMethod.GetParameters().Should().HaveCount(3); getOrCreateMethod.GetParameters()[0].ParameterType.Should().Be(); getOrCreateMethod.GetParameters()[1].ParameterType.Should().Be(); getOrCreateMethod.GetParameters()[2].ParameterType.Should().Be(); - // Get method - var getMethod = methods.FirstOrDefault(m => m.Name == "Get"); + // GetAsync method + var getMethod = methods.FirstOrDefault(m => m.Name == "GetAsync"); getMethod.Should().NotBeNull(); - getMethod!.ReturnType.Should().Be(); + getMethod!.ReturnType.Should().Be(typeof(Task)); getMethod.GetParameters().Should().HaveCount(1); getMethod.GetParameters()[0].ParameterType.Should().Be(); - // Remove method - var removeMethod = methods.FirstOrDefault(m => m.Name == "Remove"); + // RemoveAsync method + var removeMethod = methods.FirstOrDefault(m => m.Name == "RemoveAsync"); removeMethod.Should().NotBeNull(); - removeMethod!.ReturnType.Should().Be(); + removeMethod!.ReturnType.Should().Be(typeof(Task)); removeMethod.GetParameters().Should().HaveCount(1); removeMethod.GetParameters()[0].ParameterType.Should().Be(); - // GetActiveSessionsCount method + // GetActiveSessionsCountAsync method var getActiveSessionsCountMethod = methods.FirstOrDefault(m => - m.Name == "GetActiveSessionsCount" + m.Name == "GetActiveSessionsCountAsync" ); getActiveSessionsCountMethod.Should().NotBeNull(); - getActiveSessionsCountMethod!.ReturnType.Should().Be(); + getActiveSessionsCountMethod!.ReturnType.Should().Be(typeof(Task)); getActiveSessionsCountMethod.GetParameters().Should().BeEmpty(); - // CleanupOldSessions method - var cleanupOldSessionsMethod = methods.FirstOrDefault(m => m.Name == "CleanupOldSessions"); + // CleanupOldSessionsAsync method + var cleanupOldSessionsMethod = methods.FirstOrDefault(m => m.Name == "CleanupOldSessionsAsync"); cleanupOldSessionsMethod.Should().NotBeNull(); - cleanupOldSessionsMethod!.ReturnType.Should().Be(); + cleanupOldSessionsMethod!.ReturnType.Should().Be(typeof(Task)); cleanupOldSessionsMethod.GetParameters().Should().HaveCount(1); cleanupOldSessionsMethod.GetParameters()[0].ParameterType.Should().Be(); @@ -88,7 +88,7 @@ public class ISessionStorageTests : UnitTestBase } [Fact] - public void ISessionStorage_GetOrCreate_ShouldReturnChatSession() + public async Task ISessionStorage_GetOrCreateAsync_ShouldReturnChatSession() { // Arrange var mock = new Mock(); @@ -97,120 +97,120 @@ public class ISessionStorageTests : UnitTestBase var chatTitle = "Test Chat"; var expectedSession = TestDataBuilder.ChatSessions.CreateBasicSession(chatId, chatType); - mock.Setup(x => x.GetOrCreate(It.IsAny(), It.IsAny(), It.IsAny())) - .Returns(expectedSession); + mock.Setup(x => x.GetOrCreateAsync(It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(expectedSession); // Act - var result = mock.Object.GetOrCreate(chatId, chatType, chatTitle); + var result = await mock.Object.GetOrCreateAsync(chatId, chatType, chatTitle); // Assert result.Should().Be(expectedSession); - mock.Verify(x => x.GetOrCreate(chatId, chatType, chatTitle), Times.Once); + mock.Verify(x => x.GetOrCreateAsync(chatId, chatType, chatTitle), Times.Once); } [Fact] - public void ISessionStorage_Get_ShouldReturnChatSessionOrNull() + public async Task ISessionStorage_GetAsync_ShouldReturnChatSessionOrNull() { // Arrange var mock = new Mock(); var chatId = 12345L; var expectedSession = TestDataBuilder.ChatSessions.CreateBasicSession(chatId, "private"); - mock.Setup(x => x.Get(It.IsAny())).Returns(expectedSession); + mock.Setup(x => x.GetAsync(It.IsAny())).ReturnsAsync(expectedSession); // Act - var result = mock.Object.Get(chatId); + var result = await mock.Object.GetAsync(chatId); // Assert result.Should().Be(expectedSession); - mock.Verify(x => x.Get(chatId), Times.Once); + mock.Verify(x => x.GetAsync(chatId), Times.Once); } [Fact] - public void ISessionStorage_Get_ShouldReturnNullWhenSessionNotFound() + public async Task ISessionStorage_GetAsync_ShouldReturnNullWhenSessionNotFound() { // Arrange var mock = new Mock(); var chatId = 12345L; - mock.Setup(x => x.Get(It.IsAny())).Returns((ChatSession?)null); + mock.Setup(x => x.GetAsync(It.IsAny())).ReturnsAsync((ChatSession?)null); // Act - var result = mock.Object.Get(chatId); + var result = await mock.Object.GetAsync(chatId); // Assert result.Should().BeNull(); - mock.Verify(x => x.Get(chatId), Times.Once); + mock.Verify(x => x.GetAsync(chatId), Times.Once); } [Fact] - public void ISessionStorage_Remove_ShouldReturnBoolean() + public async Task ISessionStorage_RemoveAsync_ShouldReturnBoolean() { // Arrange var mock = new Mock(); var chatId = 12345L; var expectedResult = true; - mock.Setup(x => x.Remove(It.IsAny())).Returns(expectedResult); + mock.Setup(x => x.RemoveAsync(It.IsAny())).ReturnsAsync(expectedResult); // Act - var result = mock.Object.Remove(chatId); + var result = await mock.Object.RemoveAsync(chatId); // Assert result.Should().Be(expectedResult); - mock.Verify(x => x.Remove(chatId), Times.Once); + mock.Verify(x => x.RemoveAsync(chatId), Times.Once); } [Fact] - public void ISessionStorage_GetActiveSessionsCount_ShouldReturnInt() + public async Task ISessionStorage_GetActiveSessionsCountAsync_ShouldReturnInt() { // Arrange var mock = new Mock(); var expectedCount = 5; - mock.Setup(x => x.GetActiveSessionsCount()).Returns(expectedCount); + mock.Setup(x => x.GetActiveSessionsCountAsync()).ReturnsAsync(expectedCount); // Act - var result = mock.Object.GetActiveSessionsCount(); + var result = await mock.Object.GetActiveSessionsCountAsync(); // Assert result.Should().Be(expectedCount); - mock.Verify(x => x.GetActiveSessionsCount(), Times.Once); + mock.Verify(x => x.GetActiveSessionsCountAsync(), Times.Once); } [Fact] - public void ISessionStorage_CleanupOldSessions_ShouldReturnInt() + public async Task ISessionStorage_CleanupOldSessionsAsync_ShouldReturnInt() { // Arrange var mock = new Mock(); var hoursOld = 24; var expectedCleanedCount = 3; - mock.Setup(x => x.CleanupOldSessions(It.IsAny())).Returns(expectedCleanedCount); + mock.Setup(x => x.CleanupOldSessionsAsync(It.IsAny())).ReturnsAsync(expectedCleanedCount); // Act - var result = mock.Object.CleanupOldSessions(hoursOld); + var result = await mock.Object.CleanupOldSessionsAsync(hoursOld); // Assert result.Should().Be(expectedCleanedCount); - mock.Verify(x => x.CleanupOldSessions(hoursOld), Times.Once); + mock.Verify(x => x.CleanupOldSessionsAsync(hoursOld), Times.Once); } [Fact] - public void ISessionStorage_CleanupOldSessions_ShouldUseDefaultValue() + public async Task ISessionStorage_CleanupOldSessionsAsync_ShouldUseDefaultValue() { // Arrange var mock = new Mock(); var expectedCleanedCount = 2; - mock.Setup(x => x.CleanupOldSessions(It.IsAny())).Returns(expectedCleanedCount); + mock.Setup(x => x.CleanupOldSessionsAsync(It.IsAny())).ReturnsAsync(expectedCleanedCount); // Act - var result = mock.Object.CleanupOldSessions(); + var result = await mock.Object.CleanupOldSessionsAsync(); // Assert result.Should().Be(expectedCleanedCount); - mock.Verify(x => x.CleanupOldSessions(24), Times.Once); // Default value is 24 + mock.Verify(x => x.CleanupOldSessionsAsync(24), Times.Once); // Default value is 24 } [Fact] @@ -230,22 +230,22 @@ public class ISessionStorageTests : UnitTestBase } [Fact] - public void ISessionStorage_GetOrCreate_ShouldUseDefaultValues() + public async Task ISessionStorage_GetOrCreateAsync_ShouldUseDefaultValues() { // Arrange var mock = new Mock(); var chatId = 12345L; var expectedSession = TestDataBuilder.ChatSessions.CreateBasicSession(chatId, "private"); - mock.Setup(x => x.GetOrCreate(It.IsAny(), It.IsAny(), It.IsAny())) - .Returns(expectedSession); + mock.Setup(x => x.GetOrCreateAsync(It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(expectedSession); // Act - var result = mock.Object.GetOrCreate(chatId); + var result = await mock.Object.GetOrCreateAsync(chatId); // Assert result.Should().Be(expectedSession); - mock.Verify(x => x.GetOrCreate(chatId, "private", ""), Times.Once); // Default values + mock.Verify(x => x.GetOrCreateAsync(chatId, "private", ""), Times.Once); // Default values } [Fact] @@ -280,15 +280,15 @@ public class ISessionStorageTests : UnitTestBase // All methods should be public methods.All(m => m.IsPublic).Should().BeTrue(); - // GetOrCreate should have default parameters - var getOrCreateMethod = methods.First(m => m.Name == "GetOrCreate"); + // GetOrCreateAsync should have default parameters + var getOrCreateMethod = methods.First(m => m.Name == "GetOrCreateAsync"); getOrCreateMethod.GetParameters()[1].HasDefaultValue.Should().BeTrue(); getOrCreateMethod.GetParameters()[1].DefaultValue.Should().Be("private"); getOrCreateMethod.GetParameters()[2].HasDefaultValue.Should().BeTrue(); getOrCreateMethod.GetParameters()[2].DefaultValue.Should().Be(""); - // CleanupOldSessions should have default parameter - var cleanupMethod = methods.First(m => m.Name == "CleanupOldSessions"); + // CleanupOldSessionsAsync should have default parameter + var cleanupMethod = methods.First(m => m.Name == "CleanupOldSessionsAsync"); cleanupMethod.GetParameters()[0].HasDefaultValue.Should().BeTrue(); cleanupMethod.GetParameters()[0].DefaultValue.Should().Be(24); } diff --git a/ChatBot.Tests/Telegram/Commands/SettingsCommandTests.cs b/ChatBot.Tests/Telegram/Commands/SettingsCommandTests.cs index eceb0e2..fddfbed 100644 --- a/ChatBot.Tests/Telegram/Commands/SettingsCommandTests.cs +++ b/ChatBot.Tests/Telegram/Commands/SettingsCommandTests.cs @@ -44,7 +44,7 @@ public class SettingsCommandTests : UnitTestBase // Arrange var chatId = 12345L; var session = TestDataBuilder.ChatSessions.CreateBasicSession(chatId); - _chatServiceMock.Setup(x => x.GetSession(chatId)).Returns(session); + _chatServiceMock.Setup(x => x.GetSessionAsync(chatId)).ReturnsAsync(session); var context = new TelegramCommandContext { @@ -70,8 +70,8 @@ public class SettingsCommandTests : UnitTestBase // Arrange var chatId = 12345L; _chatServiceMock - .Setup(x => x.GetSession(chatId)) - .Returns((ChatBot.Models.ChatSession?)null); + .Setup(x => x.GetSessionAsync(chatId)) + .ReturnsAsync((ChatBot.Models.ChatSession?)null); var context = new TelegramCommandContext { diff --git a/ChatBot.Tests/Telegram/Commands/StatusCommandTests.cs b/ChatBot.Tests/Telegram/Commands/StatusCommandTests.cs index bc34a85..9f4d19d 100644 --- a/ChatBot.Tests/Telegram/Commands/StatusCommandTests.cs +++ b/ChatBot.Tests/Telegram/Commands/StatusCommandTests.cs @@ -375,7 +375,7 @@ public class StatusCommandTests : UnitTestBase var session = TestDataBuilder.ChatSessions.CreateBasicSession(12345, "private"); session.AddUserMessage("Test", "user"); - chatServiceMock.Setup(x => x.GetSession(12345)).Returns(session); + chatServiceMock.Setup(x => x.GetSessionAsync(12345)).ReturnsAsync(session); var statusCommand = new StatusCommand( chatServiceMock.Object, diff --git a/ChatBot.Tests/Telegram/Commands/TelegramCommandProcessorTests.cs b/ChatBot.Tests/Telegram/Commands/TelegramCommandProcessorTests.cs index ef2fcc1..d6d23aa 100644 --- a/ChatBot.Tests/Telegram/Commands/TelegramCommandProcessorTests.cs +++ b/ChatBot.Tests/Telegram/Commands/TelegramCommandProcessorTests.cs @@ -236,7 +236,7 @@ public class TelegramCommandProcessorTests : UnitTestBase var chatTitle = "Test Chat"; // Act - var result = await _processor.ProcessMessageAsync( + Func act = async () => await _processor.ProcessMessageAsync( messageText, chatId, username, @@ -245,7 +245,8 @@ public class TelegramCommandProcessorTests : UnitTestBase ); // Assert - result.Should().NotBeNull(); + await act.Should().ThrowAsync() + .WithMessage("ChatId cannot be 0*"); } [Fact] @@ -746,7 +747,7 @@ public class TelegramCommandProcessorTests : UnitTestBase ); // Assert - result.Should().Contain("Произошла ошибка"); + result.Should().Contain("Произошла непредвиденная ошибка"); } [Fact] diff --git a/ChatBot.Tests/TestUtilities/TestDataBuilder.cs b/ChatBot.Tests/TestUtilities/TestDataBuilder.cs index a1fa04f..b14c7c3 100644 --- a/ChatBot.Tests/TestUtilities/TestDataBuilder.cs +++ b/ChatBot.Tests/TestUtilities/TestDataBuilder.cs @@ -152,25 +152,23 @@ public static class TestDataBuilder var mock = new Mock(); var sessions = new Dictionary(); - mock.Setup(x => x.GetOrCreate(It.IsAny(), It.IsAny(), It.IsAny())) - .Returns( - (chatId, chatType, chatTitle) => + mock.Setup(x => x.GetOrCreateAsync(It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync((long chatId, string chatType, string chatTitle) => + { + if (!sessions.TryGetValue(chatId, out var session)) { - if (!sessions.TryGetValue(chatId, out var session)) - { - session = TestDataBuilder.ChatSessions.CreateBasicSession( - chatId, - chatType - ); - session.ChatTitle = chatTitle; - sessions[chatId] = session; - } - return session; + session = TestDataBuilder.ChatSessions.CreateBasicSession( + chatId, + chatType + ); + session.ChatTitle = chatTitle; + sessions[chatId] = session; } - ); + return session; + }); - mock.Setup(x => x.Get(It.IsAny())) - .Returns(chatId => + mock.Setup(x => x.GetAsync(It.IsAny())) + .ReturnsAsync((long chatId) => sessions.TryGetValue(chatId, out var session) ? session : null ); @@ -181,12 +179,12 @@ public static class TestDataBuilder return Task.CompletedTask; }); - mock.Setup(x => x.Remove(It.IsAny())) - .Returns(chatId => sessions.Remove(chatId)); + mock.Setup(x => x.RemoveAsync(It.IsAny())) + .ReturnsAsync((long chatId) => sessions.Remove(chatId)); - mock.Setup(x => x.GetActiveSessionsCount()).Returns(() => sessions.Count); + mock.Setup(x => x.GetActiveSessionsCountAsync()).ReturnsAsync(() => sessions.Count); - mock.Setup(x => x.CleanupOldSessions(It.IsAny())).Returns(hoursOld => 0); + mock.Setup(x => x.CleanupOldSessionsAsync(It.IsAny())).ReturnsAsync((int hoursOld) => 0); return mock; } diff --git a/ChatBot/Models/ChatSession.cs b/ChatBot/Models/ChatSession.cs index a308cc5..454164e 100644 --- a/ChatBot/Models/ChatSession.cs +++ b/ChatBot/Models/ChatSession.cs @@ -10,6 +10,7 @@ namespace ChatBot.Models public class ChatSession { private readonly object _lock = new object(); + private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1); private readonly List _messageHistory = new List(); private IHistoryCompressionService? _compressionService; @@ -110,35 +111,41 @@ namespace ChatBot.Models int compressionTarget ) { - lock (_lock) + await _semaphore.WaitAsync(); + try { _messageHistory.Add(message); LastUpdatedAt = DateTime.UtcNow; - } - // Check if compression is needed and perform it asynchronously - if ( - _compressionService != null - && _compressionService.ShouldCompress(_messageHistory.Count, compressionThreshold) - ) - { - await CompressHistoryAsync(compressionTarget); + // Check if compression is needed and perform it asynchronously + if ( + _compressionService != null + && _compressionService.ShouldCompress(_messageHistory.Count, compressionThreshold) + ) + { + await CompressHistoryAsync(compressionTarget); + } + else if (_messageHistory.Count > MaxHistoryLength) + { + // Fallback to simple trimming if compression is not available + await TrimHistoryAsync(); + } } - else if (_messageHistory.Count > MaxHistoryLength) + finally { - // Fallback to simple trimming if compression is not available - await TrimHistoryAsync(); + _semaphore.Release(); } } /// /// Compress message history using the compression service + /// Note: This method should be called within a semaphore lock /// private async Task CompressHistoryAsync(int targetCount) { if (_compressionService == null) { - await TrimHistoryAsync(); + TrimHistoryInternal(); return; } @@ -149,50 +156,52 @@ namespace ChatBot.Models targetCount ); - lock (_lock) - { - _messageHistory.Clear(); - _messageHistory.AddRange(compressedMessages); - LastUpdatedAt = DateTime.UtcNow; - } + _messageHistory.Clear(); + _messageHistory.AddRange(compressedMessages); + LastUpdatedAt = DateTime.UtcNow; } catch (Exception) { // Log error and fallback to simple trimming // Note: We can't inject ILogger here, so we'll just fallback - await TrimHistoryAsync(); + TrimHistoryInternal(); } } /// - /// Simple history trimming without compression + /// Simple history trimming without compression (async wrapper) + /// Note: This method should be called within a semaphore lock /// - private async Task TrimHistoryAsync() + private Task TrimHistoryAsync() { - await Task.Run(() => - { - lock (_lock) - { - if (_messageHistory.Count > MaxHistoryLength) - { - var systemMessage = _messageHistory.FirstOrDefault(m => - m.Role == ChatRole.System - ); - var recentMessages = _messageHistory - .Where(m => m.Role != ChatRole.System) - .TakeLast(MaxHistoryLength - (systemMessage != null ? 1 : 0)) - .ToList(); + TrimHistoryInternal(); + return Task.CompletedTask; + } - _messageHistory.Clear(); - if (systemMessage != null) - { - _messageHistory.Add(systemMessage); - } - _messageHistory.AddRange(recentMessages); - LastUpdatedAt = DateTime.UtcNow; - } + /// + /// Internal method to trim history without async overhead + /// Note: This method should be called within a semaphore lock + /// + private void TrimHistoryInternal() + { + if (_messageHistory.Count > MaxHistoryLength) + { + var systemMessage = _messageHistory.FirstOrDefault(m => + m.Role == ChatRole.System + ); + var recentMessages = _messageHistory + .Where(m => m.Role != ChatRole.System) + .TakeLast(MaxHistoryLength - (systemMessage != null ? 1 : 0)) + .ToList(); + + _messageHistory.Clear(); + if (systemMessage != null) + { + _messageHistory.Add(systemMessage); } - }); + _messageHistory.AddRange(recentMessages); + LastUpdatedAt = DateTime.UtcNow; + } } /// diff --git a/ChatBot/Services/ChatService.cs b/ChatBot/Services/ChatService.cs index aaf2f87..80d533a 100644 --- a/ChatBot/Services/ChatService.cs +++ b/ChatBot/Services/ChatService.cs @@ -35,13 +35,13 @@ namespace ChatBot.Services /// /// Get or create a chat session for the given chat ID /// - public ChatSession GetOrCreateSession( + public async Task GetOrCreateSessionAsync( long chatId, string chatType = ChatTypes.Private, string chatTitle = "" ) { - var session = _sessionStorage.GetOrCreate(chatId, chatType, chatTitle); + var session = await _sessionStorage.GetOrCreateAsync(chatId, chatType, chatTitle); // Set compression service if compression is enabled if (_aiSettings.EnableHistoryCompression) @@ -66,7 +66,7 @@ namespace ChatBot.Services { try { - var session = GetOrCreateSession(chatId, chatType, chatTitle); + var session = await GetOrCreateSessionAsync(chatId, chatType, chatTitle); // Add user message to history with username if (_aiSettings.EnableHistoryCompression) @@ -157,7 +157,7 @@ namespace ChatBot.Services /// public async Task UpdateSessionParametersAsync(long chatId, string? model = null) { - var session = _sessionStorage.Get(chatId); + var session = await _sessionStorage.GetAsync(chatId); if (session != null) { if (!string.IsNullOrEmpty(model)) @@ -177,7 +177,7 @@ namespace ChatBot.Services /// public virtual async Task ClearHistoryAsync(long chatId) { - var session = _sessionStorage.Get(chatId); + var session = await _sessionStorage.GetAsync(chatId); if (session != null) { session.ClearHistory(); @@ -192,33 +192,33 @@ namespace ChatBot.Services /// /// Get session information /// - public virtual ChatSession? GetSession(long chatId) + public virtual async Task GetSessionAsync(long chatId) { - return _sessionStorage.Get(chatId); + return await _sessionStorage.GetAsync(chatId); } /// /// Remove a session /// - public bool RemoveSession(long chatId) + public async Task RemoveSessionAsync(long chatId) { - return _sessionStorage.Remove(chatId); + return await _sessionStorage.RemoveAsync(chatId); } /// /// Get all active sessions count /// - public int GetActiveSessionsCount() + public async Task GetActiveSessionsCountAsync() { - return _sessionStorage.GetActiveSessionsCount(); + return await _sessionStorage.GetActiveSessionsCountAsync(); } /// /// Clean up old sessions (older than specified hours) /// - public int CleanupOldSessions(int hoursOld = 24) + public async Task CleanupOldSessionsAsync(int hoursOld = 24) { - return _sessionStorage.CleanupOldSessions(hoursOld); + return await _sessionStorage.CleanupOldSessionsAsync(hoursOld); } } } diff --git a/ChatBot/Services/DatabaseSessionStorage.cs b/ChatBot/Services/DatabaseSessionStorage.cs index 56988e0..7b59344 100644 --- a/ChatBot/Services/DatabaseSessionStorage.cs +++ b/ChatBot/Services/DatabaseSessionStorage.cs @@ -1,3 +1,4 @@ +using ChatBot.Data; using ChatBot.Data.Interfaces; using ChatBot.Models; using ChatBot.Models.Dto; @@ -15,19 +16,22 @@ namespace ChatBot.Services private readonly IChatSessionRepository _repository; private readonly ILogger _logger; private readonly IHistoryCompressionService? _compressionService; + private readonly ChatBotDbContext _context; public DatabaseSessionStorage( IChatSessionRepository repository, ILogger logger, + ChatBotDbContext context, IHistoryCompressionService? compressionService = null ) { _repository = repository; _logger = logger; + _context = context; _compressionService = compressionService; } - public ChatSession GetOrCreate( + public async Task GetOrCreateAsync( long chatId, string chatType = "private", string chatTitle = "" @@ -35,10 +39,7 @@ namespace ChatBot.Services { try { - var sessionEntity = _repository - .GetOrCreateAsync(chatId, chatType, chatTitle) - .GetAwaiter() - .GetResult(); + var sessionEntity = await _repository.GetOrCreateAsync(chatId, chatType, chatTitle); return ConvertToChatSession(sessionEntity); } catch (Exception ex) @@ -51,11 +52,11 @@ namespace ChatBot.Services } } - public ChatSession? Get(long chatId) + public async Task GetAsync(long chatId) { try { - var sessionEntity = _repository.GetByChatIdAsync(chatId).GetAwaiter().GetResult(); + var sessionEntity = await _repository.GetByChatIdAsync(chatId); return sessionEntity != null ? ConvertToChatSession(sessionEntity) : null; } catch (Exception ex) @@ -65,11 +66,11 @@ namespace ChatBot.Services } } - public bool Remove(long chatId) + public async Task RemoveAsync(long chatId) { try { - return _repository.DeleteAsync(chatId).GetAwaiter().GetResult(); + return await _repository.DeleteAsync(chatId); } catch (Exception ex) { @@ -78,11 +79,11 @@ namespace ChatBot.Services } } - public int GetActiveSessionsCount() + public async Task GetActiveSessionsCountAsync() { try { - return _repository.GetActiveSessionsCountAsync().GetAwaiter().GetResult(); + return await _repository.GetActiveSessionsCountAsync(); } catch (Exception ex) { @@ -91,11 +92,11 @@ namespace ChatBot.Services } } - public int CleanupOldSessions(int hoursOld = 24) + public async Task CleanupOldSessionsAsync(int hoursOld = 24) { try { - return _repository.CleanupOldSessionsAsync(hoursOld).GetAwaiter().GetResult(); + return await _repository.CleanupOldSessionsAsync(hoursOld); } catch (Exception ex) { @@ -105,10 +106,11 @@ namespace ChatBot.Services } /// - /// Save session changes to database + /// Save session changes to database with transaction support /// public async Task SaveSessionAsync(ChatSession session) { + await using var transaction = await _context.Database.BeginTransactionAsync(); try { var sessionEntity = await _repository.GetByChatIdAsync(session.ChatId); @@ -126,7 +128,7 @@ namespace ChatBot.Services sessionEntity.MaxHistoryLength = session.MaxHistoryLength; sessionEntity.LastUpdatedAt = DateTime.UtcNow; - // Clear existing messages and add new ones + // Clear existing messages and add new ones in a transaction await _repository.ClearMessagesAsync(sessionEntity.Id); var messages = session.GetAllMessages(); @@ -141,9 +143,14 @@ namespace ChatBot.Services } await _repository.UpdateAsync(sessionEntity); + + // Commit transaction if all operations succeeded + await transaction.CommitAsync(); } catch (Exception ex) { + // Transaction will be automatically rolled back on exception + await transaction.RollbackAsync(); _logger.LogError(ex, "Failed to save session for chat {ChatId}", session.ChatId); throw new InvalidOperationException( $"Failed to save session for chat {session.ChatId}", diff --git a/ChatBot/Services/InMemorySessionStorage.cs b/ChatBot/Services/InMemorySessionStorage.cs index b8e4105..8138afb 100644 --- a/ChatBot/Services/InMemorySessionStorage.cs +++ b/ChatBot/Services/InMemorySessionStorage.cs @@ -17,7 +17,7 @@ namespace ChatBot.Services _logger = logger; } - public ChatSession GetOrCreate( + public Task GetOrCreateAsync( long chatId, string chatType = "private", string chatTitle = "" @@ -54,31 +54,31 @@ namespace ChatBot.Services } } - return session; + return Task.FromResult(session); } - public ChatSession? Get(long chatId) + public Task GetAsync(long chatId) { _sessions.TryGetValue(chatId, out var session); - return session; + return Task.FromResult(session); } - public bool Remove(long chatId) + public Task RemoveAsync(long chatId) { var removed = _sessions.TryRemove(chatId, out _); if (removed) { _logger.LogInformation("Removed session for chat {ChatId}", chatId); } - return removed; + return Task.FromResult(removed); } - public int GetActiveSessionsCount() + public Task GetActiveSessionsCountAsync() { - return _sessions.Count; + return Task.FromResult(_sessions.Count); } - public int CleanupOldSessions(int hoursOld = 24) + public Task CleanupOldSessionsAsync(int hoursOld = 24) { var cutoffTime = DateTime.UtcNow.AddHours(-hoursOld); var sessionsToRemove = _sessions @@ -97,7 +97,7 @@ namespace ChatBot.Services } _logger.LogInformation("Cleaned up {DeletedCount} old sessions", deletedCount); - return deletedCount; + return Task.FromResult(deletedCount); } public Task SaveSessionAsync(ChatSession session) diff --git a/ChatBot/Services/Interfaces/ISessionStorage.cs b/ChatBot/Services/Interfaces/ISessionStorage.cs index 0b9e6c6..ac90128 100644 --- a/ChatBot/Services/Interfaces/ISessionStorage.cs +++ b/ChatBot/Services/Interfaces/ISessionStorage.cs @@ -10,27 +10,27 @@ namespace ChatBot.Services.Interfaces /// /// Get or create a chat session /// - ChatSession GetOrCreate(long chatId, string chatType = "private", string chatTitle = ""); + Task GetOrCreateAsync(long chatId, string chatType = "private", string chatTitle = ""); /// /// Get a session by chat ID /// - ChatSession? Get(long chatId); + Task GetAsync(long chatId); /// /// Remove a session /// - bool Remove(long chatId); + Task RemoveAsync(long chatId); /// /// Get count of active sessions /// - int GetActiveSessionsCount(); + Task GetActiveSessionsCountAsync(); /// /// Clean up old sessions /// - int CleanupOldSessions(int hoursOld = 24); + Task CleanupOldSessionsAsync(int hoursOld = 24); /// /// Save session changes to storage (for database implementations) diff --git a/ChatBot/Services/Telegram/Commands/SettingsCommand.cs b/ChatBot/Services/Telegram/Commands/SettingsCommand.cs index 74f1acd..f97daf8 100644 --- a/ChatBot/Services/Telegram/Commands/SettingsCommand.cs +++ b/ChatBot/Services/Telegram/Commands/SettingsCommand.cs @@ -24,17 +24,15 @@ namespace ChatBot.Services.Telegram.Commands public override string CommandName => "/settings"; public override string Description => "Показать настройки чата"; - public override Task ExecuteAsync( + public override async Task ExecuteAsync( TelegramCommandContext context, CancellationToken cancellationToken = default ) { - var session = _chatService.GetSession(context.ChatId); + var session = await _chatService.GetSessionAsync(context.ChatId); if (session == null) { - return Task.FromResult( - "Сессия не найдена. Отправьте любое сообщение для создания новой сессии." - ); + return "Сессия не найдена. Отправьте любое сообщение для создания новой сессии."; } var compressionStatus = _aiSettings.EnableHistoryCompression ? "Включено" : "Отключено"; @@ -42,15 +40,13 @@ namespace ChatBot.Services.Telegram.Commands ? $"\nСжатие истории: {compressionStatus}\nПорог сжатия: {_aiSettings.CompressionThreshold} сообщений\nЦелевое количество: {_aiSettings.CompressionTarget} сообщений" : $"\nСжатие истории: {compressionStatus}"; - return Task.FromResult( - $"Настройки чата:\n" - + $"Тип чата: {session.ChatType}\n" - + $"Название: {session.ChatTitle}\n" - + $"Модель: {session.Model}\n" - + $"Сообщений в истории: {session.GetMessageCount()}\n" - + $"Создана: {session.CreatedAt:dd.MM.yyyy HH:mm}" - + compressionInfo - ); + return $"Настройки чата:\n" + + $"Тип чата: {session.ChatType}\n" + + $"Название: {session.ChatTitle}\n" + + $"Модель: {session.Model}\n" + + $"Сообщений в истории: {session.GetMessageCount()}\n" + + $"Создана: {session.CreatedAt:dd.MM.yyyy HH:mm}" + + compressionInfo; } } } diff --git a/ChatBot/Services/Telegram/Commands/StatusCommand.cs b/ChatBot/Services/Telegram/Commands/StatusCommand.cs index a728917..08c1ec8 100644 --- a/ChatBot/Services/Telegram/Commands/StatusCommand.cs +++ b/ChatBot/Services/Telegram/Commands/StatusCommand.cs @@ -40,7 +40,7 @@ namespace ChatBot.Services.Telegram.Commands statusBuilder.AppendLine(); // Информация о сессии - var session = _chatService.GetSession(context.ChatId); + var session = await _chatService.GetSessionAsync(context.ChatId); if (session != null) { statusBuilder.AppendLine($"📊 **Сессия:**"); diff --git a/ChatBot/Services/Telegram/Commands/TelegramCommandProcessor.cs b/ChatBot/Services/Telegram/Commands/TelegramCommandProcessor.cs index 5cde7dd..6dbdd99 100644 --- a/ChatBot/Services/Telegram/Commands/TelegramCommandProcessor.cs +++ b/ChatBot/Services/Telegram/Commands/TelegramCommandProcessor.cs @@ -39,6 +39,23 @@ namespace ChatBot.Services.Telegram.Commands CancellationToken cancellationToken = default ) { + // Input validation + if (string.IsNullOrWhiteSpace(messageText)) + { + _logger.LogWarning("Empty message received for chat {ChatId}", chatId); + return string.Empty; + } + + if (chatId == 0) + { + _logger.LogError("Invalid chatId (0) received"); + throw new ArgumentException("ChatId cannot be 0", nameof(chatId)); + } + + username = username ?? "Unknown"; + chatType = chatType ?? "private"; + chatTitle = chatTitle ?? string.Empty; + try { // Получаем информацию о боте @@ -121,10 +138,25 @@ namespace ChatBot.Services.Telegram.Commands cancellationToken ); } + catch (HttpRequestException ex) + { + _logger.LogError(ex, "Network error processing message for chat {ChatId}", chatId); + return "Ошибка сети при обработке сообщения. Проверьте подключение к AI сервису."; + } + catch (TaskCanceledException ex) + { + _logger.LogWarning(ex, "Request timeout for chat {ChatId}", chatId); + return "Превышено время ожидания ответа. Попробуйте еще раз."; + } + catch (InvalidOperationException ex) + { + _logger.LogError(ex, "Invalid operation for chat {ChatId}", chatId); + return "Ошибка в работе системы. Попробуйте позже."; + } catch (Exception ex) { - _logger.LogError(ex, "Error processing message for chat {ChatId}", chatId); - return "Произошла ошибка при обработке сообщения. Попробуйте еще раз."; + _logger.LogError(ex, "Unexpected error processing message for chat {ChatId}", chatId); + return "Произошла непредвиденная ошибка. Попробуйте еще раз."; } } }