368 lines
12 KiB
C#
368 lines
12 KiB
C#
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();
|
|
}
|
|
}
|