add more tests
Some checks failed
SonarQube / Build and analyze (push) Failing after 2m56s
Unit Tests / Run Tests (push) Failing after 2m28s

This commit is contained in:
Leonid Pershin
2025-10-20 07:02:12 +03:00
parent af9773e7d6
commit 1647fe19d3
12 changed files with 3714 additions and 21 deletions

View File

@@ -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
);
}
}