Add more test
Some checks failed
SonarQube / Build and analyze (push) Failing after 2m59s
Unit Tests / Run Tests (push) Failing after 2m22s

This commit is contained in:
Leonid Pershin
2025-10-20 08:36:57 +03:00
parent c9eac74e35
commit e011bb667f
7 changed files with 2120 additions and 6 deletions

View 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");
}
}

View File

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

View 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;
}
}
}

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

View File

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