Files
ChatBot/ChatBot.Tests/Services/ChatServiceTests.cs
Leonid Pershin 1996fec14f
All checks were successful
SonarQube / Build and analyze (push) Successful in 2m54s
Add promt fix tests
2025-10-21 12:07:56 +03:00

860 lines
28 KiB
C#

using ChatBot.Models.Configuration;
using ChatBot.Services;
using ChatBot.Services.Interfaces;
using ChatBot.Tests.TestUtilities;
using FluentAssertions;
using Microsoft.Extensions.Logging;
using Moq;
namespace ChatBot.Tests.Services;
public class ChatServiceTests : UnitTestBase
{
private readonly Mock<ILogger<ChatService>> _loggerMock;
private readonly Mock<IAIService> _aiServiceMock;
private readonly Mock<ISessionStorage> _sessionStorageMock;
private readonly Mock<IHistoryCompressionService> _compressionServiceMock;
private readonly AISettings _aiSettings;
private readonly ChatService _chatService;
public ChatServiceTests()
{
_loggerMock = TestDataBuilder.Mocks.CreateLoggerMock<ChatService>();
_aiServiceMock = TestDataBuilder.Mocks.CreateAIServiceMock();
_sessionStorageMock = TestDataBuilder.Mocks.CreateSessionStorageMock();
_compressionServiceMock = TestDataBuilder.Mocks.CreateCompressionServiceMock();
_aiSettings = TestDataBuilder.Configurations.CreateAISettings();
var optionsMock = TestDataBuilder.Mocks.CreateOptionsMock(_aiSettings);
_chatService = new ChatService(
_loggerMock.Object,
_aiServiceMock.Object,
_sessionStorageMock.Object,
optionsMock.Object,
_compressionServiceMock.Object
);
}
[Fact]
public async Task GetOrCreateSession_ShouldCreateNewSession_WhenSessionDoesNotExist()
{
// Arrange
var chatId = 12345L;
var chatType = "private";
var chatTitle = "Test Chat";
// Act
var session = await _chatService.GetOrCreateSessionAsync(chatId, chatType, chatTitle);
// Assert
session.Should().NotBeNull();
session.ChatId.Should().Be(chatId);
session.ChatType.Should().Be(chatType);
session.ChatTitle.Should().Be(chatTitle);
_sessionStorageMock.Verify(x => x.GetOrCreateAsync(chatId, chatType, chatTitle), Times.Once);
}
[Fact]
public async Task GetOrCreateSession_ShouldSetCompressionService_WhenCompressionIsEnabled()
{
// Arrange
var chatId = 12345L;
_aiSettings.EnableHistoryCompression = true;
// Act
var session = await _chatService.GetOrCreateSessionAsync(chatId);
// Assert
session.Should().NotBeNull();
_sessionStorageMock.Verify(x => x.GetOrCreateAsync(chatId, "private", ""), Times.Once);
}
[Fact]
public async Task ProcessMessageAsync_ShouldProcessMessageSuccessfully_WhenValidInput()
{
// Arrange
var chatId = 12345L;
var username = "testuser";
var message = "Hello, bot!";
var expectedResponse = "Hello! How can I help you?";
_aiServiceMock
.Setup(x =>
x.GenerateChatCompletionWithCompressionAsync(
It.IsAny<List<ChatBot.Models.Dto.ChatMessage>>(),
It.IsAny<CancellationToken>()
)
)
.ReturnsAsync(expectedResponse);
// Act
var result = await _chatService.ProcessMessageAsync(chatId, username, message);
// Assert
result.Should().Be(expectedResponse);
_sessionStorageMock.Verify(
x => x.SaveSessionAsync(It.IsAny<ChatBot.Models.ChatSession>()),
Times.AtLeastOnce
);
_aiServiceMock.Verify(
x =>
x.GenerateChatCompletionWithCompressionAsync(
It.IsAny<List<ChatBot.Models.Dto.ChatMessage>>(),
It.IsAny<CancellationToken>()
),
Times.Once
);
}
[Fact]
public async Task ProcessMessageAsync_ShouldReturnEmptyString_WhenAIResponseIsEmptyMarker()
{
// Arrange
var chatId = 12345L;
var username = "testuser";
var message = "Hello, bot!";
var emptyResponse = "{empty}";
_aiServiceMock
.Setup(x =>
x.GenerateChatCompletionWithCompressionAsync(
It.IsAny<List<ChatBot.Models.Dto.ChatMessage>>(),
It.IsAny<CancellationToken>()
)
)
.ReturnsAsync(emptyResponse);
// Act
var result = await _chatService.ProcessMessageAsync(chatId, username, message);
// Assert
result.Should().BeEmpty();
}
[Fact]
public async Task ProcessMessageAsync_ShouldReturnErrorMessage_WhenAIResponseIsNull()
{
// Arrange
var chatId = 12345L;
var username = "testuser";
var message = "Hello, bot!";
_aiServiceMock
.Setup(x =>
x.GenerateChatCompletionWithCompressionAsync(
It.IsAny<List<ChatBot.Models.Dto.ChatMessage>>(),
It.IsAny<CancellationToken>()
)
)
.ReturnsAsync((string)null!);
// Act
var result = await _chatService.ProcessMessageAsync(chatId, username, message);
// Assert
result.Should().Be("Извините, произошла ошибка при генерации ответа.");
}
[Fact]
public async Task ProcessMessageAsync_ShouldHandleException_WhenErrorOccurs()
{
// Arrange
var chatId = 12345L;
var username = "testuser";
var message = "Hello, bot!";
_aiServiceMock
.Setup(x =>
x.GenerateChatCompletionWithCompressionAsync(
It.IsAny<List<ChatBot.Models.Dto.ChatMessage>>(),
It.IsAny<CancellationToken>()
)
)
.ThrowsAsync(new Exception("Test exception"));
// Act
var result = await _chatService.ProcessMessageAsync(chatId, username, message);
// Assert
result.Should().Be("Извините, произошла ошибка при обработке вашего сообщения.");
// Verify that error was logged
// In a real test, we would verify the logger calls
}
[Fact]
public async Task UpdateSessionParametersAsync_ShouldUpdateSession_WhenSessionExists()
{
// Arrange
var chatId = 12345L;
var newModel = "llama3.2";
var session = TestDataBuilder.ChatSessions.CreateBasicSession(chatId);
_sessionStorageMock.Setup(x => x.GetAsync(chatId)).ReturnsAsync(session);
// Act
await _chatService.UpdateSessionParametersAsync(chatId, newModel);
// Assert
session.Model.Should().Be(newModel);
_sessionStorageMock.Verify(x => x.SaveSessionAsync(session), Times.Once);
}
[Fact]
public async Task UpdateSessionParametersAsync_ShouldNotUpdate_WhenSessionDoesNotExist()
{
// Arrange
var chatId = 12345L;
var newModel = "llama3.2";
_sessionStorageMock.Setup(x => x.GetAsync(chatId)).ReturnsAsync((ChatBot.Models.ChatSession?)null);
// Act
await _chatService.UpdateSessionParametersAsync(chatId, newModel);
// Assert
_sessionStorageMock.Verify(
x => x.SaveSessionAsync(It.IsAny<ChatBot.Models.ChatSession>()),
Times.Never
);
}
[Fact]
public async Task ClearHistoryAsync_ShouldClearHistory_WhenSessionExists()
{
// Arrange
var chatId = 12345L;
var session = TestDataBuilder.ChatSessions.CreateSessionWithMessages(chatId, 5);
_sessionStorageMock.Setup(x => x.GetAsync(chatId)).ReturnsAsync(session);
// Act
await _chatService.ClearHistoryAsync(chatId);
// Assert
session.GetMessageCount().Should().Be(0);
_sessionStorageMock.Verify(x => x.SaveSessionAsync(session), Times.Once);
}
[Fact]
public async Task GetSession_ShouldReturnSession_WhenSessionExists()
{
// Arrange
var chatId = 12345L;
var session = TestDataBuilder.ChatSessions.CreateBasicSession(chatId);
_sessionStorageMock.Setup(x => x.GetAsync(chatId)).ReturnsAsync(session);
// Act
var result = await _chatService.GetSessionAsync(chatId);
// Assert
result.Should().Be(session);
}
[Fact]
public async Task GetSession_ShouldReturnNull_WhenSessionDoesNotExist()
{
// Arrange
var chatId = 12345L;
_sessionStorageMock.Setup(x => x.GetAsync(chatId)).ReturnsAsync((ChatBot.Models.ChatSession?)null);
// Act
var result = await _chatService.GetSessionAsync(chatId);
// Assert
result.Should().BeNull();
}
[Fact]
public async Task RemoveSession_ShouldReturnTrue_WhenSessionExists()
{
// Arrange
var chatId = 12345L;
_sessionStorageMock.Setup(x => x.RemoveAsync(chatId)).ReturnsAsync(true);
// Act
var result = await _chatService.RemoveSessionAsync(chatId);
// Assert
result.Should().BeTrue();
_sessionStorageMock.Verify(x => x.RemoveAsync(chatId), Times.Once);
}
[Fact]
public async Task GetActiveSessionsCount_ShouldReturnCorrectCount()
{
// Arrange
var expectedCount = 5;
_sessionStorageMock.Setup(x => x.GetActiveSessionsCountAsync()).ReturnsAsync(expectedCount);
// Act
var result = await _chatService.GetActiveSessionsCountAsync();
// Assert
result.Should().Be(expectedCount);
}
[Fact]
public async Task CleanupOldSessions_ShouldReturnCleanedCount()
{
// Arrange
var hoursOld = 24;
var expectedCleaned = 3;
_sessionStorageMock.Setup(x => x.CleanupOldSessionsAsync(hoursOld)).ReturnsAsync(expectedCleaned);
// Act
var result = await _chatService.CleanupOldSessionsAsync(hoursOld);
// Assert
result.Should().Be(expectedCleaned);
_sessionStorageMock.Verify(x => x.CleanupOldSessionsAsync(hoursOld), Times.Once);
}
[Theory]
[InlineData("")]
[InlineData(" ")]
[InlineData(null!)]
public async Task ProcessMessageAsync_ShouldHandleEmptyOrNullMessage(string? message)
{
// Arrange
var chatId = 12345L;
var username = "testuser";
var expectedResponse = "Hello! How can I help you?";
_aiServiceMock
.Setup(x =>
x.GenerateChatCompletionWithCompressionAsync(
It.IsAny<List<ChatBot.Models.Dto.ChatMessage>>(),
It.IsAny<CancellationToken>()
)
)
.ReturnsAsync(expectedResponse);
// Act
var result = await _chatService.ProcessMessageAsync(
chatId,
username,
message ?? string.Empty
);
// Assert
result.Should().Be(expectedResponse);
_sessionStorageMock.Verify(
x => x.SaveSessionAsync(It.IsAny<ChatBot.Models.ChatSession>()),
Times.AtLeastOnce
);
}
[Theory]
[InlineData("")]
[InlineData(" ")]
[InlineData(null!)]
public async Task ProcessMessageAsync_ShouldHandleEmptyOrNullUsername(string? username)
{
// Arrange
var chatId = 12345L;
var message = "Hello, bot!";
var expectedResponse = "Hello! How can I help you?";
_aiServiceMock
.Setup(x =>
x.GenerateChatCompletionWithCompressionAsync(
It.IsAny<List<ChatBot.Models.Dto.ChatMessage>>(),
It.IsAny<CancellationToken>()
)
)
.ReturnsAsync(expectedResponse);
// Act
var result = await _chatService.ProcessMessageAsync(
chatId,
username ?? string.Empty,
message
);
// Assert
result.Should().Be(expectedResponse);
_sessionStorageMock.Verify(
x => x.SaveSessionAsync(It.IsAny<ChatBot.Models.ChatSession>()),
Times.AtLeastOnce
);
}
[Fact]
public async Task ProcessMessageAsync_ShouldHandleSessionStorageException()
{
// Arrange
var chatId = 12345L;
var username = "testuser";
var message = "Hello, bot!";
_sessionStorageMock
.Setup(x => x.GetOrCreateAsync(It.IsAny<long>(), It.IsAny<string>(), It.IsAny<string>()))
.ThrowsAsync(new Exception("Database connection failed"));
// Act
var result = await _chatService.ProcessMessageAsync(chatId, username, message);
// Assert
result.Should().Be("Извините, произошла ошибка при обработке вашего сообщения.");
_loggerMock.Verify(
x =>
x.Log(
LogLevel.Error,
It.IsAny<EventId>(),
It.Is<It.IsAnyType>(
(v, t) => v.ToString()!.Contains("Error processing message")
),
It.IsAny<Exception>(),
It.IsAny<Func<It.IsAnyType, Exception?, string>>()
),
Times.Once
);
}
[Fact]
public async Task ProcessMessageAsync_ShouldHandleAIServiceException()
{
// Arrange
var chatId = 12345L;
var username = "testuser";
var message = "Hello, bot!";
_aiServiceMock
.Setup(x =>
x.GenerateChatCompletionWithCompressionAsync(
It.IsAny<List<ChatBot.Models.Dto.ChatMessage>>(),
It.IsAny<CancellationToken>()
)
)
.ThrowsAsync(new HttpRequestException("AI service unavailable"));
// Act
var result = await _chatService.ProcessMessageAsync(chatId, username, message);
// Assert
result.Should().Be("Извините, произошла ошибка при обработке вашего сообщения.");
_loggerMock.Verify(
x =>
x.Log(
LogLevel.Error,
It.IsAny<EventId>(),
It.Is<It.IsAnyType>(
(v, t) => v.ToString()!.Contains("Error processing message")
),
It.IsAny<Exception>(),
It.IsAny<Func<It.IsAnyType, Exception?, string>>()
),
Times.Once
);
}
[Fact]
public async Task ProcessMessageAsync_ShouldHandleCancellationToken()
{
// Arrange
var chatId = 12345L;
var username = "testuser";
var message = "Hello, bot!";
var cts = new CancellationTokenSource();
cts.Cancel(); // Cancel immediately
// Setup AI service to throw OperationCanceledException when cancellation is requested
_aiServiceMock
.Setup(x =>
x.GenerateChatCompletionWithCompressionAsync(
It.IsAny<List<ChatBot.Models.Dto.ChatMessage>>(),
It.IsAny<CancellationToken>()
)
)
.ThrowsAsync(new OperationCanceledException("Operation was canceled"));
// Act
var result = await _chatService.ProcessMessageAsync(
chatId,
username,
message,
cancellationToken: cts.Token
);
// Assert
result.Should().Be("Извините, произошла ошибка при обработке вашего сообщения.");
}
[Fact]
public async Task ProcessMessageAsync_ShouldLogCorrectInformation()
{
// Arrange
var chatId = 12345L;
var username = "testuser";
var message = "Hello, bot!";
var expectedResponse = "Hello! How can I help you?";
_aiServiceMock
.Setup(x =>
x.GenerateChatCompletionWithCompressionAsync(
It.IsAny<List<ChatBot.Models.Dto.ChatMessage>>(),
It.IsAny<CancellationToken>()
)
)
.ReturnsAsync(expectedResponse);
// Act
var result = await _chatService.ProcessMessageAsync(
chatId,
username,
message,
"group",
"Test Group"
);
// Assert
result.Should().Be(expectedResponse);
_loggerMock.Verify(
x =>
x.Log(
LogLevel.Information,
It.IsAny<EventId>(),
It.Is<It.IsAnyType>(
(v, t) =>
v.ToString()!
.Contains(
"Processing message from user testuser in chat 12345 (group): Hello, bot!"
)
),
It.IsAny<Exception>(),
It.IsAny<Func<It.IsAnyType, Exception?, string>>()
),
Times.Once
);
}
[Fact]
public async Task ProcessMessageAsync_ShouldLogDebugForResponseLength()
{
// Arrange
var chatId = 12345L;
var username = "testuser";
var message = "Hello, bot!";
var expectedResponse = "Hello! How can I help you?";
_aiServiceMock
.Setup(x =>
x.GenerateChatCompletionWithCompressionAsync(
It.IsAny<List<ChatBot.Models.Dto.ChatMessage>>(),
It.IsAny<CancellationToken>()
)
)
.ReturnsAsync(expectedResponse);
// Act
var result = await _chatService.ProcessMessageAsync(chatId, username, message);
// Assert
result.Should().Be(expectedResponse);
_loggerMock.Verify(
x =>
x.Log(
LogLevel.Debug,
It.IsAny<EventId>(),
It.Is<It.IsAnyType>(
(v, t) =>
v.ToString()!.Contains("AI response generated for chat 12345 (length:")
),
It.IsAny<Exception>(),
It.IsAny<Func<It.IsAnyType, Exception?, string>>()
),
Times.Once
);
}
[Fact]
public async Task ProcessMessageAsync_ShouldTrimHistory_WhenCompressionDisabled()
{
// Arrange
var chatId = 99999L;
_aiSettings.EnableHistoryCompression = false;
var session = TestDataBuilder.ChatSessions.CreateBasicSession(chatId);
session.MaxHistoryLength = 5; // force small history limit
_sessionStorageMock
.Setup(x => x.GetOrCreateAsync(chatId, It.IsAny<string>(), It.IsAny<string>()))
.ReturnsAsync(session);
_sessionStorageMock.Setup(x => x.GetAsync(chatId)).ReturnsAsync(session);
_aiServiceMock
.Setup(x =>
x.GenerateChatCompletionWithCompressionAsync(
It.IsAny<List<ChatBot.Models.Dto.ChatMessage>>(),
It.IsAny<CancellationToken>()
)
)
.ReturnsAsync("ok");
// Act: add many messages to exceed the limit
for (int i = 0; i < 10; i++)
{
await _chatService.ProcessMessageAsync(chatId, "u", $"m{i}");
}
// Assert: history trimmed to MaxHistoryLength
session.GetMessageCount().Should().BeLessThanOrEqualTo(5);
}
[Fact]
public async Task ProcessMessageAsync_ShouldCompressHistory_WhenCompressionEnabledAndThresholdExceeded()
{
// Arrange
var chatId = 88888L;
_aiSettings.EnableHistoryCompression = true;
_aiSettings.CompressionThreshold = 4;
_aiSettings.CompressionTarget = 3;
var session = TestDataBuilder.ChatSessions.CreateBasicSession(chatId);
session.MaxHistoryLength = 50; // avoid trimming impacting compression assertion
_sessionStorageMock
.Setup(x => x.GetOrCreateAsync(chatId, It.IsAny<string>(), It.IsAny<string>()))
.ReturnsAsync(session);
_sessionStorageMock.Setup(x => x.GetAsync(chatId)).ReturnsAsync(session);
_aiServiceMock
.Setup(x =>
x.GenerateChatCompletionWithCompressionAsync(
It.IsAny<List<ChatBot.Models.Dto.ChatMessage>>(),
It.IsAny<CancellationToken>()
)
)
.ReturnsAsync("ok");
// Act: add enough messages to exceed threshold
for (int i = 0; i < 6; i++)
{
await _chatService.ProcessMessageAsync(chatId, "u", $"m{i}");
}
// Assert: compressed to target (user+assistant messages should be around target)
session.GetMessageCount().Should().BeLessThanOrEqualTo(_aiSettings.CompressionTarget + 1);
}
[Fact]
public async Task ProcessMessageAsync_ShouldLogEmptyResponseMarker()
{
// Arrange
var chatId = 12345L;
var username = "testuser";
var message = "Hello, bot!";
var emptyResponse = "{empty}";
_aiServiceMock
.Setup(x =>
x.GenerateChatCompletionWithCompressionAsync(
It.IsAny<List<ChatBot.Models.Dto.ChatMessage>>(),
It.IsAny<CancellationToken>()
)
)
.ReturnsAsync(emptyResponse);
// Act
var result = await _chatService.ProcessMessageAsync(chatId, username, message);
// Assert
result.Should().BeEmpty();
_loggerMock.Verify(
x =>
x.Log(
LogLevel.Information,
It.IsAny<EventId>(),
It.Is<It.IsAnyType>(
(v, t) =>
v.ToString()!
.Contains(
"AI returned empty response marker for chat 12345, ignoring message"
)
),
It.IsAny<Exception>(),
It.IsAny<Func<It.IsAnyType, Exception?, string>>()
),
Times.Once
);
}
[Fact]
public async Task UpdateSessionParametersAsync_ShouldHandleSessionStorageException()
{
// Arrange
var chatId = 12345L;
var newModel = "llama3.2";
var session = TestDataBuilder.ChatSessions.CreateBasicSession(chatId);
_sessionStorageMock.Setup(x => x.GetAsync(chatId)).ReturnsAsync(session);
_sessionStorageMock
.Setup(x => x.SaveSessionAsync(It.IsAny<ChatBot.Models.ChatSession>()))
.ThrowsAsync(new Exception("Database save failed"));
// Act & Assert
var act = async () => await _chatService.UpdateSessionParametersAsync(chatId, newModel);
await act.Should().ThrowAsync<Exception>().WithMessage("Database save failed");
}
[Fact]
public async Task ClearHistoryAsync_ShouldHandleSessionStorageException()
{
// Arrange
var chatId = 12345L;
var session = TestDataBuilder.ChatSessions.CreateSessionWithMessages(chatId, 5);
_sessionStorageMock.Setup(x => x.GetAsync(chatId)).ReturnsAsync(session);
_sessionStorageMock
.Setup(x => x.SaveSessionAsync(It.IsAny<ChatBot.Models.ChatSession>()))
.ThrowsAsync(new Exception("Database save failed"));
// Act & Assert
var act = async () => await _chatService.ClearHistoryAsync(chatId);
await act.Should().ThrowAsync<Exception>().WithMessage("Database save failed");
}
[Theory]
[InlineData(0)]
[InlineData(-1)]
[InlineData(int.MinValue)]
public async Task CleanupOldSessions_ShouldHandleInvalidHoursOld(int hoursOld)
{
// Arrange
var expectedCleaned = 0;
_sessionStorageMock.Setup(x => x.CleanupOldSessionsAsync(hoursOld)).ReturnsAsync(expectedCleaned);
// Act
var result = await _chatService.CleanupOldSessionsAsync(hoursOld);
// Assert
result.Should().Be(expectedCleaned);
_sessionStorageMock.Verify(x => x.CleanupOldSessionsAsync(hoursOld), Times.Once);
}
[Theory]
[InlineData(long.MaxValue)]
[InlineData(long.MinValue)]
[InlineData(0)]
[InlineData(-1)]
public async Task ProcessMessageAsync_ShouldHandleExtremeChatIds(long chatId)
{
// Arrange
var username = "testuser";
var message = "Hello, bot!";
var expectedResponse = "Hello! How can I help you?";
_aiServiceMock
.Setup(x =>
x.GenerateChatCompletionWithCompressionAsync(
It.IsAny<List<ChatBot.Models.Dto.ChatMessage>>(),
It.IsAny<CancellationToken>()
)
)
.ReturnsAsync(expectedResponse);
// Act
var result = await _chatService.ProcessMessageAsync(chatId, username, message);
// Assert
result.Should().Be(expectedResponse);
_sessionStorageMock.Verify(x => x.GetOrCreateAsync(chatId, "private", ""), Times.Once);
}
[Fact]
public async Task ProcessMessageAsync_ShouldHandleVeryLongMessage()
{
// Arrange
var chatId = 12345L;
var username = "testuser";
var veryLongMessage = new string('A', 10000); // Very long message
var expectedResponse = "Hello! How can I help you?";
_aiServiceMock
.Setup(x =>
x.GenerateChatCompletionWithCompressionAsync(
It.IsAny<List<ChatBot.Models.Dto.ChatMessage>>(),
It.IsAny<CancellationToken>()
)
)
.ReturnsAsync(expectedResponse);
// Act
var result = await _chatService.ProcessMessageAsync(chatId, username, veryLongMessage);
// Assert
result.Should().Be(expectedResponse);
_sessionStorageMock.Verify(
x => x.SaveSessionAsync(It.IsAny<ChatBot.Models.ChatSession>()),
Times.AtLeastOnce
);
}
[Fact]
public async Task ProcessMessageAsync_ShouldHandleVeryLongUsername()
{
// Arrange
var chatId = 12345L;
var veryLongUsername = new string('U', 1000); // Very long username
var message = "Hello, bot!";
var expectedResponse = "Hello! How can I help you?";
_aiServiceMock
.Setup(x =>
x.GenerateChatCompletionWithCompressionAsync(
It.IsAny<List<ChatBot.Models.Dto.ChatMessage>>(),
It.IsAny<CancellationToken>()
)
)
.ReturnsAsync(expectedResponse);
// Act
var result = await _chatService.ProcessMessageAsync(chatId, veryLongUsername, message);
// Assert
result.Should().Be(expectedResponse);
_sessionStorageMock.Verify(
x => x.SaveSessionAsync(It.IsAny<ChatBot.Models.ChatSession>()),
Times.AtLeastOnce
);
}
[Fact]
public async Task ProcessMessageAsync_ShouldHandleCompressionServiceException()
{
// Arrange
var chatId = 12345L;
var username = "testuser";
var message = "Hello, bot!";
_aiSettings.EnableHistoryCompression = true;
_compressionServiceMock
.Setup(x => x.ShouldCompress(It.IsAny<int>(), It.IsAny<int>()))
.Throws(new Exception("Compression service failed"));
// Act
var result = await _chatService.ProcessMessageAsync(chatId, username, message);
// Assert
result.Should().Be("Извините, произошла ошибка при обработке вашего сообщения.");
_loggerMock.Verify(
x =>
x.Log(
LogLevel.Error,
It.IsAny<EventId>(),
It.Is<It.IsAnyType>(
(v, t) => v.ToString()!.Contains("Error processing message")
),
It.IsAny<Exception>(),
It.IsAny<Func<It.IsAnyType, Exception?, string>>()
),
Times.Once
);
}
}