add more tests
This commit is contained in:
@@ -315,4 +315,477 @@ public class ChatServiceTests : UnitTestBase
|
||||
result.Should().Be(expectedCleaned);
|
||||
_sessionStorageMock.Verify(x => x.CleanupOldSessions(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.GetOrCreate(It.IsAny<long>(), It.IsAny<string>(), It.IsAny<string>()))
|
||||
.Throws(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_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.Get(chatId)).Returns(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.Get(chatId)).Returns(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 void CleanupOldSessions_ShouldHandleInvalidHoursOld(int hoursOld)
|
||||
{
|
||||
// Arrange
|
||||
var expectedCleaned = 0;
|
||||
_sessionStorageMock.Setup(x => x.CleanupOldSessions(hoursOld)).Returns(expectedCleaned);
|
||||
|
||||
// Act
|
||||
var result = _chatService.CleanupOldSessions(hoursOld);
|
||||
|
||||
// Assert
|
||||
result.Should().Be(expectedCleaned);
|
||||
_sessionStorageMock.Verify(x => x.CleanupOldSessions(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.GetOrCreate(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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user