Add more test
This commit is contained in:
473
ChatBot.Tests/Data/Interfaces/IChatSessionRepositoryTests.cs
Normal file
473
ChatBot.Tests/Data/Interfaces/IChatSessionRepositoryTests.cs
Normal file
@@ -0,0 +1,473 @@
|
|||||||
|
using ChatBot.Data.Interfaces;
|
||||||
|
using ChatBot.Data.Repositories;
|
||||||
|
using ChatBot.Models.Entities;
|
||||||
|
using ChatBot.Tests.TestUtilities;
|
||||||
|
using FluentAssertions;
|
||||||
|
using Moq;
|
||||||
|
|
||||||
|
namespace ChatBot.Tests.Data.Interfaces;
|
||||||
|
|
||||||
|
public class IChatSessionRepositoryTests : UnitTestBase
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void IChatSessionRepository_ShouldHaveCorrectMethodSignatures()
|
||||||
|
{
|
||||||
|
// Arrange & Act
|
||||||
|
var interfaceType = typeof(IChatSessionRepository);
|
||||||
|
var methods = interfaceType.GetMethods();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
methods.Should().HaveCount(11);
|
||||||
|
|
||||||
|
// GetOrCreateAsync method
|
||||||
|
var getOrCreateAsyncMethod = methods.FirstOrDefault(m => m.Name == "GetOrCreateAsync");
|
||||||
|
getOrCreateAsyncMethod.Should().NotBeNull();
|
||||||
|
getOrCreateAsyncMethod!.ReturnType.Should().Be(typeof(Task<ChatSessionEntity>));
|
||||||
|
getOrCreateAsyncMethod.GetParameters().Should().HaveCount(3);
|
||||||
|
getOrCreateAsyncMethod.GetParameters()[0].ParameterType.Should().Be(typeof(long));
|
||||||
|
getOrCreateAsyncMethod.GetParameters()[1].ParameterType.Should().Be(typeof(string));
|
||||||
|
getOrCreateAsyncMethod.GetParameters()[2].ParameterType.Should().Be(typeof(string));
|
||||||
|
|
||||||
|
// GetByChatIdAsync method
|
||||||
|
var getByChatIdAsyncMethod = methods.FirstOrDefault(m => m.Name == "GetByChatIdAsync");
|
||||||
|
getByChatIdAsyncMethod.Should().NotBeNull();
|
||||||
|
getByChatIdAsyncMethod!.ReturnType.Should().Be(typeof(Task<ChatSessionEntity?>));
|
||||||
|
getByChatIdAsyncMethod.GetParameters().Should().HaveCount(1);
|
||||||
|
getByChatIdAsyncMethod.GetParameters()[0].ParameterType.Should().Be(typeof(long));
|
||||||
|
|
||||||
|
// GetBySessionIdAsync method
|
||||||
|
var getBySessionIdAsyncMethod = methods.FirstOrDefault(m =>
|
||||||
|
m.Name == "GetBySessionIdAsync"
|
||||||
|
);
|
||||||
|
getBySessionIdAsyncMethod.Should().NotBeNull();
|
||||||
|
getBySessionIdAsyncMethod!.ReturnType.Should().Be(typeof(Task<ChatSessionEntity?>));
|
||||||
|
getBySessionIdAsyncMethod.GetParameters().Should().HaveCount(1);
|
||||||
|
getBySessionIdAsyncMethod.GetParameters()[0].ParameterType.Should().Be(typeof(string));
|
||||||
|
|
||||||
|
// UpdateAsync method
|
||||||
|
var updateAsyncMethod = methods.FirstOrDefault(m => m.Name == "UpdateAsync");
|
||||||
|
updateAsyncMethod.Should().NotBeNull();
|
||||||
|
updateAsyncMethod!.ReturnType.Should().Be(typeof(Task<ChatSessionEntity>));
|
||||||
|
updateAsyncMethod.GetParameters().Should().HaveCount(1);
|
||||||
|
updateAsyncMethod.GetParameters()[0].ParameterType.Should().Be(typeof(ChatSessionEntity));
|
||||||
|
|
||||||
|
// DeleteAsync method
|
||||||
|
var deleteAsyncMethod = methods.FirstOrDefault(m => m.Name == "DeleteAsync");
|
||||||
|
deleteAsyncMethod.Should().NotBeNull();
|
||||||
|
deleteAsyncMethod!.ReturnType.Should().Be(typeof(Task<bool>));
|
||||||
|
deleteAsyncMethod.GetParameters().Should().HaveCount(1);
|
||||||
|
deleteAsyncMethod.GetParameters()[0].ParameterType.Should().Be(typeof(long));
|
||||||
|
|
||||||
|
// GetMessagesAsync method
|
||||||
|
var getMessagesAsyncMethod = methods.FirstOrDefault(m => m.Name == "GetMessagesAsync");
|
||||||
|
getMessagesAsyncMethod.Should().NotBeNull();
|
||||||
|
getMessagesAsyncMethod!.ReturnType.Should().Be(typeof(Task<List<ChatMessageEntity>>));
|
||||||
|
getMessagesAsyncMethod.GetParameters().Should().HaveCount(1);
|
||||||
|
getMessagesAsyncMethod.GetParameters()[0].ParameterType.Should().Be(typeof(int));
|
||||||
|
|
||||||
|
// AddMessageAsync method
|
||||||
|
var addMessageAsyncMethod = methods.FirstOrDefault(m => m.Name == "AddMessageAsync");
|
||||||
|
addMessageAsyncMethod.Should().NotBeNull();
|
||||||
|
addMessageAsyncMethod!.ReturnType.Should().Be(typeof(Task<ChatMessageEntity>));
|
||||||
|
addMessageAsyncMethod.GetParameters().Should().HaveCount(4);
|
||||||
|
addMessageAsyncMethod.GetParameters()[0].ParameterType.Should().Be(typeof(int));
|
||||||
|
addMessageAsyncMethod.GetParameters()[1].ParameterType.Should().Be(typeof(string));
|
||||||
|
addMessageAsyncMethod.GetParameters()[2].ParameterType.Should().Be(typeof(string));
|
||||||
|
addMessageAsyncMethod.GetParameters()[3].ParameterType.Should().Be(typeof(int));
|
||||||
|
|
||||||
|
// ClearMessagesAsync method
|
||||||
|
var clearMessagesAsyncMethod = methods.FirstOrDefault(m => m.Name == "ClearMessagesAsync");
|
||||||
|
clearMessagesAsyncMethod.Should().NotBeNull();
|
||||||
|
clearMessagesAsyncMethod!.ReturnType.Should().Be(typeof(Task));
|
||||||
|
clearMessagesAsyncMethod.GetParameters().Should().HaveCount(1);
|
||||||
|
clearMessagesAsyncMethod.GetParameters()[0].ParameterType.Should().Be(typeof(int));
|
||||||
|
|
||||||
|
// GetActiveSessionsCountAsync method
|
||||||
|
var getActiveSessionsCountAsyncMethod = methods.FirstOrDefault(m =>
|
||||||
|
m.Name == "GetActiveSessionsCountAsync"
|
||||||
|
);
|
||||||
|
getActiveSessionsCountAsyncMethod.Should().NotBeNull();
|
||||||
|
getActiveSessionsCountAsyncMethod!.ReturnType.Should().Be(typeof(Task<int>));
|
||||||
|
getActiveSessionsCountAsyncMethod.GetParameters().Should().BeEmpty();
|
||||||
|
|
||||||
|
// CleanupOldSessionsAsync method
|
||||||
|
var cleanupOldSessionsAsyncMethod = methods.FirstOrDefault(m =>
|
||||||
|
m.Name == "CleanupOldSessionsAsync"
|
||||||
|
);
|
||||||
|
cleanupOldSessionsAsyncMethod.Should().NotBeNull();
|
||||||
|
cleanupOldSessionsAsyncMethod!.ReturnType.Should().Be(typeof(Task<int>));
|
||||||
|
cleanupOldSessionsAsyncMethod.GetParameters().Should().HaveCount(1);
|
||||||
|
cleanupOldSessionsAsyncMethod.GetParameters()[0].ParameterType.Should().Be(typeof(int));
|
||||||
|
|
||||||
|
// GetSessionsForCleanupAsync method
|
||||||
|
var getSessionsForCleanupAsyncMethod = methods.FirstOrDefault(m =>
|
||||||
|
m.Name == "GetSessionsForCleanupAsync"
|
||||||
|
);
|
||||||
|
getSessionsForCleanupAsyncMethod.Should().NotBeNull();
|
||||||
|
getSessionsForCleanupAsyncMethod!
|
||||||
|
.ReturnType.Should()
|
||||||
|
.Be(typeof(Task<List<ChatSessionEntity>>));
|
||||||
|
getSessionsForCleanupAsyncMethod.GetParameters().Should().HaveCount(1);
|
||||||
|
getSessionsForCleanupAsyncMethod.GetParameters()[0].ParameterType.Should().Be(typeof(int));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void IChatSessionRepository_ShouldBeImplementedByChatSessionRepository()
|
||||||
|
{
|
||||||
|
// Arrange & Act
|
||||||
|
var chatSessionRepositoryType = typeof(ChatSessionRepository);
|
||||||
|
var interfaceType = typeof(IChatSessionRepository);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
interfaceType.IsAssignableFrom(chatSessionRepositoryType).Should().BeTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task IChatSessionRepository_GetOrCreateAsync_ShouldReturnChatSessionEntity()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var mock = new Mock<IChatSessionRepository>();
|
||||||
|
var chatId = 12345L;
|
||||||
|
var chatType = "private";
|
||||||
|
var chatTitle = "Test Chat";
|
||||||
|
var expectedSession = TestDataBuilder.Mocks.CreateChatSessionEntity();
|
||||||
|
|
||||||
|
mock.Setup(x =>
|
||||||
|
x.GetOrCreateAsync(It.IsAny<long>(), It.IsAny<string>(), It.IsAny<string>())
|
||||||
|
)
|
||||||
|
.ReturnsAsync(expectedSession);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await mock.Object.GetOrCreateAsync(chatId, chatType, chatTitle);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().Be(expectedSession);
|
||||||
|
mock.Verify(x => x.GetOrCreateAsync(chatId, chatType, chatTitle), Times.Once);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task IChatSessionRepository_GetOrCreateAsync_ShouldUseDefaultValues()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var mock = new Mock<IChatSessionRepository>();
|
||||||
|
var chatId = 12345L;
|
||||||
|
var expectedSession = TestDataBuilder.Mocks.CreateChatSessionEntity();
|
||||||
|
|
||||||
|
mock.Setup(x =>
|
||||||
|
x.GetOrCreateAsync(It.IsAny<long>(), It.IsAny<string>(), It.IsAny<string>())
|
||||||
|
)
|
||||||
|
.ReturnsAsync(expectedSession);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await mock.Object.GetOrCreateAsync(chatId);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().Be(expectedSession);
|
||||||
|
mock.Verify(x => x.GetOrCreateAsync(chatId, "private", ""), Times.Once);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task IChatSessionRepository_GetByChatIdAsync_ShouldReturnChatSessionEntityOrNull()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var mock = new Mock<IChatSessionRepository>();
|
||||||
|
var chatId = 12345L;
|
||||||
|
var expectedSession = TestDataBuilder.Mocks.CreateChatSessionEntity();
|
||||||
|
|
||||||
|
mock.Setup(x => x.GetByChatIdAsync(It.IsAny<long>())).ReturnsAsync(expectedSession);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await mock.Object.GetByChatIdAsync(chatId);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().Be(expectedSession);
|
||||||
|
mock.Verify(x => x.GetByChatIdAsync(chatId), Times.Once);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task IChatSessionRepository_GetByChatIdAsync_ShouldReturnNullWhenNotFound()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var mock = new Mock<IChatSessionRepository>();
|
||||||
|
var chatId = 12345L;
|
||||||
|
|
||||||
|
mock.Setup(x => x.GetByChatIdAsync(It.IsAny<long>()))
|
||||||
|
.ReturnsAsync((ChatSessionEntity?)null);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await mock.Object.GetByChatIdAsync(chatId);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().BeNull();
|
||||||
|
mock.Verify(x => x.GetByChatIdAsync(chatId), Times.Once);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task IChatSessionRepository_GetBySessionIdAsync_ShouldReturnChatSessionEntityOrNull()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var mock = new Mock<IChatSessionRepository>();
|
||||||
|
var sessionId = "test-session-id";
|
||||||
|
var expectedSession = TestDataBuilder.Mocks.CreateChatSessionEntity();
|
||||||
|
|
||||||
|
mock.Setup(x => x.GetBySessionIdAsync(It.IsAny<string>())).ReturnsAsync(expectedSession);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await mock.Object.GetBySessionIdAsync(sessionId);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().Be(expectedSession);
|
||||||
|
mock.Verify(x => x.GetBySessionIdAsync(sessionId), Times.Once);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task IChatSessionRepository_UpdateAsync_ShouldReturnUpdatedChatSessionEntity()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var mock = new Mock<IChatSessionRepository>();
|
||||||
|
var session = TestDataBuilder.Mocks.CreateChatSessionEntity();
|
||||||
|
var expectedUpdatedSession = TestDataBuilder.Mocks.CreateChatSessionEntity();
|
||||||
|
|
||||||
|
mock.Setup(x => x.UpdateAsync(It.IsAny<ChatSessionEntity>()))
|
||||||
|
.ReturnsAsync(expectedUpdatedSession);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await mock.Object.UpdateAsync(session);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().Be(expectedUpdatedSession);
|
||||||
|
mock.Verify(x => x.UpdateAsync(session), Times.Once);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task IChatSessionRepository_DeleteAsync_ShouldReturnBoolean()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var mock = new Mock<IChatSessionRepository>();
|
||||||
|
var chatId = 12345L;
|
||||||
|
var expectedResult = true;
|
||||||
|
|
||||||
|
mock.Setup(x => x.DeleteAsync(It.IsAny<long>())).ReturnsAsync(expectedResult);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await mock.Object.DeleteAsync(chatId);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().Be(expectedResult);
|
||||||
|
mock.Verify(x => x.DeleteAsync(chatId), Times.Once);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task IChatSessionRepository_GetMessagesAsync_ShouldReturnListOfMessages()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var mock = new Mock<IChatSessionRepository>();
|
||||||
|
var sessionId = 1;
|
||||||
|
var expectedMessages = new List<ChatMessageEntity>
|
||||||
|
{
|
||||||
|
TestDataBuilder.Mocks.CreateChatMessageEntity(),
|
||||||
|
TestDataBuilder.Mocks.CreateChatMessageEntity(),
|
||||||
|
};
|
||||||
|
|
||||||
|
mock.Setup(x => x.GetMessagesAsync(It.IsAny<int>())).ReturnsAsync(expectedMessages);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await mock.Object.GetMessagesAsync(sessionId);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().BeEquivalentTo(expectedMessages);
|
||||||
|
mock.Verify(x => x.GetMessagesAsync(sessionId), Times.Once);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task IChatSessionRepository_AddMessageAsync_ShouldReturnChatMessageEntity()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var mock = new Mock<IChatSessionRepository>();
|
||||||
|
var sessionId = 1;
|
||||||
|
var content = "Test message";
|
||||||
|
var role = "user";
|
||||||
|
var messageOrder = 1;
|
||||||
|
var expectedMessage = TestDataBuilder.Mocks.CreateChatMessageEntity();
|
||||||
|
|
||||||
|
mock.Setup(x =>
|
||||||
|
x.AddMessageAsync(
|
||||||
|
It.IsAny<int>(),
|
||||||
|
It.IsAny<string>(),
|
||||||
|
It.IsAny<string>(),
|
||||||
|
It.IsAny<int>()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.ReturnsAsync(expectedMessage);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await mock.Object.AddMessageAsync(sessionId, content, role, messageOrder);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().Be(expectedMessage);
|
||||||
|
mock.Verify(x => x.AddMessageAsync(sessionId, content, role, messageOrder), Times.Once);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task IChatSessionRepository_ClearMessagesAsync_ShouldReturnTask()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var mock = new Mock<IChatSessionRepository>();
|
||||||
|
var sessionId = 1;
|
||||||
|
|
||||||
|
mock.Setup(x => x.ClearMessagesAsync(It.IsAny<int>())).Returns(Task.CompletedTask);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await mock.Object.ClearMessagesAsync(sessionId);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
mock.Verify(x => x.ClearMessagesAsync(sessionId), Times.Once);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task IChatSessionRepository_GetActiveSessionsCountAsync_ShouldReturnInt()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var mock = new Mock<IChatSessionRepository>();
|
||||||
|
var expectedCount = 5;
|
||||||
|
|
||||||
|
mock.Setup(x => x.GetActiveSessionsCountAsync()).ReturnsAsync(expectedCount);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await mock.Object.GetActiveSessionsCountAsync();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().Be(expectedCount);
|
||||||
|
mock.Verify(x => x.GetActiveSessionsCountAsync(), Times.Once);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task IChatSessionRepository_CleanupOldSessionsAsync_ShouldReturnInt()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var mock = new Mock<IChatSessionRepository>();
|
||||||
|
var hoursOld = 24;
|
||||||
|
var expectedCleanedCount = 3;
|
||||||
|
|
||||||
|
mock.Setup(x => x.CleanupOldSessionsAsync(It.IsAny<int>()))
|
||||||
|
.ReturnsAsync(expectedCleanedCount);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await mock.Object.CleanupOldSessionsAsync(hoursOld);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().Be(expectedCleanedCount);
|
||||||
|
mock.Verify(x => x.CleanupOldSessionsAsync(hoursOld), Times.Once);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task IChatSessionRepository_CleanupOldSessionsAsync_ShouldUseDefaultValue()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var mock = new Mock<IChatSessionRepository>();
|
||||||
|
var expectedCleanedCount = 2;
|
||||||
|
|
||||||
|
mock.Setup(x => x.CleanupOldSessionsAsync(It.IsAny<int>()))
|
||||||
|
.ReturnsAsync(expectedCleanedCount);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await mock.Object.CleanupOldSessionsAsync();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().Be(expectedCleanedCount);
|
||||||
|
mock.Verify(x => x.CleanupOldSessionsAsync(24), Times.Once);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task IChatSessionRepository_GetSessionsForCleanupAsync_ShouldReturnListOfSessions()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var mock = new Mock<IChatSessionRepository>();
|
||||||
|
var hoursOld = 24;
|
||||||
|
var expectedSessions = new List<ChatSessionEntity>
|
||||||
|
{
|
||||||
|
TestDataBuilder.Mocks.CreateChatSessionEntity(),
|
||||||
|
TestDataBuilder.Mocks.CreateChatSessionEntity(),
|
||||||
|
};
|
||||||
|
|
||||||
|
mock.Setup(x => x.GetSessionsForCleanupAsync(It.IsAny<int>()))
|
||||||
|
.ReturnsAsync(expectedSessions);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await mock.Object.GetSessionsForCleanupAsync(hoursOld);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().BeEquivalentTo(expectedSessions);
|
||||||
|
mock.Verify(x => x.GetSessionsForCleanupAsync(hoursOld), Times.Once);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task IChatSessionRepository_GetSessionsForCleanupAsync_ShouldUseDefaultValue()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var mock = new Mock<IChatSessionRepository>();
|
||||||
|
var expectedSessions = new List<ChatSessionEntity>();
|
||||||
|
|
||||||
|
mock.Setup(x => x.GetSessionsForCleanupAsync(It.IsAny<int>()))
|
||||||
|
.ReturnsAsync(expectedSessions);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await mock.Object.GetSessionsForCleanupAsync();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().BeEquivalentTo(expectedSessions);
|
||||||
|
mock.Verify(x => x.GetSessionsForCleanupAsync(24), Times.Once);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void IChatSessionRepository_ShouldBePublicInterface()
|
||||||
|
{
|
||||||
|
// Arrange & Act
|
||||||
|
var interfaceType = typeof(IChatSessionRepository);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
interfaceType.IsPublic.Should().BeTrue();
|
||||||
|
interfaceType.IsInterface.Should().BeTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void IChatSessionRepository_ShouldHaveCorrectNamespace()
|
||||||
|
{
|
||||||
|
// Arrange & Act
|
||||||
|
var interfaceType = typeof(IChatSessionRepository);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
interfaceType.Namespace.Should().Be("ChatBot.Data.Interfaces");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void IChatSessionRepository_ShouldHaveCorrectGenericConstraints()
|
||||||
|
{
|
||||||
|
// Arrange & Act
|
||||||
|
var interfaceType = typeof(IChatSessionRepository);
|
||||||
|
var methods = interfaceType.GetMethods();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
// All methods should be public
|
||||||
|
methods.All(m => m.IsPublic).Should().BeTrue();
|
||||||
|
|
||||||
|
// GetOrCreateAsync should have default parameters
|
||||||
|
var getOrCreateAsyncMethod = methods.First(m => m.Name == "GetOrCreateAsync");
|
||||||
|
getOrCreateAsyncMethod.GetParameters()[1].HasDefaultValue.Should().BeTrue();
|
||||||
|
getOrCreateAsyncMethod.GetParameters()[1].DefaultValue.Should().Be("private");
|
||||||
|
getOrCreateAsyncMethod.GetParameters()[2].HasDefaultValue.Should().BeTrue();
|
||||||
|
getOrCreateAsyncMethod.GetParameters()[2].DefaultValue.Should().Be("");
|
||||||
|
|
||||||
|
// CleanupOldSessionsAsync should have default parameter
|
||||||
|
var cleanupOldSessionsAsyncMethod = methods.First(m => m.Name == "CleanupOldSessionsAsync");
|
||||||
|
cleanupOldSessionsAsyncMethod.GetParameters()[0].HasDefaultValue.Should().BeTrue();
|
||||||
|
cleanupOldSessionsAsyncMethod.GetParameters()[0].DefaultValue.Should().Be(24);
|
||||||
|
|
||||||
|
// GetSessionsForCleanupAsync should have default parameter
|
||||||
|
var getSessionsForCleanupAsyncMethod = methods.First(m =>
|
||||||
|
m.Name == "GetSessionsForCleanupAsync"
|
||||||
|
);
|
||||||
|
getSessionsForCleanupAsyncMethod.GetParameters()[0].HasDefaultValue.Should().BeTrue();
|
||||||
|
getSessionsForCleanupAsyncMethod.GetParameters()[0].DefaultValue.Should().Be(24);
|
||||||
|
}
|
||||||
|
}
|
||||||
327
ChatBot.Tests/Services/Interfaces/IAIServiceTests.cs
Normal file
327
ChatBot.Tests/Services/Interfaces/IAIServiceTests.cs
Normal file
@@ -0,0 +1,327 @@
|
|||||||
|
using ChatBot.Models.Dto;
|
||||||
|
using ChatBot.Services.Interfaces;
|
||||||
|
using ChatBot.Tests.TestUtilities;
|
||||||
|
using FluentAssertions;
|
||||||
|
using Moq;
|
||||||
|
|
||||||
|
namespace ChatBot.Tests.Services.Interfaces;
|
||||||
|
|
||||||
|
public class IAIServiceTests : UnitTestBase
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void IAIService_ShouldHaveCorrectMethodSignatures()
|
||||||
|
{
|
||||||
|
// Arrange & Act
|
||||||
|
var interfaceType = typeof(IAIService);
|
||||||
|
var methods = interfaceType.GetMethods();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
methods.Should().HaveCount(2);
|
||||||
|
|
||||||
|
var generateChatCompletionMethod = methods.FirstOrDefault(m =>
|
||||||
|
m.Name == "GenerateChatCompletionAsync"
|
||||||
|
);
|
||||||
|
generateChatCompletionMethod.Should().NotBeNull();
|
||||||
|
generateChatCompletionMethod!.ReturnType.Should().Be(typeof(Task<string>));
|
||||||
|
generateChatCompletionMethod.GetParameters().Should().HaveCount(2);
|
||||||
|
generateChatCompletionMethod
|
||||||
|
.GetParameters()[0]
|
||||||
|
.ParameterType.Should()
|
||||||
|
.Be(typeof(List<ChatMessage>));
|
||||||
|
generateChatCompletionMethod
|
||||||
|
.GetParameters()[1]
|
||||||
|
.ParameterType.Should()
|
||||||
|
.Be(typeof(CancellationToken));
|
||||||
|
|
||||||
|
var generateChatCompletionWithCompressionMethod = methods.FirstOrDefault(m =>
|
||||||
|
m.Name == "GenerateChatCompletionWithCompressionAsync"
|
||||||
|
);
|
||||||
|
generateChatCompletionWithCompressionMethod.Should().NotBeNull();
|
||||||
|
generateChatCompletionWithCompressionMethod!.ReturnType.Should().Be(typeof(Task<string>));
|
||||||
|
generateChatCompletionWithCompressionMethod.GetParameters().Should().HaveCount(2);
|
||||||
|
generateChatCompletionWithCompressionMethod
|
||||||
|
.GetParameters()[0]
|
||||||
|
.ParameterType.Should()
|
||||||
|
.Be(typeof(List<ChatMessage>));
|
||||||
|
generateChatCompletionWithCompressionMethod
|
||||||
|
.GetParameters()[1]
|
||||||
|
.ParameterType.Should()
|
||||||
|
.Be(typeof(CancellationToken));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void IAIService_ShouldBeImplementedByAIService()
|
||||||
|
{
|
||||||
|
// Arrange & Act
|
||||||
|
var aiServiceType = typeof(ChatBot.Services.AIService);
|
||||||
|
var interfaceType = typeof(IAIService);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
interfaceType.IsAssignableFrom(aiServiceType).Should().BeTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task IAIService_GenerateChatCompletionAsync_ShouldReturnString()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var mock = new Mock<IAIService>();
|
||||||
|
var messages = new List<ChatMessage>
|
||||||
|
{
|
||||||
|
new() { Role = "user", Content = "Test message" },
|
||||||
|
};
|
||||||
|
var cancellationToken = CancellationToken.None;
|
||||||
|
var expectedResponse = "Test response";
|
||||||
|
|
||||||
|
mock.Setup(x =>
|
||||||
|
x.GenerateChatCompletionAsync(
|
||||||
|
It.IsAny<List<ChatMessage>>(),
|
||||||
|
It.IsAny<CancellationToken>()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.ReturnsAsync(expectedResponse);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await mock.Object.GenerateChatCompletionAsync(messages, cancellationToken);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().Be(expectedResponse);
|
||||||
|
mock.Verify(x => x.GenerateChatCompletionAsync(messages, cancellationToken), Times.Once);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task IAIService_GenerateChatCompletionWithCompressionAsync_ShouldReturnString()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var mock = new Mock<IAIService>();
|
||||||
|
var messages = new List<ChatMessage>
|
||||||
|
{
|
||||||
|
new() { Role = "user", Content = "Test message" },
|
||||||
|
};
|
||||||
|
var cancellationToken = CancellationToken.None;
|
||||||
|
var expectedResponse = "Test response with compression";
|
||||||
|
|
||||||
|
mock.Setup(x =>
|
||||||
|
x.GenerateChatCompletionWithCompressionAsync(
|
||||||
|
It.IsAny<List<ChatMessage>>(),
|
||||||
|
It.IsAny<CancellationToken>()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.ReturnsAsync(expectedResponse);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await mock.Object.GenerateChatCompletionWithCompressionAsync(
|
||||||
|
messages,
|
||||||
|
cancellationToken
|
||||||
|
);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().Be(expectedResponse);
|
||||||
|
mock.Verify(
|
||||||
|
x => x.GenerateChatCompletionWithCompressionAsync(messages, cancellationToken),
|
||||||
|
Times.Once
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task IAIService_GenerateChatCompletionAsync_ShouldHandleEmptyMessages()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var mock = new Mock<IAIService>();
|
||||||
|
var messages = new List<ChatMessage>();
|
||||||
|
var cancellationToken = CancellationToken.None;
|
||||||
|
var expectedResponse = "Empty response";
|
||||||
|
|
||||||
|
mock.Setup(x =>
|
||||||
|
x.GenerateChatCompletionAsync(
|
||||||
|
It.IsAny<List<ChatMessage>>(),
|
||||||
|
It.IsAny<CancellationToken>()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.ReturnsAsync(expectedResponse);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await mock.Object.GenerateChatCompletionAsync(messages, cancellationToken);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().Be(expectedResponse);
|
||||||
|
mock.Verify(x => x.GenerateChatCompletionAsync(messages, cancellationToken), Times.Once);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task IAIService_GenerateChatCompletionWithCompressionAsync_ShouldHandleEmptyMessages()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var mock = new Mock<IAIService>();
|
||||||
|
var messages = new List<ChatMessage>();
|
||||||
|
var cancellationToken = CancellationToken.None;
|
||||||
|
var expectedResponse = "Empty response with compression";
|
||||||
|
|
||||||
|
mock.Setup(x =>
|
||||||
|
x.GenerateChatCompletionWithCompressionAsync(
|
||||||
|
It.IsAny<List<ChatMessage>>(),
|
||||||
|
It.IsAny<CancellationToken>()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.ReturnsAsync(expectedResponse);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await mock.Object.GenerateChatCompletionWithCompressionAsync(
|
||||||
|
messages,
|
||||||
|
cancellationToken
|
||||||
|
);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().Be(expectedResponse);
|
||||||
|
mock.Verify(
|
||||||
|
x => x.GenerateChatCompletionWithCompressionAsync(messages, cancellationToken),
|
||||||
|
Times.Once
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task IAIService_GenerateChatCompletionAsync_ShouldHandleCancellationToken()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var mock = new Mock<IAIService>();
|
||||||
|
var messages = new List<ChatMessage>
|
||||||
|
{
|
||||||
|
new() { Role = "user", Content = "Test message" },
|
||||||
|
};
|
||||||
|
var cancellationToken = new CancellationToken(true); // Cancelled token
|
||||||
|
var expectedResponse = "Cancelled response";
|
||||||
|
|
||||||
|
mock.Setup(x =>
|
||||||
|
x.GenerateChatCompletionAsync(
|
||||||
|
It.IsAny<List<ChatMessage>>(),
|
||||||
|
It.IsAny<CancellationToken>()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.ReturnsAsync(expectedResponse);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await mock.Object.GenerateChatCompletionAsync(messages, cancellationToken);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().Be(expectedResponse);
|
||||||
|
mock.Verify(x => x.GenerateChatCompletionAsync(messages, cancellationToken), Times.Once);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task IAIService_GenerateChatCompletionWithCompressionAsync_ShouldHandleCancellationToken()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var mock = new Mock<IAIService>();
|
||||||
|
var messages = new List<ChatMessage>
|
||||||
|
{
|
||||||
|
new() { Role = "user", Content = "Test message" },
|
||||||
|
};
|
||||||
|
var cancellationToken = new CancellationToken(true); // Cancelled token
|
||||||
|
var expectedResponse = "Cancelled response with compression";
|
||||||
|
|
||||||
|
mock.Setup(x =>
|
||||||
|
x.GenerateChatCompletionWithCompressionAsync(
|
||||||
|
It.IsAny<List<ChatMessage>>(),
|
||||||
|
It.IsAny<CancellationToken>()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.ReturnsAsync(expectedResponse);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await mock.Object.GenerateChatCompletionWithCompressionAsync(
|
||||||
|
messages,
|
||||||
|
cancellationToken
|
||||||
|
);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().Be(expectedResponse);
|
||||||
|
mock.Verify(
|
||||||
|
x => x.GenerateChatCompletionWithCompressionAsync(messages, cancellationToken),
|
||||||
|
Times.Once
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task IAIService_GenerateChatCompletionAsync_ShouldHandleLargeMessageList()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var mock = new Mock<IAIService>();
|
||||||
|
var messages = new List<ChatMessage>();
|
||||||
|
for (int i = 0; i < 100; i++)
|
||||||
|
{
|
||||||
|
messages.Add(new() { Role = "user", Content = $"Message {i}" });
|
||||||
|
}
|
||||||
|
var cancellationToken = CancellationToken.None;
|
||||||
|
var expectedResponse = "Large response";
|
||||||
|
|
||||||
|
mock.Setup(x =>
|
||||||
|
x.GenerateChatCompletionAsync(
|
||||||
|
It.IsAny<List<ChatMessage>>(),
|
||||||
|
It.IsAny<CancellationToken>()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.ReturnsAsync(expectedResponse);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await mock.Object.GenerateChatCompletionAsync(messages, cancellationToken);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().Be(expectedResponse);
|
||||||
|
mock.Verify(x => x.GenerateChatCompletionAsync(messages, cancellationToken), Times.Once);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task IAIService_GenerateChatCompletionWithCompressionAsync_ShouldHandleLargeMessageList()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var mock = new Mock<IAIService>();
|
||||||
|
var messages = new List<ChatMessage>();
|
||||||
|
for (int i = 0; i < 100; i++)
|
||||||
|
{
|
||||||
|
messages.Add(new() { Role = "user", Content = $"Message {i}" });
|
||||||
|
}
|
||||||
|
var cancellationToken = CancellationToken.None;
|
||||||
|
var expectedResponse = "Large response with compression";
|
||||||
|
|
||||||
|
mock.Setup(x =>
|
||||||
|
x.GenerateChatCompletionWithCompressionAsync(
|
||||||
|
It.IsAny<List<ChatMessage>>(),
|
||||||
|
It.IsAny<CancellationToken>()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.ReturnsAsync(expectedResponse);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await mock.Object.GenerateChatCompletionWithCompressionAsync(
|
||||||
|
messages,
|
||||||
|
cancellationToken
|
||||||
|
);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().Be(expectedResponse);
|
||||||
|
mock.Verify(
|
||||||
|
x => x.GenerateChatCompletionWithCompressionAsync(messages, cancellationToken),
|
||||||
|
Times.Once
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void IAIService_ShouldBePublicInterface()
|
||||||
|
{
|
||||||
|
// Arrange & Act
|
||||||
|
var interfaceType = typeof(IAIService);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
interfaceType.IsPublic.Should().BeTrue();
|
||||||
|
interfaceType.IsInterface.Should().BeTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void IAIService_ShouldHaveCorrectNamespace()
|
||||||
|
{
|
||||||
|
// Arrange & Act
|
||||||
|
var interfaceType = typeof(IAIService);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
interfaceType.Namespace.Should().Be("ChatBot.Services.Interfaces");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,367 @@
|
|||||||
|
using ChatBot.Models.Dto;
|
||||||
|
using ChatBot.Services;
|
||||||
|
using ChatBot.Services.Interfaces;
|
||||||
|
using ChatBot.Tests.TestUtilities;
|
||||||
|
using FluentAssertions;
|
||||||
|
using Moq;
|
||||||
|
|
||||||
|
namespace ChatBot.Tests.Services.Interfaces;
|
||||||
|
|
||||||
|
public class IHistoryCompressionServiceTests : UnitTestBase
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void IHistoryCompressionService_ShouldHaveCorrectMethodSignatures()
|
||||||
|
{
|
||||||
|
// Arrange & Act
|
||||||
|
var interfaceType = typeof(IHistoryCompressionService);
|
||||||
|
var methods = interfaceType.GetMethods();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
methods.Should().HaveCount(2);
|
||||||
|
|
||||||
|
// CompressHistoryAsync method
|
||||||
|
var compressHistoryAsyncMethod = methods.FirstOrDefault(m =>
|
||||||
|
m.Name == "CompressHistoryAsync"
|
||||||
|
);
|
||||||
|
compressHistoryAsyncMethod.Should().NotBeNull();
|
||||||
|
compressHistoryAsyncMethod!.ReturnType.Should().Be(typeof(Task<List<ChatMessage>>));
|
||||||
|
compressHistoryAsyncMethod.GetParameters().Should().HaveCount(3);
|
||||||
|
compressHistoryAsyncMethod
|
||||||
|
.GetParameters()[0]
|
||||||
|
.ParameterType.Should()
|
||||||
|
.Be(typeof(List<ChatMessage>));
|
||||||
|
compressHistoryAsyncMethod.GetParameters()[1].ParameterType.Should().Be(typeof(int));
|
||||||
|
compressHistoryAsyncMethod
|
||||||
|
.GetParameters()[2]
|
||||||
|
.ParameterType.Should()
|
||||||
|
.Be(typeof(CancellationToken));
|
||||||
|
|
||||||
|
// ShouldCompress method
|
||||||
|
var shouldCompressMethod = methods.FirstOrDefault(m => m.Name == "ShouldCompress");
|
||||||
|
shouldCompressMethod.Should().NotBeNull();
|
||||||
|
shouldCompressMethod!.ReturnType.Should().Be(typeof(bool));
|
||||||
|
shouldCompressMethod.GetParameters().Should().HaveCount(2);
|
||||||
|
shouldCompressMethod.GetParameters()[0].ParameterType.Should().Be(typeof(int));
|
||||||
|
shouldCompressMethod.GetParameters()[1].ParameterType.Should().Be(typeof(int));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void IHistoryCompressionService_ShouldBeImplementedByHistoryCompressionService()
|
||||||
|
{
|
||||||
|
// Arrange & Act
|
||||||
|
var historyCompressionServiceType = typeof(HistoryCompressionService);
|
||||||
|
var interfaceType = typeof(IHistoryCompressionService);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
interfaceType.IsAssignableFrom(historyCompressionServiceType).Should().BeTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task IHistoryCompressionService_CompressHistoryAsync_ShouldReturnCompressedMessages()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var mock = new Mock<IHistoryCompressionService>();
|
||||||
|
var messages = new List<ChatMessage>
|
||||||
|
{
|
||||||
|
new() { Role = "user", Content = "Message 1" },
|
||||||
|
new() { Role = "assistant", Content = "Response 1" },
|
||||||
|
new() { Role = "user", Content = "Message 2" },
|
||||||
|
new() { Role = "assistant", Content = "Response 2" },
|
||||||
|
};
|
||||||
|
var targetCount = 2;
|
||||||
|
var cancellationToken = CancellationToken.None;
|
||||||
|
var expectedCompressedMessages = new List<ChatMessage>
|
||||||
|
{
|
||||||
|
new() { Role = "user", Content = "Compressed message" },
|
||||||
|
new() { Role = "assistant", Content = "Compressed response" },
|
||||||
|
};
|
||||||
|
|
||||||
|
mock.Setup(x =>
|
||||||
|
x.CompressHistoryAsync(
|
||||||
|
It.IsAny<List<ChatMessage>>(),
|
||||||
|
It.IsAny<int>(),
|
||||||
|
It.IsAny<CancellationToken>()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.ReturnsAsync(expectedCompressedMessages);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await mock.Object.CompressHistoryAsync(
|
||||||
|
messages,
|
||||||
|
targetCount,
|
||||||
|
cancellationToken
|
||||||
|
);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().BeEquivalentTo(expectedCompressedMessages);
|
||||||
|
mock.Verify(
|
||||||
|
x => x.CompressHistoryAsync(messages, targetCount, cancellationToken),
|
||||||
|
Times.Once
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task IHistoryCompressionService_CompressHistoryAsync_ShouldHandleEmptyMessages()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var mock = new Mock<IHistoryCompressionService>();
|
||||||
|
var messages = new List<ChatMessage>();
|
||||||
|
var targetCount = 5;
|
||||||
|
var cancellationToken = CancellationToken.None;
|
||||||
|
var expectedCompressedMessages = new List<ChatMessage>();
|
||||||
|
|
||||||
|
mock.Setup(x =>
|
||||||
|
x.CompressHistoryAsync(
|
||||||
|
It.IsAny<List<ChatMessage>>(),
|
||||||
|
It.IsAny<int>(),
|
||||||
|
It.IsAny<CancellationToken>()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.ReturnsAsync(expectedCompressedMessages);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await mock.Object.CompressHistoryAsync(
|
||||||
|
messages,
|
||||||
|
targetCount,
|
||||||
|
cancellationToken
|
||||||
|
);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().BeEquivalentTo(expectedCompressedMessages);
|
||||||
|
mock.Verify(
|
||||||
|
x => x.CompressHistoryAsync(messages, targetCount, cancellationToken),
|
||||||
|
Times.Once
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task IHistoryCompressionService_CompressHistoryAsync_ShouldHandleLargeMessageList()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var mock = new Mock<IHistoryCompressionService>();
|
||||||
|
var messages = new List<ChatMessage>();
|
||||||
|
for (int i = 0; i < 100; i++)
|
||||||
|
{
|
||||||
|
messages.Add(new() { Role = "user", Content = $"Message {i}" });
|
||||||
|
}
|
||||||
|
var targetCount = 10;
|
||||||
|
var cancellationToken = CancellationToken.None;
|
||||||
|
var expectedCompressedMessages = new List<ChatMessage>
|
||||||
|
{
|
||||||
|
new() { Role = "user", Content = "Compressed summary" },
|
||||||
|
};
|
||||||
|
|
||||||
|
mock.Setup(x =>
|
||||||
|
x.CompressHistoryAsync(
|
||||||
|
It.IsAny<List<ChatMessage>>(),
|
||||||
|
It.IsAny<int>(),
|
||||||
|
It.IsAny<CancellationToken>()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.ReturnsAsync(expectedCompressedMessages);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await mock.Object.CompressHistoryAsync(
|
||||||
|
messages,
|
||||||
|
targetCount,
|
||||||
|
cancellationToken
|
||||||
|
);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().BeEquivalentTo(expectedCompressedMessages);
|
||||||
|
mock.Verify(
|
||||||
|
x => x.CompressHistoryAsync(messages, targetCount, cancellationToken),
|
||||||
|
Times.Once
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task IHistoryCompressionService_CompressHistoryAsync_ShouldHandleCancellationToken()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var mock = new Mock<IHistoryCompressionService>();
|
||||||
|
var messages = new List<ChatMessage>
|
||||||
|
{
|
||||||
|
new() { Role = "user", Content = "Test message" },
|
||||||
|
};
|
||||||
|
var targetCount = 1;
|
||||||
|
var cancellationToken = new CancellationToken(true); // Cancelled token
|
||||||
|
var expectedCompressedMessages = new List<ChatMessage>();
|
||||||
|
|
||||||
|
mock.Setup(x =>
|
||||||
|
x.CompressHistoryAsync(
|
||||||
|
It.IsAny<List<ChatMessage>>(),
|
||||||
|
It.IsAny<int>(),
|
||||||
|
It.IsAny<CancellationToken>()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.ReturnsAsync(expectedCompressedMessages);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await mock.Object.CompressHistoryAsync(
|
||||||
|
messages,
|
||||||
|
targetCount,
|
||||||
|
cancellationToken
|
||||||
|
);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().BeEquivalentTo(expectedCompressedMessages);
|
||||||
|
mock.Verify(
|
||||||
|
x => x.CompressHistoryAsync(messages, targetCount, cancellationToken),
|
||||||
|
Times.Once
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void IHistoryCompressionService_ShouldCompress_ShouldReturnTrue_WhenMessageCountExceedsThreshold()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var mock = new Mock<IHistoryCompressionService>();
|
||||||
|
var messageCount = 15;
|
||||||
|
var threshold = 10;
|
||||||
|
var expectedResult = true;
|
||||||
|
|
||||||
|
mock.Setup(x => x.ShouldCompress(It.IsAny<int>(), It.IsAny<int>())).Returns(expectedResult);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = mock.Object.ShouldCompress(messageCount, threshold);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().Be(expectedResult);
|
||||||
|
mock.Verify(x => x.ShouldCompress(messageCount, threshold), Times.Once);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void IHistoryCompressionService_ShouldCompress_ShouldReturnFalse_WhenMessageCountIsBelowThreshold()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var mock = new Mock<IHistoryCompressionService>();
|
||||||
|
var messageCount = 5;
|
||||||
|
var threshold = 10;
|
||||||
|
var expectedResult = false;
|
||||||
|
|
||||||
|
mock.Setup(x => x.ShouldCompress(It.IsAny<int>(), It.IsAny<int>())).Returns(expectedResult);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = mock.Object.ShouldCompress(messageCount, threshold);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().Be(expectedResult);
|
||||||
|
mock.Verify(x => x.ShouldCompress(messageCount, threshold), Times.Once);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void IHistoryCompressionService_ShouldCompress_ShouldReturnFalse_WhenMessageCountEqualsThreshold()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var mock = new Mock<IHistoryCompressionService>();
|
||||||
|
var messageCount = 10;
|
||||||
|
var threshold = 10;
|
||||||
|
var expectedResult = false;
|
||||||
|
|
||||||
|
mock.Setup(x => x.ShouldCompress(It.IsAny<int>(), It.IsAny<int>())).Returns(expectedResult);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = mock.Object.ShouldCompress(messageCount, threshold);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().Be(expectedResult);
|
||||||
|
mock.Verify(x => x.ShouldCompress(messageCount, threshold), Times.Once);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void IHistoryCompressionService_ShouldCompress_ShouldHandleZeroValues()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var mock = new Mock<IHistoryCompressionService>();
|
||||||
|
var messageCount = 0;
|
||||||
|
var threshold = 0;
|
||||||
|
var expectedResult = false;
|
||||||
|
|
||||||
|
mock.Setup(x => x.ShouldCompress(It.IsAny<int>(), It.IsAny<int>())).Returns(expectedResult);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = mock.Object.ShouldCompress(messageCount, threshold);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().Be(expectedResult);
|
||||||
|
mock.Verify(x => x.ShouldCompress(messageCount, threshold), Times.Once);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void IHistoryCompressionService_ShouldCompress_ShouldHandleNegativeValues()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var mock = new Mock<IHistoryCompressionService>();
|
||||||
|
var messageCount = -5;
|
||||||
|
var threshold = -10;
|
||||||
|
var expectedResult = false;
|
||||||
|
|
||||||
|
mock.Setup(x => x.ShouldCompress(It.IsAny<int>(), It.IsAny<int>())).Returns(expectedResult);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = mock.Object.ShouldCompress(messageCount, threshold);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().Be(expectedResult);
|
||||||
|
mock.Verify(x => x.ShouldCompress(messageCount, threshold), Times.Once);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void IHistoryCompressionService_ShouldCompress_ShouldHandleLargeValues()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var mock = new Mock<IHistoryCompressionService>();
|
||||||
|
var messageCount = int.MaxValue;
|
||||||
|
var threshold = int.MaxValue - 1;
|
||||||
|
var expectedResult = true;
|
||||||
|
|
||||||
|
mock.Setup(x => x.ShouldCompress(It.IsAny<int>(), It.IsAny<int>())).Returns(expectedResult);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = mock.Object.ShouldCompress(messageCount, threshold);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().Be(expectedResult);
|
||||||
|
mock.Verify(x => x.ShouldCompress(messageCount, threshold), Times.Once);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void IHistoryCompressionService_ShouldBePublicInterface()
|
||||||
|
{
|
||||||
|
// Arrange & Act
|
||||||
|
var interfaceType = typeof(IHistoryCompressionService);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
interfaceType.IsPublic.Should().BeTrue();
|
||||||
|
interfaceType.IsInterface.Should().BeTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void IHistoryCompressionService_ShouldHaveCorrectNamespace()
|
||||||
|
{
|
||||||
|
// Arrange & Act
|
||||||
|
var interfaceType = typeof(IHistoryCompressionService);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
interfaceType.Namespace.Should().Be("ChatBot.Services.Interfaces");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void IHistoryCompressionService_ShouldHaveCorrectGenericConstraints()
|
||||||
|
{
|
||||||
|
// Arrange & Act
|
||||||
|
var interfaceType = typeof(IHistoryCompressionService);
|
||||||
|
var methods = interfaceType.GetMethods();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
// All methods should be public
|
||||||
|
methods.All(m => m.IsPublic).Should().BeTrue();
|
||||||
|
|
||||||
|
// CompressHistoryAsync should have default parameter for CancellationToken
|
||||||
|
var compressMethod = methods.First(m => m.Name == "CompressHistoryAsync");
|
||||||
|
compressMethod.GetParameters()[2].HasDefaultValue.Should().BeTrue();
|
||||||
|
// Note: Default value for CancellationToken in interface might be null
|
||||||
|
compressMethod.GetParameters()[2].DefaultValue.Should().BeNull();
|
||||||
|
}
|
||||||
|
}
|
||||||
380
ChatBot.Tests/Services/Interfaces/IOllamaClientTests.cs
Normal file
380
ChatBot.Tests/Services/Interfaces/IOllamaClientTests.cs
Normal file
@@ -0,0 +1,380 @@
|
|||||||
|
using ChatBot.Services;
|
||||||
|
using ChatBot.Services.Interfaces;
|
||||||
|
using ChatBot.Tests.TestUtilities;
|
||||||
|
using FluentAssertions;
|
||||||
|
using Moq;
|
||||||
|
using OllamaSharp.Models;
|
||||||
|
using OllamaSharp.Models.Chat;
|
||||||
|
|
||||||
|
namespace ChatBot.Tests.Services.Interfaces;
|
||||||
|
|
||||||
|
public class IOllamaClientTests : UnitTestBase
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void IOllamaClient_ShouldHaveCorrectMethodSignatures()
|
||||||
|
{
|
||||||
|
// Arrange & Act
|
||||||
|
var interfaceType = typeof(IOllamaClient);
|
||||||
|
var methods = interfaceType.GetMethods();
|
||||||
|
var properties = interfaceType.GetProperties();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
methods.Should().HaveCount(4); // ChatAsync, ListLocalModelsAsync, get_SelectedModel, set_SelectedModel
|
||||||
|
properties.Should().HaveCount(1);
|
||||||
|
|
||||||
|
// SelectedModel property
|
||||||
|
var selectedModelProperty = properties.FirstOrDefault(p => p.Name == "SelectedModel");
|
||||||
|
selectedModelProperty.Should().NotBeNull();
|
||||||
|
selectedModelProperty!.PropertyType.Should().Be(typeof(string));
|
||||||
|
selectedModelProperty.CanRead.Should().BeTrue();
|
||||||
|
selectedModelProperty.CanWrite.Should().BeTrue();
|
||||||
|
|
||||||
|
// ChatAsync method
|
||||||
|
var chatAsyncMethod = methods.FirstOrDefault(m => m.Name == "ChatAsync");
|
||||||
|
chatAsyncMethod.Should().NotBeNull();
|
||||||
|
chatAsyncMethod!.ReturnType.Should().Be(typeof(IAsyncEnumerable<ChatResponseStream?>));
|
||||||
|
chatAsyncMethod.GetParameters().Should().HaveCount(1);
|
||||||
|
chatAsyncMethod.GetParameters()[0].ParameterType.Should().Be(typeof(ChatRequest));
|
||||||
|
|
||||||
|
// ListLocalModelsAsync method
|
||||||
|
var listLocalModelsAsyncMethod = methods.FirstOrDefault(m =>
|
||||||
|
m.Name == "ListLocalModelsAsync"
|
||||||
|
);
|
||||||
|
listLocalModelsAsyncMethod.Should().NotBeNull();
|
||||||
|
listLocalModelsAsyncMethod!.ReturnType.Should().Be(typeof(Task<IEnumerable<Model>>));
|
||||||
|
listLocalModelsAsyncMethod.GetParameters().Should().BeEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void IOllamaClient_ShouldBeImplementedByOllamaClientAdapter()
|
||||||
|
{
|
||||||
|
// Arrange & Act
|
||||||
|
var ollamaClientAdapterType = typeof(OllamaClientAdapter);
|
||||||
|
var interfaceType = typeof(IOllamaClient);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
interfaceType.IsAssignableFrom(ollamaClientAdapterType).Should().BeTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void IOllamaClient_SelectedModel_ShouldBeReadableAndWritable()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var mock = new Mock<IOllamaClient>();
|
||||||
|
var expectedModel = "llama2:7b";
|
||||||
|
|
||||||
|
mock.SetupProperty(x => x.SelectedModel, "default-model");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
mock.Object.SelectedModel = expectedModel;
|
||||||
|
var result = mock.Object.SelectedModel;
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().Be(expectedModel);
|
||||||
|
mock.VerifySet(x => x.SelectedModel = expectedModel, Times.Once);
|
||||||
|
mock.VerifyGet(x => x.SelectedModel, Times.Once);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task IOllamaClient_ChatAsync_ShouldReturnAsyncEnumerable()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var mock = new Mock<IOllamaClient>();
|
||||||
|
var request = new ChatRequest
|
||||||
|
{
|
||||||
|
Model = "llama2:7b",
|
||||||
|
Messages = new List<Message>
|
||||||
|
{
|
||||||
|
new() { Role = "user", Content = "Hello" },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
var expectedResponse = new List<ChatResponseStream?>
|
||||||
|
{
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
Message = new Message
|
||||||
|
{
|
||||||
|
Role = "assistant",
|
||||||
|
Content = "Hello! How can I help you?",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
new() { Done = true },
|
||||||
|
};
|
||||||
|
|
||||||
|
mock.Setup(x => x.ChatAsync(It.IsAny<ChatRequest>()))
|
||||||
|
.Returns(CreateAsyncEnumerable(expectedResponse));
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = mock.Object.ChatAsync(request);
|
||||||
|
var responses = new List<ChatResponseStream?>();
|
||||||
|
await foreach (var response in result)
|
||||||
|
{
|
||||||
|
responses.Add(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
responses.Should().HaveCount(2);
|
||||||
|
responses[0]?.Message?.Content.Should().Be("Hello! How can I help you?");
|
||||||
|
responses[1]?.Done.Should().BeTrue();
|
||||||
|
mock.Verify(x => x.ChatAsync(request), Times.Once);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task IOllamaClient_ChatAsync_ShouldHandleEmptyResponse()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var mock = new Mock<IOllamaClient>();
|
||||||
|
var request = new ChatRequest { Model = "llama2:7b", Messages = new List<Message>() };
|
||||||
|
|
||||||
|
var expectedResponse = new List<ChatResponseStream?>();
|
||||||
|
|
||||||
|
mock.Setup(x => x.ChatAsync(It.IsAny<ChatRequest>()))
|
||||||
|
.Returns(CreateAsyncEnumerable(expectedResponse));
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = mock.Object.ChatAsync(request);
|
||||||
|
var responses = new List<ChatResponseStream?>();
|
||||||
|
await foreach (var response in result)
|
||||||
|
{
|
||||||
|
responses.Add(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
responses.Should().BeEmpty();
|
||||||
|
mock.Verify(x => x.ChatAsync(request), Times.Once);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task IOllamaClient_ChatAsync_ShouldHandleNullResponse()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var mock = new Mock<IOllamaClient>();
|
||||||
|
var request = new ChatRequest
|
||||||
|
{
|
||||||
|
Model = "llama2:7b",
|
||||||
|
Messages = new List<Message>
|
||||||
|
{
|
||||||
|
new() { Role = "user", Content = "Test" },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
var expectedResponse = new List<ChatResponseStream?>
|
||||||
|
{
|
||||||
|
null,
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
Message = new Message { Role = "assistant", Content = "Response" },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
mock.Setup(x => x.ChatAsync(It.IsAny<ChatRequest>()))
|
||||||
|
.Returns(CreateAsyncEnumerable(expectedResponse));
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = mock.Object.ChatAsync(request);
|
||||||
|
var responses = new List<ChatResponseStream?>();
|
||||||
|
await foreach (var response in result)
|
||||||
|
{
|
||||||
|
responses.Add(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
responses.Should().HaveCount(2);
|
||||||
|
responses[0].Should().BeNull();
|
||||||
|
responses[1]?.Message?.Content.Should().Be("Response");
|
||||||
|
mock.Verify(x => x.ChatAsync(request), Times.Once);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task IOllamaClient_ListLocalModelsAsync_ShouldReturnModels()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var mock = new Mock<IOllamaClient>();
|
||||||
|
var expectedModels = new List<Model>
|
||||||
|
{
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
Name = "llama2:7b",
|
||||||
|
Size = 3825819519,
|
||||||
|
ModifiedAt = DateTime.UtcNow,
|
||||||
|
},
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
Name = "codellama:7b",
|
||||||
|
Size = 3825819519,
|
||||||
|
ModifiedAt = DateTime.UtcNow,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
mock.Setup(x => x.ListLocalModelsAsync()).ReturnsAsync(expectedModels);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await mock.Object.ListLocalModelsAsync();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().BeEquivalentTo(expectedModels);
|
||||||
|
mock.Verify(x => x.ListLocalModelsAsync(), Times.Once);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task IOllamaClient_ListLocalModelsAsync_ShouldReturnEmptyList()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var mock = new Mock<IOllamaClient>();
|
||||||
|
var expectedModels = new List<Model>();
|
||||||
|
|
||||||
|
mock.Setup(x => x.ListLocalModelsAsync()).ReturnsAsync(expectedModels);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await mock.Object.ListLocalModelsAsync();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().BeEmpty();
|
||||||
|
mock.Verify(x => x.ListLocalModelsAsync(), Times.Once);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task IOllamaClient_ListLocalModelsAsync_ShouldHandleNullModels()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var mock = new Mock<IOllamaClient>();
|
||||||
|
IEnumerable<Model>? expectedModels = null;
|
||||||
|
|
||||||
|
mock.Setup(x => x.ListLocalModelsAsync()).ReturnsAsync(expectedModels!);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await mock.Object.ListLocalModelsAsync();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().BeNull();
|
||||||
|
mock.Verify(x => x.ListLocalModelsAsync(), Times.Once);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void IOllamaClient_SelectedModel_ShouldHandleNullValue()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var mock = new Mock<IOllamaClient>();
|
||||||
|
string? expectedModel = null;
|
||||||
|
|
||||||
|
mock.SetupProperty(x => x.SelectedModel, "default-model");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
mock.Object.SelectedModel = expectedModel!;
|
||||||
|
var result = mock.Object.SelectedModel;
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().BeNull();
|
||||||
|
mock.VerifySet(x => x.SelectedModel = expectedModel!, Times.Once);
|
||||||
|
mock.VerifyGet(x => x.SelectedModel, Times.Once);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void IOllamaClient_SelectedModel_ShouldHandleEmptyString()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var mock = new Mock<IOllamaClient>();
|
||||||
|
var expectedModel = "";
|
||||||
|
|
||||||
|
mock.SetupProperty(x => x.SelectedModel, "default-model");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
mock.Object.SelectedModel = expectedModel;
|
||||||
|
var result = mock.Object.SelectedModel;
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().Be(expectedModel);
|
||||||
|
mock.VerifySet(x => x.SelectedModel = expectedModel, Times.Once);
|
||||||
|
mock.VerifyGet(x => x.SelectedModel, Times.Once);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void IOllamaClient_ShouldBePublicInterface()
|
||||||
|
{
|
||||||
|
// Arrange & Act
|
||||||
|
var interfaceType = typeof(IOllamaClient);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
interfaceType.IsPublic.Should().BeTrue();
|
||||||
|
interfaceType.IsInterface.Should().BeTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void IOllamaClient_ShouldHaveCorrectNamespace()
|
||||||
|
{
|
||||||
|
// Arrange & Act
|
||||||
|
var interfaceType = typeof(IOllamaClient);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
interfaceType.Namespace.Should().Be("ChatBot.Services.Interfaces");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void IOllamaClient_ShouldHaveCorrectGenericConstraints()
|
||||||
|
{
|
||||||
|
// Arrange & Act
|
||||||
|
var interfaceType = typeof(IOllamaClient);
|
||||||
|
var methods = interfaceType.GetMethods();
|
||||||
|
var properties = interfaceType.GetProperties();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
// All methods should be public
|
||||||
|
methods.All(m => m.IsPublic).Should().BeTrue();
|
||||||
|
|
||||||
|
// All properties should be public
|
||||||
|
properties.All(p => p.GetGetMethod()?.IsPublic == true).Should().BeTrue();
|
||||||
|
properties.All(p => p.GetSetMethod()?.IsPublic == true).Should().BeTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task IOllamaClient_ChatAsync_ShouldHandleLargeRequest()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var mock = new Mock<IOllamaClient>();
|
||||||
|
var messages = new List<Message>();
|
||||||
|
// Add many messages
|
||||||
|
for (int i = 0; i < 100; i++)
|
||||||
|
{
|
||||||
|
messages.Add(
|
||||||
|
new Message { Role = i % 2 == 0 ? "user" : "assistant", Content = $"Message {i}" }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
var request = new ChatRequest { Model = "llama2:7b", Messages = messages };
|
||||||
|
|
||||||
|
var expectedResponse = new List<ChatResponseStream?>
|
||||||
|
{
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
Message = new Message { Role = "assistant", Content = "Large response" },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
mock.Setup(x => x.ChatAsync(It.IsAny<ChatRequest>()))
|
||||||
|
.Returns(CreateAsyncEnumerable(expectedResponse));
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = mock.Object.ChatAsync(request);
|
||||||
|
var responses = new List<ChatResponseStream?>();
|
||||||
|
await foreach (var response in result)
|
||||||
|
{
|
||||||
|
responses.Add(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
responses.Should().HaveCount(1);
|
||||||
|
responses[0]?.Message?.Content.Should().Be("Large response");
|
||||||
|
mock.Verify(x => x.ChatAsync(request), Times.Once);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async IAsyncEnumerable<ChatResponseStream?> CreateAsyncEnumerable(
|
||||||
|
List<ChatResponseStream?> items
|
||||||
|
)
|
||||||
|
{
|
||||||
|
foreach (var item in items)
|
||||||
|
{
|
||||||
|
yield return item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
295
ChatBot.Tests/Services/Interfaces/ISessionStorageTests.cs
Normal file
295
ChatBot.Tests/Services/Interfaces/ISessionStorageTests.cs
Normal file
@@ -0,0 +1,295 @@
|
|||||||
|
using ChatBot.Models;
|
||||||
|
using ChatBot.Services;
|
||||||
|
using ChatBot.Services.Interfaces;
|
||||||
|
using ChatBot.Tests.TestUtilities;
|
||||||
|
using FluentAssertions;
|
||||||
|
using Moq;
|
||||||
|
|
||||||
|
namespace ChatBot.Tests.Services.Interfaces;
|
||||||
|
|
||||||
|
public class ISessionStorageTests : UnitTestBase
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void ISessionStorage_ShouldHaveCorrectMethodSignatures()
|
||||||
|
{
|
||||||
|
// Arrange & Act
|
||||||
|
var interfaceType = typeof(ISessionStorage);
|
||||||
|
var methods = interfaceType.GetMethods();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
methods.Should().HaveCount(6);
|
||||||
|
|
||||||
|
// GetOrCreate method
|
||||||
|
var getOrCreateMethod = methods.FirstOrDefault(m => m.Name == "GetOrCreate");
|
||||||
|
getOrCreateMethod.Should().NotBeNull();
|
||||||
|
getOrCreateMethod!.ReturnType.Should().Be(typeof(ChatSession));
|
||||||
|
getOrCreateMethod.GetParameters().Should().HaveCount(3);
|
||||||
|
getOrCreateMethod.GetParameters()[0].ParameterType.Should().Be(typeof(long));
|
||||||
|
getOrCreateMethod.GetParameters()[1].ParameterType.Should().Be(typeof(string));
|
||||||
|
getOrCreateMethod.GetParameters()[2].ParameterType.Should().Be(typeof(string));
|
||||||
|
|
||||||
|
// Get method
|
||||||
|
var getMethod = methods.FirstOrDefault(m => m.Name == "Get");
|
||||||
|
getMethod.Should().NotBeNull();
|
||||||
|
getMethod!.ReturnType.Should().Be(typeof(ChatSession));
|
||||||
|
getMethod.GetParameters().Should().HaveCount(1);
|
||||||
|
getMethod.GetParameters()[0].ParameterType.Should().Be(typeof(long));
|
||||||
|
|
||||||
|
// Remove method
|
||||||
|
var removeMethod = methods.FirstOrDefault(m => m.Name == "Remove");
|
||||||
|
removeMethod.Should().NotBeNull();
|
||||||
|
removeMethod!.ReturnType.Should().Be(typeof(bool));
|
||||||
|
removeMethod.GetParameters().Should().HaveCount(1);
|
||||||
|
removeMethod.GetParameters()[0].ParameterType.Should().Be(typeof(long));
|
||||||
|
|
||||||
|
// GetActiveSessionsCount method
|
||||||
|
var getActiveSessionsCountMethod = methods.FirstOrDefault(m =>
|
||||||
|
m.Name == "GetActiveSessionsCount"
|
||||||
|
);
|
||||||
|
getActiveSessionsCountMethod.Should().NotBeNull();
|
||||||
|
getActiveSessionsCountMethod!.ReturnType.Should().Be(typeof(int));
|
||||||
|
getActiveSessionsCountMethod.GetParameters().Should().BeEmpty();
|
||||||
|
|
||||||
|
// CleanupOldSessions method
|
||||||
|
var cleanupOldSessionsMethod = methods.FirstOrDefault(m => m.Name == "CleanupOldSessions");
|
||||||
|
cleanupOldSessionsMethod.Should().NotBeNull();
|
||||||
|
cleanupOldSessionsMethod!.ReturnType.Should().Be(typeof(int));
|
||||||
|
cleanupOldSessionsMethod.GetParameters().Should().HaveCount(1);
|
||||||
|
cleanupOldSessionsMethod.GetParameters()[0].ParameterType.Should().Be(typeof(int));
|
||||||
|
|
||||||
|
// SaveSessionAsync method
|
||||||
|
var saveSessionAsyncMethod = methods.FirstOrDefault(m => m.Name == "SaveSessionAsync");
|
||||||
|
saveSessionAsyncMethod.Should().NotBeNull();
|
||||||
|
saveSessionAsyncMethod!.ReturnType.Should().Be(typeof(Task));
|
||||||
|
saveSessionAsyncMethod.GetParameters().Should().HaveCount(1);
|
||||||
|
saveSessionAsyncMethod.GetParameters()[0].ParameterType.Should().Be(typeof(ChatSession));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ISessionStorage_ShouldBeImplementedByDatabaseSessionStorage()
|
||||||
|
{
|
||||||
|
// Arrange & Act
|
||||||
|
var databaseSessionStorageType = typeof(DatabaseSessionStorage);
|
||||||
|
var interfaceType = typeof(ISessionStorage);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
interfaceType.IsAssignableFrom(databaseSessionStorageType).Should().BeTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ISessionStorage_ShouldBeImplementedByInMemorySessionStorage()
|
||||||
|
{
|
||||||
|
// Arrange & Act
|
||||||
|
var inMemorySessionStorageType = typeof(InMemorySessionStorage);
|
||||||
|
var interfaceType = typeof(ISessionStorage);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
interfaceType.IsAssignableFrom(inMemorySessionStorageType).Should().BeTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ISessionStorage_GetOrCreate_ShouldReturnChatSession()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var mock = new Mock<ISessionStorage>();
|
||||||
|
var chatId = 12345L;
|
||||||
|
var chatType = "private";
|
||||||
|
var chatTitle = "Test Chat";
|
||||||
|
var expectedSession = TestDataBuilder.ChatSessions.CreateBasicSession(chatId, chatType);
|
||||||
|
|
||||||
|
mock.Setup(x => x.GetOrCreate(It.IsAny<long>(), It.IsAny<string>(), It.IsAny<string>()))
|
||||||
|
.Returns(expectedSession);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = mock.Object.GetOrCreate(chatId, chatType, chatTitle);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().Be(expectedSession);
|
||||||
|
mock.Verify(x => x.GetOrCreate(chatId, chatType, chatTitle), Times.Once);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ISessionStorage_Get_ShouldReturnChatSessionOrNull()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var mock = new Mock<ISessionStorage>();
|
||||||
|
var chatId = 12345L;
|
||||||
|
var expectedSession = TestDataBuilder.ChatSessions.CreateBasicSession(chatId, "private");
|
||||||
|
|
||||||
|
mock.Setup(x => x.Get(It.IsAny<long>())).Returns(expectedSession);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = mock.Object.Get(chatId);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().Be(expectedSession);
|
||||||
|
mock.Verify(x => x.Get(chatId), Times.Once);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ISessionStorage_Get_ShouldReturnNullWhenSessionNotFound()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var mock = new Mock<ISessionStorage>();
|
||||||
|
var chatId = 12345L;
|
||||||
|
|
||||||
|
mock.Setup(x => x.Get(It.IsAny<long>())).Returns((ChatSession?)null);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = mock.Object.Get(chatId);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().BeNull();
|
||||||
|
mock.Verify(x => x.Get(chatId), Times.Once);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ISessionStorage_Remove_ShouldReturnBoolean()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var mock = new Mock<ISessionStorage>();
|
||||||
|
var chatId = 12345L;
|
||||||
|
var expectedResult = true;
|
||||||
|
|
||||||
|
mock.Setup(x => x.Remove(It.IsAny<long>())).Returns(expectedResult);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = mock.Object.Remove(chatId);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().Be(expectedResult);
|
||||||
|
mock.Verify(x => x.Remove(chatId), Times.Once);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ISessionStorage_GetActiveSessionsCount_ShouldReturnInt()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var mock = new Mock<ISessionStorage>();
|
||||||
|
var expectedCount = 5;
|
||||||
|
|
||||||
|
mock.Setup(x => x.GetActiveSessionsCount()).Returns(expectedCount);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = mock.Object.GetActiveSessionsCount();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().Be(expectedCount);
|
||||||
|
mock.Verify(x => x.GetActiveSessionsCount(), Times.Once);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ISessionStorage_CleanupOldSessions_ShouldReturnInt()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var mock = new Mock<ISessionStorage>();
|
||||||
|
var hoursOld = 24;
|
||||||
|
var expectedCleanedCount = 3;
|
||||||
|
|
||||||
|
mock.Setup(x => x.CleanupOldSessions(It.IsAny<int>())).Returns(expectedCleanedCount);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = mock.Object.CleanupOldSessions(hoursOld);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().Be(expectedCleanedCount);
|
||||||
|
mock.Verify(x => x.CleanupOldSessions(hoursOld), Times.Once);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ISessionStorage_CleanupOldSessions_ShouldUseDefaultValue()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var mock = new Mock<ISessionStorage>();
|
||||||
|
var expectedCleanedCount = 2;
|
||||||
|
|
||||||
|
mock.Setup(x => x.CleanupOldSessions(It.IsAny<int>())).Returns(expectedCleanedCount);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = mock.Object.CleanupOldSessions();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().Be(expectedCleanedCount);
|
||||||
|
mock.Verify(x => x.CleanupOldSessions(24), Times.Once); // Default value is 24
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ISessionStorage_SaveSessionAsync_ShouldReturnTask()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var mock = new Mock<ISessionStorage>();
|
||||||
|
var session = TestDataBuilder.ChatSessions.CreateBasicSession(12345L, "private");
|
||||||
|
|
||||||
|
mock.Setup(x => x.SaveSessionAsync(It.IsAny<ChatSession>())).Returns(Task.CompletedTask);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await mock.Object.SaveSessionAsync(session);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
mock.Verify(x => x.SaveSessionAsync(session), Times.Once);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ISessionStorage_GetOrCreate_ShouldUseDefaultValues()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var mock = new Mock<ISessionStorage>();
|
||||||
|
var chatId = 12345L;
|
||||||
|
var expectedSession = TestDataBuilder.ChatSessions.CreateBasicSession(chatId, "private");
|
||||||
|
|
||||||
|
mock.Setup(x => x.GetOrCreate(It.IsAny<long>(), It.IsAny<string>(), It.IsAny<string>()))
|
||||||
|
.Returns(expectedSession);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = mock.Object.GetOrCreate(chatId);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().Be(expectedSession);
|
||||||
|
mock.Verify(x => x.GetOrCreate(chatId, "private", ""), Times.Once); // Default values
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ISessionStorage_ShouldBePublicInterface()
|
||||||
|
{
|
||||||
|
// Arrange & Act
|
||||||
|
var interfaceType = typeof(ISessionStorage);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
interfaceType.IsPublic.Should().BeTrue();
|
||||||
|
interfaceType.IsInterface.Should().BeTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ISessionStorage_ShouldHaveCorrectNamespace()
|
||||||
|
{
|
||||||
|
// Arrange & Act
|
||||||
|
var interfaceType = typeof(ISessionStorage);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
interfaceType.Namespace.Should().Be("ChatBot.Services.Interfaces");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ISessionStorage_ShouldHaveCorrectGenericConstraints()
|
||||||
|
{
|
||||||
|
// Arrange & Act
|
||||||
|
var interfaceType = typeof(ISessionStorage);
|
||||||
|
var methods = interfaceType.GetMethods();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
// 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");
|
||||||
|
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");
|
||||||
|
cleanupMethod.GetParameters()[0].HasDefaultValue.Should().BeTrue();
|
||||||
|
cleanupMethod.GetParameters()[0].DefaultValue.Should().Be(24);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,272 @@
|
|||||||
|
using ChatBot.Services;
|
||||||
|
using ChatBot.Services.Interfaces;
|
||||||
|
using ChatBot.Tests.TestUtilities;
|
||||||
|
using FluentAssertions;
|
||||||
|
using Moq;
|
||||||
|
using Telegram.Bot.Types;
|
||||||
|
|
||||||
|
namespace ChatBot.Tests.Services.Interfaces;
|
||||||
|
|
||||||
|
public class ITelegramBotClientWrapperTests : UnitTestBase
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void ITelegramBotClientWrapper_ShouldHaveCorrectMethodSignatures()
|
||||||
|
{
|
||||||
|
// Arrange & Act
|
||||||
|
var interfaceType = typeof(ITelegramBotClientWrapper);
|
||||||
|
var methods = interfaceType.GetMethods();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
methods.Should().HaveCount(1);
|
||||||
|
|
||||||
|
// GetMeAsync method
|
||||||
|
var getMeAsyncMethod = methods.FirstOrDefault(m => m.Name == "GetMeAsync");
|
||||||
|
getMeAsyncMethod.Should().NotBeNull();
|
||||||
|
getMeAsyncMethod!.ReturnType.Should().Be(typeof(Task<User>));
|
||||||
|
getMeAsyncMethod.GetParameters().Should().HaveCount(1);
|
||||||
|
getMeAsyncMethod.GetParameters()[0].ParameterType.Should().Be(typeof(CancellationToken));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ITelegramBotClientWrapper_ShouldBeImplementedByTelegramBotClientWrapper()
|
||||||
|
{
|
||||||
|
// Arrange & Act
|
||||||
|
var telegramBotClientWrapperType = typeof(TelegramBotClientWrapper);
|
||||||
|
var interfaceType = typeof(ITelegramBotClientWrapper);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
interfaceType.IsAssignableFrom(telegramBotClientWrapperType).Should().BeTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ITelegramBotClientWrapper_GetMeAsync_ShouldReturnUser()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var mock = new Mock<ITelegramBotClientWrapper>();
|
||||||
|
var cancellationToken = CancellationToken.None;
|
||||||
|
var expectedUser = new User
|
||||||
|
{
|
||||||
|
Id = 123456789,
|
||||||
|
IsBot = true,
|
||||||
|
FirstName = "TestBot",
|
||||||
|
Username = "test_bot",
|
||||||
|
};
|
||||||
|
|
||||||
|
mock.Setup(x => x.GetMeAsync(It.IsAny<CancellationToken>())).ReturnsAsync(expectedUser);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await mock.Object.GetMeAsync(cancellationToken);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().Be(expectedUser);
|
||||||
|
mock.Verify(x => x.GetMeAsync(cancellationToken), Times.Once);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ITelegramBotClientWrapper_GetMeAsync_ShouldHandleCancellationToken()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var mock = new Mock<ITelegramBotClientWrapper>();
|
||||||
|
var cancellationToken = new CancellationToken(true); // Cancelled token
|
||||||
|
var expectedUser = new User
|
||||||
|
{
|
||||||
|
Id = 123456789,
|
||||||
|
IsBot = true,
|
||||||
|
FirstName = "TestBot",
|
||||||
|
Username = "test_bot",
|
||||||
|
};
|
||||||
|
|
||||||
|
mock.Setup(x => x.GetMeAsync(It.IsAny<CancellationToken>())).ReturnsAsync(expectedUser);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await mock.Object.GetMeAsync(cancellationToken);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().Be(expectedUser);
|
||||||
|
mock.Verify(x => x.GetMeAsync(cancellationToken), Times.Once);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ITelegramBotClientWrapper_GetMeAsync_ShouldUseDefaultCancellationToken()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var mock = new Mock<ITelegramBotClientWrapper>();
|
||||||
|
var expectedUser = new User
|
||||||
|
{
|
||||||
|
Id = 123456789,
|
||||||
|
IsBot = true,
|
||||||
|
FirstName = "TestBot",
|
||||||
|
Username = "test_bot",
|
||||||
|
};
|
||||||
|
|
||||||
|
mock.Setup(x => x.GetMeAsync(It.IsAny<CancellationToken>())).ReturnsAsync(expectedUser);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await mock.Object.GetMeAsync();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().Be(expectedUser);
|
||||||
|
mock.Verify(x => x.GetMeAsync(CancellationToken.None), Times.Once);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ITelegramBotClientWrapper_GetMeAsync_ShouldHandleUserWithAllProperties()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var mock = new Mock<ITelegramBotClientWrapper>();
|
||||||
|
var cancellationToken = CancellationToken.None;
|
||||||
|
var expectedUser = new User
|
||||||
|
{
|
||||||
|
Id = 987654321,
|
||||||
|
IsBot = true,
|
||||||
|
FirstName = "AdvancedBot",
|
||||||
|
LastName = "Test",
|
||||||
|
Username = "advanced_test_bot",
|
||||||
|
LanguageCode = "en",
|
||||||
|
IsPremium = true,
|
||||||
|
AddedToAttachmentMenu = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
mock.Setup(x => x.GetMeAsync(It.IsAny<CancellationToken>())).ReturnsAsync(expectedUser);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await mock.Object.GetMeAsync(cancellationToken);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().Be(expectedUser);
|
||||||
|
result.Id.Should().Be(987654321);
|
||||||
|
result.IsBot.Should().BeTrue();
|
||||||
|
result.FirstName.Should().Be("AdvancedBot");
|
||||||
|
result.LastName.Should().Be("Test");
|
||||||
|
result.Username.Should().Be("advanced_test_bot");
|
||||||
|
result.LanguageCode.Should().Be("en");
|
||||||
|
result.IsPremium.Should().BeTrue();
|
||||||
|
result.AddedToAttachmentMenu.Should().BeTrue();
|
||||||
|
mock.Verify(x => x.GetMeAsync(cancellationToken), Times.Once);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ITelegramBotClientWrapper_GetMeAsync_ShouldHandleMinimalUser()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var mock = new Mock<ITelegramBotClientWrapper>();
|
||||||
|
var cancellationToken = CancellationToken.None;
|
||||||
|
var expectedUser = new User
|
||||||
|
{
|
||||||
|
Id = 111111111,
|
||||||
|
IsBot = false,
|
||||||
|
FirstName = "MinimalUser",
|
||||||
|
};
|
||||||
|
|
||||||
|
mock.Setup(x => x.GetMeAsync(It.IsAny<CancellationToken>())).ReturnsAsync(expectedUser);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await mock.Object.GetMeAsync(cancellationToken);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().Be(expectedUser);
|
||||||
|
result.Id.Should().Be(111111111);
|
||||||
|
result.IsBot.Should().BeFalse();
|
||||||
|
result.FirstName.Should().Be("MinimalUser");
|
||||||
|
result.LastName.Should().BeNull();
|
||||||
|
result.Username.Should().BeNull();
|
||||||
|
result.LanguageCode.Should().BeNull();
|
||||||
|
mock.Verify(x => x.GetMeAsync(cancellationToken), Times.Once);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ITelegramBotClientWrapper_ShouldBePublicInterface()
|
||||||
|
{
|
||||||
|
// Arrange & Act
|
||||||
|
var interfaceType = typeof(ITelegramBotClientWrapper);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
interfaceType.IsPublic.Should().BeTrue();
|
||||||
|
interfaceType.IsInterface.Should().BeTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ITelegramBotClientWrapper_ShouldHaveCorrectNamespace()
|
||||||
|
{
|
||||||
|
// Arrange & Act
|
||||||
|
var interfaceType = typeof(ITelegramBotClientWrapper);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
interfaceType.Namespace.Should().Be("ChatBot.Services.Interfaces");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ITelegramBotClientWrapper_ShouldHaveCorrectGenericConstraints()
|
||||||
|
{
|
||||||
|
// Arrange & Act
|
||||||
|
var interfaceType = typeof(ITelegramBotClientWrapper);
|
||||||
|
var methods = interfaceType.GetMethods();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
// All methods should be public
|
||||||
|
methods.All(m => m.IsPublic).Should().BeTrue();
|
||||||
|
|
||||||
|
// GetMeAsync should have default parameter for CancellationToken
|
||||||
|
var getMeAsyncMethod = methods.First(m => m.Name == "GetMeAsync");
|
||||||
|
getMeAsyncMethod.GetParameters()[0].HasDefaultValue.Should().BeTrue();
|
||||||
|
getMeAsyncMethod.GetParameters()[0].DefaultValue.Should().BeNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ITelegramBotClientWrapper_GetMeAsync_ShouldHandleMultipleCalls()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var mock = new Mock<ITelegramBotClientWrapper>();
|
||||||
|
var cancellationToken = CancellationToken.None;
|
||||||
|
var expectedUser = new User
|
||||||
|
{
|
||||||
|
Id = 123456789,
|
||||||
|
IsBot = true,
|
||||||
|
FirstName = "TestBot",
|
||||||
|
Username = "test_bot",
|
||||||
|
};
|
||||||
|
|
||||||
|
mock.Setup(x => x.GetMeAsync(It.IsAny<CancellationToken>())).ReturnsAsync(expectedUser);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result1 = await mock.Object.GetMeAsync(cancellationToken);
|
||||||
|
var result2 = await mock.Object.GetMeAsync(cancellationToken);
|
||||||
|
var result3 = await mock.Object.GetMeAsync(cancellationToken);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result1.Should().Be(expectedUser);
|
||||||
|
result2.Should().Be(expectedUser);
|
||||||
|
result3.Should().Be(expectedUser);
|
||||||
|
mock.Verify(x => x.GetMeAsync(cancellationToken), Times.Exactly(3));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ITelegramBotClientWrapper_GetMeAsync_ShouldHandleConcurrentCalls()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var mock = new Mock<ITelegramBotClientWrapper>();
|
||||||
|
var cancellationToken = CancellationToken.None;
|
||||||
|
var expectedUser = new User
|
||||||
|
{
|
||||||
|
Id = 123456789,
|
||||||
|
IsBot = true,
|
||||||
|
FirstName = "TestBot",
|
||||||
|
Username = "test_bot",
|
||||||
|
};
|
||||||
|
|
||||||
|
mock.Setup(x => x.GetMeAsync(It.IsAny<CancellationToken>())).ReturnsAsync(expectedUser);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var tasks = new List<Task<User>>();
|
||||||
|
for (int i = 0; i < 10; i++)
|
||||||
|
{
|
||||||
|
tasks.Add(mock.Object.GetMeAsync(cancellationToken));
|
||||||
|
}
|
||||||
|
|
||||||
|
var results = await Task.WhenAll(tasks);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
results.Should().AllBeEquivalentTo(expectedUser);
|
||||||
|
mock.Verify(x => x.GetMeAsync(cancellationToken), Times.Exactly(10));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -90,12 +90,12 @@
|
|||||||
- [x] `TelegramMessageSender` - тесты отправки различных типов сообщений
|
- [x] `TelegramMessageSender` - тесты отправки различных типов сообщений
|
||||||
|
|
||||||
### 6. Интерфейсы
|
### 6. Интерфейсы
|
||||||
- [ ] `IAIService` - тесты интерфейса
|
- [x] `IAIService` - тесты интерфейса
|
||||||
- [ ] `ISessionStorage` - тесты интерфейса
|
- [x] `ISessionStorage` - тесты интерфейса
|
||||||
- [ ] `IHistoryCompressionService` - тесты интерфейса
|
- [x] `IHistoryCompressionService` - тесты интерфейса
|
||||||
- [ ] `IOllamaClient` - тесты интерфейса
|
- [x] `IOllamaClient` - тесты интерфейса
|
||||||
- [ ] `ITelegramBotClientWrapper` - тесты интерфейса
|
- [x] `ITelegramBotClientWrapper` - тесты интерфейса
|
||||||
- [ ] `IChatSessionRepository` - тесты интерфейса
|
- [x] `IChatSessionRepository` - тесты интерфейса
|
||||||
|
|
||||||
### 7. Контекст базы данных
|
### 7. Контекст базы данных
|
||||||
- [ ] `ChatBotDbContext` - тесты контекста БД
|
- [ ] `ChatBotDbContext` - тесты контекста БД
|
||||||
|
|||||||
Reference in New Issue
Block a user