This commit is contained in:
@@ -1,43 +0,0 @@
|
|||||||
name: Unit Tests
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- master
|
|
||||||
- develop
|
|
||||||
pull_request:
|
|
||||||
types: [opened, synchronize, reopened]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
test:
|
|
||||||
name: Run Tests
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
- name: Setup .NET
|
|
||||||
uses: actions/setup-dotnet@v4
|
|
||||||
with:
|
|
||||||
dotnet-version: '9.0.x'
|
|
||||||
|
|
||||||
- name: Restore dependencies
|
|
||||||
run: dotnet restore --verbosity normal
|
|
||||||
|
|
||||||
- name: Build solution
|
|
||||||
run: dotnet build --no-restore --verbosity normal
|
|
||||||
|
|
||||||
- name: Run unit tests
|
|
||||||
run: dotnet test --no-build --verbosity minimal --logger "trx;LogFileName=test-results.trx" --results-directory ./TestResults
|
|
||||||
|
|
||||||
- name: Generate test report
|
|
||||||
if: always()
|
|
||||||
run: |
|
|
||||||
echo "Test results:"
|
|
||||||
find ./TestResults -name "*.trx" -exec echo "Found test result file: {}" \;
|
|
||||||
echo "File sizes:"
|
|
||||||
find ./TestResults -name "*.trx" -exec ls -lh {} \;
|
|
||||||
echo "✅ Tests completed successfully"
|
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net9.0</TargetFramework>
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
@@ -20,6 +20,7 @@
|
|||||||
<PackageReference Include="FluentAssertions" Version="8.7.1" />
|
<PackageReference Include="FluentAssertions" Version="8.7.1" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="9.0.10" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="9.0.10" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="9.0.10" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="9.0.10" />
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.10" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.10" />
|
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.10" />
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.10" />
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.10" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.10" />
|
<PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.10" />
|
||||||
|
|||||||
@@ -261,4 +261,234 @@ public class ChatSessionRepositoryTests : TestBase
|
|||||||
var remainingSessions = await _repository.GetActiveSessionsCountAsync();
|
var remainingSessions = await _repository.GetActiveSessionsCountAsync();
|
||||||
remainingSessions.Should().Be(1);
|
remainingSessions.Should().Be(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task GetMessagesAsync_ShouldReturnMessages_WhenMessagesExist()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
CleanupDatabase();
|
||||||
|
var session = TestDataBuilder.Mocks.CreateChatSessionEntity(1, 12345);
|
||||||
|
_dbContext.ChatSessions.Add(session);
|
||||||
|
await _dbContext.SaveChangesAsync();
|
||||||
|
|
||||||
|
await _repository.AddMessageAsync(session.Id, "Message 1", "User", 0);
|
||||||
|
await _repository.AddMessageAsync(session.Id, "Message 2", "Assistant", 1);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var messages = await _repository.GetMessagesAsync(session.Id);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
messages.Should().HaveCount(2);
|
||||||
|
messages[0].Content.Should().Be("Message 1");
|
||||||
|
messages[0].Role.Should().Be("User");
|
||||||
|
messages[1].Content.Should().Be("Message 2");
|
||||||
|
messages[1].Role.Should().Be("Assistant");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task GetMessagesAsync_ShouldReturnEmptyList_WhenNoMessages()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
CleanupDatabase();
|
||||||
|
var session = TestDataBuilder.Mocks.CreateChatSessionEntity(1, 12345);
|
||||||
|
_dbContext.ChatSessions.Add(session);
|
||||||
|
await _dbContext.SaveChangesAsync();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var messages = await _repository.GetMessagesAsync(session.Id);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
messages.Should().BeEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task AddMessageAsync_ShouldAddMessage()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
CleanupDatabase();
|
||||||
|
var session = TestDataBuilder.Mocks.CreateChatSessionEntity(1, 12345);
|
||||||
|
_dbContext.ChatSessions.Add(session);
|
||||||
|
await _dbContext.SaveChangesAsync();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var message = await _repository.AddMessageAsync(session.Id, "Test message", "User", 0);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
message.Should().NotBeNull();
|
||||||
|
message.Content.Should().Be("Test message");
|
||||||
|
message.Role.Should().Be("User");
|
||||||
|
message.MessageOrder.Should().Be(0);
|
||||||
|
message.SessionId.Should().Be(session.Id);
|
||||||
|
|
||||||
|
var messages = await _repository.GetMessagesAsync(session.Id);
|
||||||
|
messages.Should().HaveCount(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ClearMessagesAsync_ShouldRemoveAllMessages()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
CleanupDatabase();
|
||||||
|
var session = TestDataBuilder.Mocks.CreateChatSessionEntity(1, 12345);
|
||||||
|
_dbContext.ChatSessions.Add(session);
|
||||||
|
await _dbContext.SaveChangesAsync();
|
||||||
|
|
||||||
|
await _repository.AddMessageAsync(session.Id, "Message 1", "User", 0);
|
||||||
|
await _repository.AddMessageAsync(session.Id, "Message 2", "Assistant", 1);
|
||||||
|
await _repository.AddMessageAsync(session.Id, "Message 3", "User", 2);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await _repository.ClearMessagesAsync(session.Id);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var messages = await _repository.GetMessagesAsync(session.Id);
|
||||||
|
messages.Should().BeEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task GetSessionsForCleanupAsync_ShouldReturnOldSessions()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
CleanupDatabase();
|
||||||
|
|
||||||
|
var oldSession1 = TestDataBuilder.Mocks.CreateChatSessionEntity(1, 12345);
|
||||||
|
oldSession1.LastUpdatedAt = DateTime.UtcNow.AddDays(-2);
|
||||||
|
|
||||||
|
var oldSession2 = TestDataBuilder.Mocks.CreateChatSessionEntity(2, 12346);
|
||||||
|
oldSession2.LastUpdatedAt = DateTime.UtcNow.AddHours(-25);
|
||||||
|
|
||||||
|
var recentSession = TestDataBuilder.Mocks.CreateChatSessionEntity(3, 12347);
|
||||||
|
recentSession.LastUpdatedAt = DateTime.UtcNow.AddMinutes(-30);
|
||||||
|
|
||||||
|
_dbContext.ChatSessions.AddRange(oldSession1, oldSession2, recentSession);
|
||||||
|
await _dbContext.SaveChangesAsync();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var sessionsForCleanup = await _repository.GetSessionsForCleanupAsync(24);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
sessionsForCleanup.Should().HaveCount(2);
|
||||||
|
sessionsForCleanup.Should().Contain(s => s.ChatId == 12345);
|
||||||
|
sessionsForCleanup.Should().Contain(s => s.ChatId == 12346);
|
||||||
|
sessionsForCleanup.Should().NotContain(s => s.ChatId == 12347);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task GetSessionsForCleanupAsync_ShouldReturnEmptyList_WhenNoOldSessions()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
CleanupDatabase();
|
||||||
|
|
||||||
|
var recentSession = TestDataBuilder.Mocks.CreateChatSessionEntity(1, 12345);
|
||||||
|
recentSession.LastUpdatedAt = DateTime.UtcNow.AddMinutes(-30);
|
||||||
|
|
||||||
|
_dbContext.ChatSessions.Add(recentSession);
|
||||||
|
await _dbContext.SaveChangesAsync();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var sessionsForCleanup = await _repository.GetSessionsForCleanupAsync(24);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
sessionsForCleanup.Should().BeEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task UpdateAsync_ShouldUpdateLastUpdatedAt()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
CleanupDatabase();
|
||||||
|
var session = TestDataBuilder.Mocks.CreateChatSessionEntity(1, 12345);
|
||||||
|
var originalLastUpdated = DateTime.UtcNow.AddDays(-1);
|
||||||
|
session.LastUpdatedAt = originalLastUpdated;
|
||||||
|
_dbContext.ChatSessions.Add(session);
|
||||||
|
await _dbContext.SaveChangesAsync();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
session.Model = "new-model";
|
||||||
|
var updatedSession = await _repository.UpdateAsync(session);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
updatedSession.LastUpdatedAt.Should().BeAfter(originalLastUpdated);
|
||||||
|
updatedSession.Model.Should().Be("new-model");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task GetByChatIdAsync_ShouldIncludeMessages()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
CleanupDatabase();
|
||||||
|
var session = TestDataBuilder.Mocks.CreateChatSessionEntity(1, 12345);
|
||||||
|
_dbContext.ChatSessions.Add(session);
|
||||||
|
await _dbContext.SaveChangesAsync();
|
||||||
|
|
||||||
|
await _repository.AddMessageAsync(session.Id, "Test message", "User", 0);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var retrievedSession = await _repository.GetByChatIdAsync(12345);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
retrievedSession.Should().NotBeNull();
|
||||||
|
retrievedSession!.Messages.Should().HaveCount(1);
|
||||||
|
retrievedSession.Messages.First().Content.Should().Be("Test message");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task GetBySessionIdAsync_ShouldIncludeMessages()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
CleanupDatabase();
|
||||||
|
var sessionId = "test-session-id";
|
||||||
|
var session = TestDataBuilder.Mocks.CreateChatSessionEntity(1, 12345, sessionId);
|
||||||
|
_dbContext.ChatSessions.Add(session);
|
||||||
|
await _dbContext.SaveChangesAsync();
|
||||||
|
|
||||||
|
await _repository.AddMessageAsync(session.Id, "Test message", "User", 0);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var retrievedSession = await _repository.GetBySessionIdAsync(sessionId);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
retrievedSession.Should().NotBeNull();
|
||||||
|
retrievedSession!.Messages.Should().HaveCount(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task AddMessageAsync_WithMultipleMessages_ShouldMaintainOrder()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
CleanupDatabase();
|
||||||
|
var session = TestDataBuilder.Mocks.CreateChatSessionEntity(1, 12345);
|
||||||
|
_dbContext.ChatSessions.Add(session);
|
||||||
|
await _dbContext.SaveChangesAsync();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await _repository.AddMessageAsync(session.Id, "Message 1", "User", 0);
|
||||||
|
await _repository.AddMessageAsync(session.Id, "Message 2", "Assistant", 1);
|
||||||
|
await _repository.AddMessageAsync(session.Id, "Message 3", "User", 2);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var messages = await _repository.GetMessagesAsync(session.Id);
|
||||||
|
messages.Should().HaveCount(3);
|
||||||
|
messages[0].MessageOrder.Should().Be(0);
|
||||||
|
messages[1].MessageOrder.Should().Be(1);
|
||||||
|
messages[2].MessageOrder.Should().Be(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task CleanupOldSessionsAsync_WithNoOldSessions_ShouldReturnZero()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
CleanupDatabase();
|
||||||
|
|
||||||
|
var recentSession = TestDataBuilder.Mocks.CreateChatSessionEntity(1, 12345);
|
||||||
|
recentSession.LastUpdatedAt = DateTime.UtcNow.AddMinutes(-10);
|
||||||
|
_dbContext.ChatSessions.Add(recentSession);
|
||||||
|
await _dbContext.SaveChangesAsync();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var removedCount = await _repository.CleanupOldSessionsAsync(24);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
removedCount.Should().Be(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ public class ChatSessionTests
|
|||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
session.GetAllMessages().Should().HaveCount(1);
|
session.GetAllMessages().Should().HaveCount(1);
|
||||||
var message = session.GetAllMessages().First();
|
var message = session.GetAllMessages()[0];
|
||||||
message.Role.Should().Be(ChatRole.User);
|
message.Role.Should().Be(ChatRole.User);
|
||||||
message.Content.Should().Be(content);
|
message.Content.Should().Be(content);
|
||||||
}
|
}
|
||||||
@@ -55,7 +55,7 @@ public class ChatSessionTests
|
|||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
session.GetAllMessages().Should().HaveCount(1);
|
session.GetAllMessages().Should().HaveCount(1);
|
||||||
var message = session.GetAllMessages().First();
|
var message = session.GetAllMessages()[0];
|
||||||
message.Role.Should().Be(ChatRole.Assistant);
|
message.Role.Should().Be(ChatRole.Assistant);
|
||||||
message.Content.Should().Be(content);
|
message.Content.Should().Be(content);
|
||||||
}
|
}
|
||||||
@@ -73,7 +73,7 @@ public class ChatSessionTests
|
|||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
session.GetAllMessages().Should().HaveCount(1);
|
session.GetAllMessages().Should().HaveCount(1);
|
||||||
var addedMessage = session.GetAllMessages().First();
|
var addedMessage = session.GetAllMessages()[0];
|
||||||
addedMessage.Role.Should().Be(ChatRole.System);
|
addedMessage.Role.Should().Be(ChatRole.System);
|
||||||
addedMessage.Content.Should().Be(content);
|
addedMessage.Content.Should().Be(content);
|
||||||
}
|
}
|
||||||
@@ -183,4 +183,319 @@ public class ChatSessionTests
|
|||||||
// Assert
|
// Assert
|
||||||
count.Should().Be(1);
|
count.Should().Be(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AddUserMessage_InGroupChat_ShouldIncludeUsername()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var session = new ChatSession { ChatType = "group" };
|
||||||
|
var content = "Hello";
|
||||||
|
var username = "testuser";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
session.AddUserMessage(content, username);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var message = session.GetAllMessages()[0];
|
||||||
|
message.Content.Should().Be("testuser: Hello");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AddUserMessage_InPrivateChat_ShouldNotIncludeUsername()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var session = new ChatSession { ChatType = "private" };
|
||||||
|
var content = "Hello";
|
||||||
|
var username = "testuser";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
session.AddUserMessage(content, username);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var message = session.GetAllMessages()[0];
|
||||||
|
message.Content.Should().Be("Hello");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AddMessage_ShouldTrimHistory_WhenExceedsMaxHistoryLength()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var session = new ChatSession { MaxHistoryLength = 5 };
|
||||||
|
|
||||||
|
// Add system message
|
||||||
|
session.AddMessage(new ChatMessage { Role = ChatRole.System, Content = "System prompt" });
|
||||||
|
|
||||||
|
// Add messages to exceed max history
|
||||||
|
for (int i = 0; i < 10; i++)
|
||||||
|
{
|
||||||
|
session.AddMessage(new ChatMessage { Role = ChatRole.User, Content = $"Message {i}" });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
session.GetMessageCount().Should().BeLessThanOrEqualTo(5);
|
||||||
|
// System message should be preserved
|
||||||
|
session.GetAllMessages()[0].Role.Should().Be(ChatRole.System);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AddMessage_ShouldTrimHistory_WithoutSystemMessage()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var session = new ChatSession { MaxHistoryLength = 3 };
|
||||||
|
|
||||||
|
// Add messages to exceed max history
|
||||||
|
for (int i = 0; i < 5; i++)
|
||||||
|
{
|
||||||
|
session.AddMessage(new ChatMessage { Role = ChatRole.User, Content = $"Message {i}" });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
session.GetMessageCount().Should().BeLessThanOrEqualTo(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void SetCompressionService_ShouldSetService()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var session = new ChatSession();
|
||||||
|
var compressionServiceMock =
|
||||||
|
new Moq.Mock<ChatBot.Services.Interfaces.IHistoryCompressionService>();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
session.SetCompressionService(compressionServiceMock.Object);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
// The service should be set (no exception)
|
||||||
|
session.Should().NotBeNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task AddMessageWithCompressionAsync_WithoutCompressionService_ShouldUseTrimming()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var session = new ChatSession { MaxHistoryLength = 3 };
|
||||||
|
|
||||||
|
// Act
|
||||||
|
for (int i = 0; i < 5; i++)
|
||||||
|
{
|
||||||
|
await session.AddMessageWithCompressionAsync(
|
||||||
|
new ChatMessage { Role = ChatRole.User, Content = $"Message {i}" },
|
||||||
|
10,
|
||||||
|
5
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
session.GetMessageCount().Should().BeLessThanOrEqualTo(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task AddUserMessageWithCompressionAsync_ShouldAddMessage()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var session = new ChatSession { ChatType = "private" };
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await session.AddUserMessageWithCompressionAsync("Test message", "user", 10, 5);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
session.GetMessageCount().Should().Be(1);
|
||||||
|
session.GetAllMessages()[0].Content.Should().Be("Test message");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task AddUserMessageWithCompressionAsync_InGroupChat_ShouldIncludeUsername()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var session = new ChatSession { ChatType = "group" };
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await session.AddUserMessageWithCompressionAsync("Test message", "user", 10, 5);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
session.GetMessageCount().Should().Be(1);
|
||||||
|
session.GetAllMessages()[0].Content.Should().Be("user: Test message");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task AddAssistantMessageWithCompressionAsync_ShouldAddMessage()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var session = new ChatSession();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await session.AddAssistantMessageWithCompressionAsync("Test response", 10, 5);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
session.GetMessageCount().Should().Be(1);
|
||||||
|
session.GetAllMessages()[0].Role.Should().Be(ChatRole.Assistant);
|
||||||
|
session.GetAllMessages()[0].Content.Should().Be("Test response");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task AddMessageWithCompressionAsync_ShouldTriggerTrimming_WhenNoCompressionService()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var session = new ChatSession { MaxHistoryLength = 2 };
|
||||||
|
|
||||||
|
// Act
|
||||||
|
for (int i = 0; i < 4; i++)
|
||||||
|
{
|
||||||
|
await session.AddMessageWithCompressionAsync(
|
||||||
|
new ChatMessage { Role = ChatRole.User, Content = $"Message {i}" },
|
||||||
|
2,
|
||||||
|
1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
session.GetMessageCount().Should().BeLessThanOrEqualTo(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ClearHistory_ShouldUpdateLastUpdatedAt()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var session = new ChatSession();
|
||||||
|
session.AddUserMessage("Test", "user");
|
||||||
|
var lastUpdated = session.LastUpdatedAt;
|
||||||
|
await Task.Delay(10); // Small delay
|
||||||
|
|
||||||
|
// Act
|
||||||
|
session.ClearHistory();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
session.LastUpdatedAt.Should().BeAfter(lastUpdated);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void SetCreatedAtForTesting_ShouldUpdateCreatedAt()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var session = new ChatSession();
|
||||||
|
var targetDate = DateTime.UtcNow.AddDays(-5);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
session.SetCreatedAtForTesting(targetDate);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
session.CreatedAt.Should().Be(targetDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AddMessage_MultipleTimes_ShouldMaintainOrder()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var session = new ChatSession();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
session.AddUserMessage("Message 1", "user1");
|
||||||
|
session.AddAssistantMessage("Response 1");
|
||||||
|
session.AddUserMessage("Message 2", "user1");
|
||||||
|
session.AddAssistantMessage("Response 2");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var messages = session.GetAllMessages();
|
||||||
|
messages.Should().HaveCount(4);
|
||||||
|
messages[0].Content.Should().Be("Message 1");
|
||||||
|
messages[1].Content.Should().Be("Response 1");
|
||||||
|
messages[2].Content.Should().Be("Message 2");
|
||||||
|
messages[3].Content.Should().Be("Response 2");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AddMessage_WithSystemMessage_ShouldPreserveSystemMessage()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var session = new ChatSession { MaxHistoryLength = 3 };
|
||||||
|
session.AddMessage(new ChatMessage { Role = ChatRole.System, Content = "System prompt" });
|
||||||
|
|
||||||
|
// Act
|
||||||
|
for (int i = 0; i < 5; i++)
|
||||||
|
{
|
||||||
|
session.AddUserMessage($"Message {i}", "user");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var messages = session.GetAllMessages();
|
||||||
|
messages[0].Role.Should().Be(ChatRole.System);
|
||||||
|
messages[0].Content.Should().Be("System prompt");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetAllMessages_ShouldReturnCopy()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var session = new ChatSession();
|
||||||
|
session.AddUserMessage("Test", "user");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var messages1 = session.GetAllMessages();
|
||||||
|
var messages2 = session.GetAllMessages();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
messages1.Should().NotBeSameAs(messages2);
|
||||||
|
messages1.Should().BeEquivalentTo(messages2);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ChatSession_ThreadSafety_MultipleConcurrentAdds()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var session = new ChatSession();
|
||||||
|
var tasks = new List<Task>();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
for (int i = 0; i < 10; i++)
|
||||||
|
{
|
||||||
|
int messageNum = i;
|
||||||
|
tasks.Add(Task.Run(() => session.AddUserMessage($"Message {messageNum}", "user")));
|
||||||
|
}
|
||||||
|
|
||||||
|
await Task.WhenAll(tasks.ToArray());
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
session.GetMessageCount().Should().Be(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MaxHistoryLength_ShouldBeSettable()
|
||||||
|
{
|
||||||
|
// Arrange & Act
|
||||||
|
var session = new ChatSession { MaxHistoryLength = 50 };
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
session.MaxHistoryLength.Should().Be(50);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Model_ShouldBeSettable()
|
||||||
|
{
|
||||||
|
// Arrange & Act
|
||||||
|
var session = new ChatSession { Model = "llama3" };
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
session.Model.Should().Be("llama3");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void SessionProperties_ShouldBeSettableViaInitializer()
|
||||||
|
{
|
||||||
|
// Arrange & Act
|
||||||
|
var session = new ChatSession
|
||||||
|
{
|
||||||
|
ChatId = 12345,
|
||||||
|
ChatType = "group",
|
||||||
|
ChatTitle = "Test Group",
|
||||||
|
Model = "llama3",
|
||||||
|
MaxHistoryLength = 50,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
session.ChatId.Should().Be(12345);
|
||||||
|
session.ChatType.Should().Be("group");
|
||||||
|
session.ChatTitle.Should().Be("Test Group");
|
||||||
|
session.Model.Should().Be("llama3");
|
||||||
|
session.MaxHistoryLength.Should().Be(50);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -255,4 +255,328 @@ public class DatabaseInitializationServiceTests : UnitTestBase
|
|||||||
Times.Once
|
Times.Once
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task StartAsync_WhenDatabaseExists_ShouldLogAndMigrate()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var dbPath = $"TestDb_{Guid.NewGuid()}.db";
|
||||||
|
var services = new ServiceCollection();
|
||||||
|
services.AddDbContext<ChatBotDbContext>(options =>
|
||||||
|
options.UseSqlite($"Data Source={dbPath}")
|
||||||
|
.ConfigureWarnings(w => w.Ignore(Microsoft.EntityFrameworkCore.Diagnostics.RelationalEventId.PendingModelChangesWarning))
|
||||||
|
);
|
||||||
|
|
||||||
|
var serviceProvider = services.BuildServiceProvider();
|
||||||
|
var loggerMock = new Mock<ILogger<DatabaseInitializationService>>();
|
||||||
|
|
||||||
|
var service = new DatabaseInitializationService(serviceProvider, loggerMock.Object);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Act
|
||||||
|
await service.StartAsync(CancellationToken.None);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
loggerMock.Verify(
|
||||||
|
x =>
|
||||||
|
x.Log(
|
||||||
|
LogLevel.Information,
|
||||||
|
It.IsAny<EventId>(),
|
||||||
|
It.Is<It.IsAnyType>(
|
||||||
|
(v, t) => v.ToString()!.Contains("Starting database initialization")
|
||||||
|
),
|
||||||
|
It.IsAny<Exception>(),
|
||||||
|
It.IsAny<Func<It.IsAnyType, Exception?, string>>()
|
||||||
|
),
|
||||||
|
Times.Once
|
||||||
|
);
|
||||||
|
|
||||||
|
loggerMock.Verify(
|
||||||
|
x =>
|
||||||
|
x.Log(
|
||||||
|
LogLevel.Information,
|
||||||
|
It.IsAny<EventId>(),
|
||||||
|
It.Is<It.IsAnyType>(
|
||||||
|
(v, t) =>
|
||||||
|
v.ToString()!
|
||||||
|
.Contains("Database initialization completed successfully")
|
||||||
|
),
|
||||||
|
It.IsAny<Exception>(),
|
||||||
|
It.IsAny<Func<It.IsAnyType, Exception?, string>>()
|
||||||
|
),
|
||||||
|
Times.Once
|
||||||
|
);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
// Cleanup
|
||||||
|
serviceProvider.Dispose();
|
||||||
|
GC.Collect();
|
||||||
|
GC.WaitForPendingFinalizers();
|
||||||
|
if (File.Exists(dbPath))
|
||||||
|
{
|
||||||
|
try { File.Delete(dbPath); } catch { /* Ignore cleanup errors */ }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task StopAsync_WithCancellationToken_ShouldComplete()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var serviceProviderMock = new Mock<IServiceProvider>();
|
||||||
|
var loggerMock = new Mock<ILogger<DatabaseInitializationService>>();
|
||||||
|
var service = new DatabaseInitializationService(
|
||||||
|
serviceProviderMock.Object,
|
||||||
|
loggerMock.Object
|
||||||
|
);
|
||||||
|
var cts = new CancellationTokenSource();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await service.StopAsync(cts.Token);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
loggerMock.Verify(
|
||||||
|
x =>
|
||||||
|
x.Log(
|
||||||
|
LogLevel.Information,
|
||||||
|
It.IsAny<EventId>(),
|
||||||
|
It.Is<It.IsAnyType>(
|
||||||
|
(v, t) => v.ToString()!.Contains("Database initialization service stopped")
|
||||||
|
),
|
||||||
|
It.IsAny<Exception>(),
|
||||||
|
It.IsAny<Func<It.IsAnyType, Exception?, string>>()
|
||||||
|
),
|
||||||
|
Times.Once
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task StopAsync_WhenCancellationRequested_ShouldStillComplete()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var serviceProviderMock = new Mock<IServiceProvider>();
|
||||||
|
var loggerMock = new Mock<ILogger<DatabaseInitializationService>>();
|
||||||
|
var service = new DatabaseInitializationService(
|
||||||
|
serviceProviderMock.Object,
|
||||||
|
loggerMock.Object
|
||||||
|
);
|
||||||
|
var cts = new CancellationTokenSource();
|
||||||
|
cts.Cancel();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await service.StopAsync(cts.Token);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
loggerMock.Verify(
|
||||||
|
x =>
|
||||||
|
x.Log(
|
||||||
|
LogLevel.Information,
|
||||||
|
It.IsAny<EventId>(),
|
||||||
|
It.Is<It.IsAnyType>(
|
||||||
|
(v, t) => v.ToString()!.Contains("Database initialization service stopped")
|
||||||
|
),
|
||||||
|
It.IsAny<Exception>(),
|
||||||
|
It.IsAny<Func<It.IsAnyType, Exception?, string>>()
|
||||||
|
),
|
||||||
|
Times.Once
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task StartAsync_ShouldHandleDatabaseDoesNotExistException()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var dbPath = $"TestDb_{Guid.NewGuid()}.db";
|
||||||
|
var services = new ServiceCollection();
|
||||||
|
services.AddDbContext<ChatBotDbContext>(options =>
|
||||||
|
options.UseSqlite($"Data Source={dbPath}")
|
||||||
|
.ConfigureWarnings(w => w.Ignore(Microsoft.EntityFrameworkCore.Diagnostics.RelationalEventId.PendingModelChangesWarning))
|
||||||
|
);
|
||||||
|
|
||||||
|
var serviceProvider = services.BuildServiceProvider();
|
||||||
|
var loggerMock = new Mock<ILogger<DatabaseInitializationService>>();
|
||||||
|
|
||||||
|
var service = new DatabaseInitializationService(serviceProvider, loggerMock.Object);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Act
|
||||||
|
await service.StartAsync(CancellationToken.None);
|
||||||
|
|
||||||
|
// Assert - service should complete successfully
|
||||||
|
loggerMock.Verify(
|
||||||
|
x =>
|
||||||
|
x.Log(
|
||||||
|
LogLevel.Information,
|
||||||
|
It.IsAny<EventId>(),
|
||||||
|
It.Is<It.IsAnyType>(
|
||||||
|
(v, t) =>
|
||||||
|
v.ToString()!
|
||||||
|
.Contains("Database initialization completed successfully")
|
||||||
|
),
|
||||||
|
It.IsAny<Exception>(),
|
||||||
|
It.IsAny<Func<It.IsAnyType, Exception?, string>>()
|
||||||
|
),
|
||||||
|
Times.Once
|
||||||
|
);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
// Cleanup
|
||||||
|
serviceProvider.Dispose();
|
||||||
|
GC.Collect();
|
||||||
|
GC.WaitForPendingFinalizers();
|
||||||
|
if (File.Exists(dbPath))
|
||||||
|
{
|
||||||
|
try { File.Delete(dbPath); } catch { /* Ignore cleanup errors */ }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task StartAsync_WithValidDatabase_ShouldLogDatabaseExists()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var dbPath = $"TestDb_{Guid.NewGuid()}.db";
|
||||||
|
var services = new ServiceCollection();
|
||||||
|
services.AddDbContext<ChatBotDbContext>(options =>
|
||||||
|
options.UseSqlite($"Data Source={dbPath}")
|
||||||
|
.ConfigureWarnings(w => w.Ignore(Microsoft.EntityFrameworkCore.Diagnostics.RelationalEventId.PendingModelChangesWarning))
|
||||||
|
);
|
||||||
|
|
||||||
|
var serviceProvider = services.BuildServiceProvider();
|
||||||
|
var loggerMock = new Mock<ILogger<DatabaseInitializationService>>();
|
||||||
|
|
||||||
|
var service = new DatabaseInitializationService(serviceProvider, loggerMock.Object);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Act
|
||||||
|
await service.StartAsync(CancellationToken.None);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
loggerMock.Verify(
|
||||||
|
x =>
|
||||||
|
x.Log(
|
||||||
|
LogLevel.Information,
|
||||||
|
It.IsAny<EventId>(),
|
||||||
|
It.Is<It.IsAnyType>((v, t) => true), // Any log message
|
||||||
|
It.IsAny<Exception>(),
|
||||||
|
It.IsAny<Func<It.IsAnyType, Exception?, string>>()
|
||||||
|
),
|
||||||
|
Times.AtLeastOnce
|
||||||
|
);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
// Cleanup
|
||||||
|
serviceProvider.Dispose();
|
||||||
|
GC.Collect();
|
||||||
|
GC.WaitForPendingFinalizers();
|
||||||
|
if (File.Exists(dbPath))
|
||||||
|
{
|
||||||
|
try { File.Delete(dbPath); } catch { /* Ignore cleanup errors */ }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void DatabaseInitializationService_ShouldImplementIHostedService()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var serviceProviderMock = new Mock<IServiceProvider>();
|
||||||
|
var loggerMock = new Mock<ILogger<DatabaseInitializationService>>();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var service = new DatabaseInitializationService(
|
||||||
|
serviceProviderMock.Object,
|
||||||
|
loggerMock.Object
|
||||||
|
);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
service.Should().BeAssignableTo<Microsoft.Extensions.Hosting.IHostedService>();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task StartAsync_MultipleCallsInSequence_ShouldWork()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var dbPath = $"TestDb_{Guid.NewGuid()}.db";
|
||||||
|
var services = new ServiceCollection();
|
||||||
|
services.AddDbContext<ChatBotDbContext>(options =>
|
||||||
|
options.UseSqlite($"Data Source={dbPath}")
|
||||||
|
.ConfigureWarnings(w => w.Ignore(Microsoft.EntityFrameworkCore.Diagnostics.RelationalEventId.PendingModelChangesWarning))
|
||||||
|
);
|
||||||
|
|
||||||
|
var serviceProvider = services.BuildServiceProvider();
|
||||||
|
var loggerMock = new Mock<ILogger<DatabaseInitializationService>>();
|
||||||
|
var service = new DatabaseInitializationService(serviceProvider, loggerMock.Object);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Act
|
||||||
|
await service.StartAsync(CancellationToken.None);
|
||||||
|
await service.StopAsync(CancellationToken.None);
|
||||||
|
|
||||||
|
// Assert - should complete without exceptions
|
||||||
|
loggerMock.Verify(
|
||||||
|
x =>
|
||||||
|
x.Log(
|
||||||
|
LogLevel.Information,
|
||||||
|
It.IsAny<EventId>(),
|
||||||
|
It.Is<It.IsAnyType>(
|
||||||
|
(v, t) =>
|
||||||
|
v.ToString()!
|
||||||
|
.Contains("Database initialization completed successfully")
|
||||||
|
),
|
||||||
|
It.IsAny<Exception>(),
|
||||||
|
It.IsAny<Func<It.IsAnyType, Exception?, string>>()
|
||||||
|
),
|
||||||
|
Times.Once
|
||||||
|
);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
// Cleanup
|
||||||
|
serviceProvider.Dispose();
|
||||||
|
GC.Collect();
|
||||||
|
GC.WaitForPendingFinalizers();
|
||||||
|
if (File.Exists(dbPath))
|
||||||
|
{
|
||||||
|
try { File.Delete(dbPath); } catch { /* Ignore cleanup errors */ }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task StopAsync_WithoutStartAsync_ShouldComplete()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var serviceProviderMock = new Mock<IServiceProvider>();
|
||||||
|
var loggerMock = new Mock<ILogger<DatabaseInitializationService>>();
|
||||||
|
var service = new DatabaseInitializationService(
|
||||||
|
serviceProviderMock.Object,
|
||||||
|
loggerMock.Object
|
||||||
|
);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await service.StopAsync(CancellationToken.None);
|
||||||
|
|
||||||
|
// Assert - should complete without exceptions
|
||||||
|
loggerMock.Verify(
|
||||||
|
x =>
|
||||||
|
x.Log(
|
||||||
|
LogLevel.Information,
|
||||||
|
It.IsAny<EventId>(),
|
||||||
|
It.Is<It.IsAnyType>(
|
||||||
|
(v, t) => v.ToString()!.Contains("Database initialization service stopped")
|
||||||
|
),
|
||||||
|
It.IsAny<Exception>(),
|
||||||
|
It.IsAny<Func<It.IsAnyType, Exception?, string>>()
|
||||||
|
),
|
||||||
|
Times.Once
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -176,4 +176,265 @@ public class DatabaseSessionStorageTests : TestBase
|
|||||||
result.Should().Be(expectedCount);
|
result.Should().Be(expectedCount);
|
||||||
_repositoryMock.Verify(x => x.CleanupOldSessionsAsync(24), Times.Once);
|
_repositoryMock.Verify(x => x.CleanupOldSessionsAsync(24), Times.Once);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetOrCreate_ShouldThrowInvalidOperationException_WhenRepositoryThrows()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
_repositoryMock
|
||||||
|
.Setup(x => x.GetOrCreateAsync(12345, "private", "Test Chat"))
|
||||||
|
.ThrowsAsync(new Exception("Database error"));
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var act = () => _sessionStorage.GetOrCreate(12345, "private", "Test Chat");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
act.Should()
|
||||||
|
.Throw<InvalidOperationException>()
|
||||||
|
.WithMessage("Failed to get or create session for chat 12345")
|
||||||
|
.WithInnerException<Exception>()
|
||||||
|
.WithMessage("Database error");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Get_ShouldReturnNull_WhenRepositoryThrows()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
_repositoryMock
|
||||||
|
.Setup(x => x.GetByChatIdAsync(12345))
|
||||||
|
.ThrowsAsync(new Exception("Database error"));
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = _sessionStorage.Get(12345);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().BeNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task SaveSessionAsync_ShouldLogWarning_WhenSessionNotFound()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var session = TestDataBuilder.ChatSessions.CreateBasicSession(12345, "private");
|
||||||
|
_repositoryMock
|
||||||
|
.Setup(x => x.GetByChatIdAsync(12345))
|
||||||
|
.ReturnsAsync((ChatSessionEntity?)null);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await _sessionStorage.SaveSessionAsync(session);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
_repositoryMock.Verify(x => x.GetByChatIdAsync(12345), Times.Once);
|
||||||
|
_repositoryMock.Verify(x => x.UpdateAsync(It.IsAny<ChatSessionEntity>()), Times.Never);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task SaveSessionAsync_ShouldThrowInvalidOperationException_WhenRepositoryThrows()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var session = TestDataBuilder.ChatSessions.CreateBasicSession(12345, "private");
|
||||||
|
_repositoryMock
|
||||||
|
.Setup(x => x.GetByChatIdAsync(12345))
|
||||||
|
.ThrowsAsync(new Exception("Database error"));
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var act = async () => await _sessionStorage.SaveSessionAsync(session);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var exception = await act.Should()
|
||||||
|
.ThrowAsync<InvalidOperationException>()
|
||||||
|
.WithMessage("Failed to save session for chat 12345");
|
||||||
|
exception
|
||||||
|
.And.InnerException.Should()
|
||||||
|
.BeOfType<Exception>()
|
||||||
|
.Which.Message.Should()
|
||||||
|
.Be("Database error");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task SaveSessionAsync_ShouldClearMessagesAndAddNew()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var session = TestDataBuilder.ChatSessions.CreateBasicSession(12345, "private");
|
||||||
|
session.AddUserMessage("Test message", "user1");
|
||||||
|
session.AddAssistantMessage("Test response");
|
||||||
|
|
||||||
|
var sessionEntity = TestDataBuilder.Mocks.CreateChatSessionEntity();
|
||||||
|
_repositoryMock.Setup(x => x.GetByChatIdAsync(12345)).ReturnsAsync(sessionEntity);
|
||||||
|
_repositoryMock
|
||||||
|
.Setup(x => x.UpdateAsync(It.IsAny<ChatSessionEntity>()))
|
||||||
|
.ReturnsAsync(sessionEntity);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await _sessionStorage.SaveSessionAsync(session);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
_repositoryMock.Verify(x => x.ClearMessagesAsync(It.IsAny<int>()), Times.Once);
|
||||||
|
_repositoryMock.Verify(
|
||||||
|
x =>
|
||||||
|
x.AddMessageAsync(
|
||||||
|
It.IsAny<int>(),
|
||||||
|
It.IsAny<string>(),
|
||||||
|
It.IsAny<string>(),
|
||||||
|
It.IsAny<int>()
|
||||||
|
),
|
||||||
|
Times.Exactly(2)
|
||||||
|
);
|
||||||
|
_repositoryMock.Verify(x => x.UpdateAsync(It.IsAny<ChatSessionEntity>()), Times.Once);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Remove_ShouldReturnFalse_WhenRepositoryThrows()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
_repositoryMock
|
||||||
|
.Setup(x => x.DeleteAsync(12345))
|
||||||
|
.ThrowsAsync(new Exception("Database error"));
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = _sessionStorage.Remove(12345);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().BeFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetActiveSessionsCount_ShouldReturnZero_WhenRepositoryThrows()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
_repositoryMock
|
||||||
|
.Setup(x => x.GetActiveSessionsCountAsync())
|
||||||
|
.ThrowsAsync(new Exception("Database error"));
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = _sessionStorage.GetActiveSessionsCount();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().Be(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void CleanupOldSessions_ShouldReturnZero_WhenRepositoryThrows()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
_repositoryMock
|
||||||
|
.Setup(x => x.CleanupOldSessionsAsync(24))
|
||||||
|
.ThrowsAsync(new Exception("Database error"));
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = _sessionStorage.CleanupOldSessions(24);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().Be(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetOrCreate_WithCompressionService_ShouldSetCompressionService()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var compressionServiceMock = TestDataBuilder.Mocks.CreateCompressionServiceMock();
|
||||||
|
var storageWithCompression = new DatabaseSessionStorage(
|
||||||
|
_repositoryMock.Object,
|
||||||
|
Mock.Of<ILogger<DatabaseSessionStorage>>(),
|
||||||
|
compressionServiceMock.Object
|
||||||
|
);
|
||||||
|
|
||||||
|
var sessionEntity = TestDataBuilder.Mocks.CreateChatSessionEntity();
|
||||||
|
_repositoryMock
|
||||||
|
.Setup(x => x.GetOrCreateAsync(12345, "private", "Test Chat"))
|
||||||
|
.ReturnsAsync(sessionEntity);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = storageWithCompression.GetOrCreate(12345, "private", "Test Chat");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().NotBeNull();
|
||||||
|
result.ChatId.Should().Be(12345);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Get_WithCompressionService_ShouldSetCompressionService()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var loggerMock = new Mock<ILogger<DatabaseSessionStorage>>();
|
||||||
|
var compressionServiceMock = TestDataBuilder.Mocks.CreateCompressionServiceMock();
|
||||||
|
var storageWithCompression = new DatabaseSessionStorage(
|
||||||
|
_repositoryMock.Object,
|
||||||
|
loggerMock.Object,
|
||||||
|
compressionServiceMock.Object
|
||||||
|
);
|
||||||
|
|
||||||
|
var sessionEntity = TestDataBuilder.Mocks.CreateChatSessionEntity();
|
||||||
|
sessionEntity.Messages.Add(
|
||||||
|
new ChatMessageEntity
|
||||||
|
{
|
||||||
|
Id = 1,
|
||||||
|
SessionId = sessionEntity.Id,
|
||||||
|
Content = "Test",
|
||||||
|
Role = "user",
|
||||||
|
MessageOrder = 0,
|
||||||
|
CreatedAt = DateTime.UtcNow,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
_repositoryMock.Setup(x => x.GetByChatIdAsync(12345)).ReturnsAsync(sessionEntity);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = storageWithCompression.Get(12345);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
_repositoryMock.Verify(x => x.GetByChatIdAsync(12345), Times.Once);
|
||||||
|
result.Should().NotBeNull();
|
||||||
|
result!.GetMessageCount().Should().Be(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task SaveSessionAsync_WithMultipleMessages_ShouldSaveInCorrectOrder()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var session = TestDataBuilder.ChatSessions.CreateBasicSession(12345, "private");
|
||||||
|
session.AddUserMessage("Message 1", "user1");
|
||||||
|
session.AddAssistantMessage("Response 1");
|
||||||
|
session.AddUserMessage("Message 2", "user1");
|
||||||
|
session.AddAssistantMessage("Response 2");
|
||||||
|
|
||||||
|
var sessionEntity = TestDataBuilder.Mocks.CreateChatSessionEntity();
|
||||||
|
_repositoryMock.Setup(x => x.GetByChatIdAsync(12345)).ReturnsAsync(sessionEntity);
|
||||||
|
_repositoryMock
|
||||||
|
.Setup(x => x.UpdateAsync(It.IsAny<ChatSessionEntity>()))
|
||||||
|
.ReturnsAsync(sessionEntity);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await _sessionStorage.SaveSessionAsync(session);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
_repositoryMock.Verify(x => x.ClearMessagesAsync(It.IsAny<int>()), Times.Once);
|
||||||
|
_repositoryMock.Verify(
|
||||||
|
x =>
|
||||||
|
x.AddMessageAsync(
|
||||||
|
It.IsAny<int>(),
|
||||||
|
It.IsAny<string>(),
|
||||||
|
It.IsAny<string>(),
|
||||||
|
It.IsAny<int>()
|
||||||
|
),
|
||||||
|
Times.Exactly(4)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetOrCreate_WithDefaultParameters_ShouldUseDefaults()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var sessionEntity = TestDataBuilder.Mocks.CreateChatSessionEntity();
|
||||||
|
_repositoryMock
|
||||||
|
.Setup(x => x.GetOrCreateAsync(12345, "private", ""))
|
||||||
|
.ReturnsAsync(sessionEntity);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = _sessionStorage.GetOrCreate(12345);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().NotBeNull();
|
||||||
|
_repositoryMock.Verify(x => x.GetOrCreateAsync(12345, "private", ""), Times.Once);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
using ChatBot.Services.Telegram.Services;
|
||||||
|
using ChatBot.Tests.TestUtilities;
|
||||||
|
using FluentAssertions;
|
||||||
|
using Moq;
|
||||||
|
using Telegram.Bot;
|
||||||
|
using Telegram.Bot.Types;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace ChatBot.Tests.Services.Telegram;
|
||||||
|
|
||||||
|
public class TelegramMessageSenderWrapperTests : UnitTestBase
|
||||||
|
{
|
||||||
|
private readonly Mock<ITelegramBotClient> _botClientMock;
|
||||||
|
private readonly TelegramMessageSenderWrapper _wrapper;
|
||||||
|
|
||||||
|
public TelegramMessageSenderWrapperTests()
|
||||||
|
{
|
||||||
|
_botClientMock = TestDataBuilder.Mocks.CreateTelegramBotClient();
|
||||||
|
_wrapper = new TelegramMessageSenderWrapper(_botClientMock.Object);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Constructor_ShouldInitializeCorrectly()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var botClient = TestDataBuilder.Mocks.CreateTelegramBotClient().Object;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var wrapper = new TelegramMessageSenderWrapper(botClient);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
wrapper.Should().NotBeNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void SendMessageAsync_ShouldBePublicMethod()
|
||||||
|
{
|
||||||
|
// Arrange & Act
|
||||||
|
var method = typeof(TelegramMessageSenderWrapper).GetMethod("SendMessageAsync");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
method.Should().NotBeNull();
|
||||||
|
method!.IsPublic.Should().BeTrue();
|
||||||
|
method.ReturnType.Should().Be<Task<Message>>();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -329,4 +329,8 @@ public class TelegramBotClientWrapperTests : UnitTestBase
|
|||||||
var attributes = returnType.GetCustomAttributes(false);
|
var attributes = returnType.GetCustomAttributes(false);
|
||||||
attributes.Should().NotBeNull();
|
attributes.Should().NotBeNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Note: Tests for GetMeAsync removed because GetMe is an extension method
|
||||||
|
// and cannot be mocked with Moq. The wrapper simply delegates to the
|
||||||
|
// TelegramBotClient extension method, which is tested by the Telegram.Bot library itself.
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,16 +10,15 @@ namespace ChatBot.Tests.Telegram.Commands;
|
|||||||
|
|
||||||
public class SettingsCommandTests : UnitTestBase
|
public class SettingsCommandTests : UnitTestBase
|
||||||
{
|
{
|
||||||
private readonly Mock<ISessionStorage> _sessionStorageMock;
|
private readonly Mock<ChatService> _chatServiceMock;
|
||||||
private readonly SettingsCommand _settingsCommand;
|
private readonly SettingsCommand _settingsCommand;
|
||||||
|
|
||||||
public SettingsCommandTests()
|
public SettingsCommandTests()
|
||||||
{
|
{
|
||||||
_sessionStorageMock = TestDataBuilder.Mocks.CreateSessionStorageMock();
|
_chatServiceMock = new Mock<ChatService>(
|
||||||
var chatServiceMock = new Mock<ChatService>(
|
|
||||||
TestDataBuilder.Mocks.CreateLoggerMock<ChatService>().Object,
|
TestDataBuilder.Mocks.CreateLoggerMock<ChatService>().Object,
|
||||||
TestDataBuilder.Mocks.CreateAIServiceMock().Object,
|
TestDataBuilder.Mocks.CreateAIServiceMock().Object,
|
||||||
_sessionStorageMock.Object,
|
TestDataBuilder.Mocks.CreateSessionStorageMock().Object,
|
||||||
TestDataBuilder
|
TestDataBuilder
|
||||||
.Mocks.CreateOptionsMock(TestDataBuilder.Configurations.CreateAISettings())
|
.Mocks.CreateOptionsMock(TestDataBuilder.Configurations.CreateAISettings())
|
||||||
.Object,
|
.Object,
|
||||||
@@ -33,7 +32,7 @@ public class SettingsCommandTests : UnitTestBase
|
|||||||
);
|
);
|
||||||
var aiSettingsMock = TestDataBuilder.Mocks.CreateOptionsMock(new AISettings());
|
var aiSettingsMock = TestDataBuilder.Mocks.CreateOptionsMock(new AISettings());
|
||||||
_settingsCommand = new SettingsCommand(
|
_settingsCommand = new SettingsCommand(
|
||||||
chatServiceMock.Object,
|
_chatServiceMock.Object,
|
||||||
modelServiceMock.Object,
|
modelServiceMock.Object,
|
||||||
aiSettingsMock.Object
|
aiSettingsMock.Object
|
||||||
);
|
);
|
||||||
@@ -45,7 +44,7 @@ public class SettingsCommandTests : UnitTestBase
|
|||||||
// Arrange
|
// Arrange
|
||||||
var chatId = 12345L;
|
var chatId = 12345L;
|
||||||
var session = TestDataBuilder.ChatSessions.CreateBasicSession(chatId);
|
var session = TestDataBuilder.ChatSessions.CreateBasicSession(chatId);
|
||||||
_sessionStorageMock.Setup(x => x.Get(chatId)).Returns(session);
|
_chatServiceMock.Setup(x => x.GetSession(chatId)).Returns(session);
|
||||||
|
|
||||||
var context = new TelegramCommandContext
|
var context = new TelegramCommandContext
|
||||||
{
|
{
|
||||||
@@ -70,7 +69,9 @@ public class SettingsCommandTests : UnitTestBase
|
|||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var chatId = 12345L;
|
var chatId = 12345L;
|
||||||
_sessionStorageMock.Setup(x => x.Get(chatId)).Returns((ChatBot.Models.ChatSession?)null);
|
_chatServiceMock
|
||||||
|
.Setup(x => x.GetSession(chatId))
|
||||||
|
.Returns((ChatBot.Models.ChatSession?)null);
|
||||||
|
|
||||||
var context = new TelegramCommandContext
|
var context = new TelegramCommandContext
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -155,4 +155,284 @@ public class StatusCommandTests : UnitTestBase
|
|||||||
result.Should().Contain("системы");
|
result.Should().Contain("системы");
|
||||||
result.Should().Contain("Доступен");
|
result.Should().Contain("Доступен");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ExecuteAsync_ShouldReturnTimeoutStatus_WhenRequestTimesOut()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var context = new TelegramCommandContext
|
||||||
|
{
|
||||||
|
ChatId = 12345,
|
||||||
|
Username = "testuser",
|
||||||
|
MessageText = "/status",
|
||||||
|
ChatType = "private",
|
||||||
|
ChatTitle = "Test Chat",
|
||||||
|
};
|
||||||
|
|
||||||
|
_ollamaClientMock
|
||||||
|
.Setup(x => x.ChatAsync(It.IsAny<OllamaSharp.Models.Chat.ChatRequest>()))
|
||||||
|
.Throws<TaskCanceledException>();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await _statusCommand.ExecuteAsync(context);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().NotBeNull();
|
||||||
|
result.Should().Contain("Таймаут");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ExecuteAsync_ShouldReturnHttpError502_WhenBadGateway()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var context = new TelegramCommandContext
|
||||||
|
{
|
||||||
|
ChatId = 12345,
|
||||||
|
Username = "testuser",
|
||||||
|
MessageText = "/status",
|
||||||
|
ChatType = "private",
|
||||||
|
ChatTitle = "Test Chat",
|
||||||
|
};
|
||||||
|
|
||||||
|
var httpException = new HttpRequestException(
|
||||||
|
"Response status code does not indicate success: 502"
|
||||||
|
);
|
||||||
|
_ollamaClientMock
|
||||||
|
.Setup(x => x.ChatAsync(It.IsAny<OllamaSharp.Models.Chat.ChatRequest>()))
|
||||||
|
.Throws(httpException);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await _statusCommand.ExecuteAsync(context);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().NotBeNull();
|
||||||
|
result.Should().Contain("502");
|
||||||
|
result.Should().Contain("Bad Gateway");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ExecuteAsync_ShouldReturnHttpError503_WhenServiceUnavailable()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var context = new TelegramCommandContext
|
||||||
|
{
|
||||||
|
ChatId = 12345,
|
||||||
|
Username = "testuser",
|
||||||
|
MessageText = "/status",
|
||||||
|
ChatType = "private",
|
||||||
|
ChatTitle = "Test Chat",
|
||||||
|
};
|
||||||
|
|
||||||
|
var httpException = new HttpRequestException(
|
||||||
|
"Response status code does not indicate success: 503"
|
||||||
|
);
|
||||||
|
_ollamaClientMock
|
||||||
|
.Setup(x => x.ChatAsync(It.IsAny<OllamaSharp.Models.Chat.ChatRequest>()))
|
||||||
|
.Throws(httpException);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await _statusCommand.ExecuteAsync(context);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().NotBeNull();
|
||||||
|
result.Should().Contain("503");
|
||||||
|
result.Should().Contain("Service Unavailable");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ExecuteAsync_ShouldReturnHttpError504_WhenGatewayTimeout()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var context = new TelegramCommandContext
|
||||||
|
{
|
||||||
|
ChatId = 12345,
|
||||||
|
Username = "testuser",
|
||||||
|
MessageText = "/status",
|
||||||
|
ChatType = "private",
|
||||||
|
ChatTitle = "Test Chat",
|
||||||
|
};
|
||||||
|
|
||||||
|
var httpException = new HttpRequestException(
|
||||||
|
"Response status code does not indicate success: 504"
|
||||||
|
);
|
||||||
|
_ollamaClientMock
|
||||||
|
.Setup(x => x.ChatAsync(It.IsAny<OllamaSharp.Models.Chat.ChatRequest>()))
|
||||||
|
.Throws(httpException);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await _statusCommand.ExecuteAsync(context);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().NotBeNull();
|
||||||
|
result.Should().Contain("504");
|
||||||
|
result.Should().Contain("Gateway Timeout");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ExecuteAsync_ShouldReturnHttpError429_WhenTooManyRequests()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var context = new TelegramCommandContext
|
||||||
|
{
|
||||||
|
ChatId = 12345,
|
||||||
|
Username = "testuser",
|
||||||
|
MessageText = "/status",
|
||||||
|
ChatType = "private",
|
||||||
|
ChatTitle = "Test Chat",
|
||||||
|
};
|
||||||
|
|
||||||
|
var httpException = new HttpRequestException(
|
||||||
|
"Response status code does not indicate success: 429"
|
||||||
|
);
|
||||||
|
_ollamaClientMock
|
||||||
|
.Setup(x => x.ChatAsync(It.IsAny<OllamaSharp.Models.Chat.ChatRequest>()))
|
||||||
|
.Throws(httpException);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await _statusCommand.ExecuteAsync(context);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().NotBeNull();
|
||||||
|
result.Should().Contain("429");
|
||||||
|
result.Should().Contain("Too Many Requests");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ExecuteAsync_ShouldReturnHttpError500_WhenInternalServerError()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var context = new TelegramCommandContext
|
||||||
|
{
|
||||||
|
ChatId = 12345,
|
||||||
|
Username = "testuser",
|
||||||
|
MessageText = "/status",
|
||||||
|
ChatType = "private",
|
||||||
|
ChatTitle = "Test Chat",
|
||||||
|
};
|
||||||
|
|
||||||
|
var httpException = new HttpRequestException(
|
||||||
|
"Response status code does not indicate success: 500"
|
||||||
|
);
|
||||||
|
_ollamaClientMock
|
||||||
|
.Setup(x => x.ChatAsync(It.IsAny<OllamaSharp.Models.Chat.ChatRequest>()))
|
||||||
|
.Throws(httpException);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await _statusCommand.ExecuteAsync(context);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().NotBeNull();
|
||||||
|
result.Should().Contain("500");
|
||||||
|
result.Should().Contain("Internal Server Error");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ExecuteAsync_ShouldReturnNoResponseStatus_WhenResponseIsEmpty()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var context = new TelegramCommandContext
|
||||||
|
{
|
||||||
|
ChatId = 12345,
|
||||||
|
Username = "testuser",
|
||||||
|
MessageText = "/status",
|
||||||
|
ChatType = "private",
|
||||||
|
ChatTitle = "Test Chat",
|
||||||
|
};
|
||||||
|
|
||||||
|
// Return empty response
|
||||||
|
_ollamaClientMock
|
||||||
|
.Setup(x => x.ChatAsync(It.IsAny<OllamaSharp.Models.Chat.ChatRequest>()))
|
||||||
|
.Returns(
|
||||||
|
TestDataBuilder.Mocks.CreateAsyncEnumerable(
|
||||||
|
new List<OllamaSharp.Models.Chat.ChatResponseStream>
|
||||||
|
{
|
||||||
|
new OllamaSharp.Models.Chat.ChatResponseStream { Message = null! },
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await _statusCommand.ExecuteAsync(context);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().NotBeNull();
|
||||||
|
result.Should().Contain("Нет ответа");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ExecuteAsync_WithSession_ShouldShowSessionInfo()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var chatServiceMock = new Mock<ChatService>(
|
||||||
|
TestDataBuilder.Mocks.CreateLoggerMock<ChatService>().Object,
|
||||||
|
TestDataBuilder.Mocks.CreateAIServiceMock().Object,
|
||||||
|
TestDataBuilder.Mocks.CreateSessionStorageMock().Object,
|
||||||
|
TestDataBuilder
|
||||||
|
.Mocks.CreateOptionsMock(TestDataBuilder.Configurations.CreateAISettings())
|
||||||
|
.Object,
|
||||||
|
TestDataBuilder.Mocks.CreateCompressionServiceMock().Object
|
||||||
|
);
|
||||||
|
|
||||||
|
var session = TestDataBuilder.ChatSessions.CreateBasicSession(12345, "private");
|
||||||
|
session.AddUserMessage("Test", "user");
|
||||||
|
chatServiceMock.Setup(x => x.GetSession(12345)).Returns(session);
|
||||||
|
|
||||||
|
var statusCommand = new StatusCommand(
|
||||||
|
chatServiceMock.Object,
|
||||||
|
new Mock<ModelService>(
|
||||||
|
TestDataBuilder.Mocks.CreateLoggerMock<ModelService>().Object,
|
||||||
|
_ollamaOptionsMock.Object
|
||||||
|
).Object,
|
||||||
|
TestDataBuilder.Mocks.CreateOptionsMock(new AISettings()).Object,
|
||||||
|
_ollamaClientMock.Object
|
||||||
|
);
|
||||||
|
|
||||||
|
var context = new TelegramCommandContext
|
||||||
|
{
|
||||||
|
ChatId = 12345,
|
||||||
|
Username = "testuser",
|
||||||
|
MessageText = "/status",
|
||||||
|
ChatType = "private",
|
||||||
|
ChatTitle = "Test Chat",
|
||||||
|
};
|
||||||
|
|
||||||
|
_ollamaClientMock
|
||||||
|
.Setup(x => x.ChatAsync(It.IsAny<OllamaSharp.Models.Chat.ChatRequest>()))
|
||||||
|
.Returns(
|
||||||
|
TestDataBuilder.Mocks.CreateAsyncEnumerable(
|
||||||
|
new List<OllamaSharp.Models.Chat.ChatResponseStream>
|
||||||
|
{
|
||||||
|
new OllamaSharp.Models.Chat.ChatResponseStream
|
||||||
|
{
|
||||||
|
Message = new OllamaSharp.Models.Chat.Message(
|
||||||
|
ChatRole.Assistant,
|
||||||
|
"Test"
|
||||||
|
),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await statusCommand.ExecuteAsync(context);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().NotBeNull();
|
||||||
|
result.Should().Contain("Сессия");
|
||||||
|
result.Should().Contain("Сообщений в истории");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void CommandName_ShouldReturnCorrectName()
|
||||||
|
{
|
||||||
|
// Act & Assert
|
||||||
|
_statusCommand.CommandName.Should().Be("/status");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Description_ShouldReturnCorrectDescription()
|
||||||
|
{
|
||||||
|
// Act & Assert
|
||||||
|
_statusCommand.Description.Should().Be("Показать статус системы и API");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ using ChatBot.Tests.TestUtilities;
|
|||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Moq;
|
using Moq;
|
||||||
|
using Telegram.Bot;
|
||||||
using Telegram.Bot.Types;
|
using Telegram.Bot.Types;
|
||||||
|
|
||||||
namespace ChatBot.Tests.Telegram.Commands;
|
namespace ChatBot.Tests.Telegram.Commands;
|
||||||
@@ -417,8 +418,8 @@ public class TelegramCommandProcessorTests : UnitTestBase
|
|||||||
var username = "testuser";
|
var username = "testuser";
|
||||||
var chatType = "private";
|
var chatType = "private";
|
||||||
var chatTitle = "Test Chat";
|
var chatTitle = "Test Chat";
|
||||||
var cts = new CancellationTokenSource();
|
using var cts = new CancellationTokenSource();
|
||||||
cts.Cancel();
|
await cts.CancelAsync();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await _processor.ProcessMessageAsync(
|
var result = await _processor.ProcessMessageAsync(
|
||||||
@@ -488,4 +489,347 @@ public class TelegramCommandProcessorTests : UnitTestBase
|
|||||||
// Assert
|
// Assert
|
||||||
result.Should().NotBeNull();
|
result.Should().NotBeNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ProcessMessageAsync_WithReplyToBot_ShouldProcessMessage()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var botUser = new User
|
||||||
|
{
|
||||||
|
Id = 999,
|
||||||
|
Username = "testbot",
|
||||||
|
IsBot = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
var botInfoServiceMock = new Mock<BotInfoService>(
|
||||||
|
TestDataBuilder.Mocks.CreateTelegramBotClient().Object,
|
||||||
|
TestDataBuilder.Mocks.CreateLoggerMock<BotInfoService>().Object
|
||||||
|
);
|
||||||
|
botInfoServiceMock.Setup(x => x.GetBotInfoAsync(It.IsAny<CancellationToken>()))
|
||||||
|
.ReturnsAsync(botUser);
|
||||||
|
|
||||||
|
var processor = new TelegramCommandProcessor(
|
||||||
|
_commandRegistry,
|
||||||
|
_chatService,
|
||||||
|
_loggerMock.Object,
|
||||||
|
botInfoServiceMock.Object
|
||||||
|
);
|
||||||
|
|
||||||
|
var replyInfo = new ReplyInfo
|
||||||
|
{
|
||||||
|
MessageId = 1,
|
||||||
|
UserId = 999,
|
||||||
|
Username = "testbot",
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await processor.ProcessMessageAsync(
|
||||||
|
"Test reply",
|
||||||
|
12345L,
|
||||||
|
"user",
|
||||||
|
"private",
|
||||||
|
"Test Chat",
|
||||||
|
replyInfo
|
||||||
|
);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().NotBeNullOrEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ProcessMessageAsync_WithReplyToOtherUser_ShouldReturnEmpty()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var botUser = new User
|
||||||
|
{
|
||||||
|
Id = 999,
|
||||||
|
Username = "testbot",
|
||||||
|
IsBot = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
var botInfoServiceMock = new Mock<BotInfoService>(
|
||||||
|
TestDataBuilder.Mocks.CreateTelegramBotClient().Object,
|
||||||
|
TestDataBuilder.Mocks.CreateLoggerMock<BotInfoService>().Object
|
||||||
|
);
|
||||||
|
botInfoServiceMock.Setup(x => x.GetBotInfoAsync(It.IsAny<CancellationToken>()))
|
||||||
|
.ReturnsAsync(botUser);
|
||||||
|
|
||||||
|
var processor = new TelegramCommandProcessor(
|
||||||
|
_commandRegistry,
|
||||||
|
_chatService,
|
||||||
|
_loggerMock.Object,
|
||||||
|
botInfoServiceMock.Object
|
||||||
|
);
|
||||||
|
|
||||||
|
var replyInfo = new ReplyInfo
|
||||||
|
{
|
||||||
|
MessageId = 1,
|
||||||
|
UserId = 123,
|
||||||
|
Username = "otheruser",
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await processor.ProcessMessageAsync(
|
||||||
|
"Test reply",
|
||||||
|
12345L,
|
||||||
|
"user",
|
||||||
|
"private",
|
||||||
|
"Test Chat",
|
||||||
|
replyInfo
|
||||||
|
);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().BeEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ProcessMessageAsync_WithBotMention_ShouldProcessMessage()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var botUser = new User
|
||||||
|
{
|
||||||
|
Id = 999,
|
||||||
|
Username = "testbot",
|
||||||
|
IsBot = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
var botInfoServiceMock = new Mock<BotInfoService>(
|
||||||
|
TestDataBuilder.Mocks.CreateTelegramBotClient().Object,
|
||||||
|
TestDataBuilder.Mocks.CreateLoggerMock<BotInfoService>().Object
|
||||||
|
);
|
||||||
|
botInfoServiceMock.Setup(x => x.GetBotInfoAsync(It.IsAny<CancellationToken>()))
|
||||||
|
.ReturnsAsync(botUser);
|
||||||
|
|
||||||
|
var processor = new TelegramCommandProcessor(
|
||||||
|
_commandRegistry,
|
||||||
|
_chatService,
|
||||||
|
_loggerMock.Object,
|
||||||
|
botInfoServiceMock.Object
|
||||||
|
);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await processor.ProcessMessageAsync(
|
||||||
|
"Hello @testbot",
|
||||||
|
12345L,
|
||||||
|
"user",
|
||||||
|
"private",
|
||||||
|
"Test Chat"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().NotBeNullOrEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ProcessMessageAsync_WithOtherUserMention_ShouldReturnEmpty()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var botUser = new User
|
||||||
|
{
|
||||||
|
Id = 999,
|
||||||
|
Username = "testbot",
|
||||||
|
IsBot = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
var botInfoServiceMock = new Mock<BotInfoService>(
|
||||||
|
TestDataBuilder.Mocks.CreateTelegramBotClient().Object,
|
||||||
|
TestDataBuilder.Mocks.CreateLoggerMock<BotInfoService>().Object
|
||||||
|
);
|
||||||
|
botInfoServiceMock.Setup(x => x.GetBotInfoAsync(It.IsAny<CancellationToken>()))
|
||||||
|
.ReturnsAsync(botUser);
|
||||||
|
|
||||||
|
var processor = new TelegramCommandProcessor(
|
||||||
|
_commandRegistry,
|
||||||
|
_chatService,
|
||||||
|
_loggerMock.Object,
|
||||||
|
botInfoServiceMock.Object
|
||||||
|
);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await processor.ProcessMessageAsync(
|
||||||
|
"Hello @otheruser",
|
||||||
|
12345L,
|
||||||
|
"user",
|
||||||
|
"private",
|
||||||
|
"Test Chat"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().BeEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ProcessMessageAsync_WithCommand_ShouldExecuteCommand()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var commandMock = new Mock<ITelegramCommand>();
|
||||||
|
commandMock.Setup(x => x.CommandName).Returns("/test");
|
||||||
|
commandMock
|
||||||
|
.Setup(x => x.CanHandle(It.IsAny<string>()))
|
||||||
|
.Returns((string msg) => msg.StartsWith("/test"));
|
||||||
|
commandMock
|
||||||
|
.Setup(x =>
|
||||||
|
x.ExecuteAsync(It.IsAny<TelegramCommandContext>(), It.IsAny<CancellationToken>())
|
||||||
|
)
|
||||||
|
.ReturnsAsync("Command executed");
|
||||||
|
|
||||||
|
var commandRegistry = new CommandRegistry(
|
||||||
|
TestDataBuilder.Mocks.CreateLoggerMock<CommandRegistry>().Object,
|
||||||
|
new[] { commandMock.Object }
|
||||||
|
);
|
||||||
|
|
||||||
|
var processor = new TelegramCommandProcessor(
|
||||||
|
commandRegistry,
|
||||||
|
_chatService,
|
||||||
|
_loggerMock.Object,
|
||||||
|
_botInfoService
|
||||||
|
);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await processor.ProcessMessageAsync(
|
||||||
|
"/test argument",
|
||||||
|
12345L,
|
||||||
|
"user",
|
||||||
|
"private",
|
||||||
|
"Test Chat"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().Be("Command executed");
|
||||||
|
commandMock.Verify(
|
||||||
|
x => x.ExecuteAsync(It.IsAny<TelegramCommandContext>(), It.IsAny<CancellationToken>()),
|
||||||
|
Times.Once
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ProcessMessageAsync_WithException_ShouldReturnErrorMessage()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var chatServiceMock = new Mock<ChatService>(
|
||||||
|
TestDataBuilder.Mocks.CreateLoggerMock<ChatService>().Object,
|
||||||
|
TestDataBuilder.Mocks.CreateAIServiceMock().Object,
|
||||||
|
TestDataBuilder.Mocks.CreateSessionStorageMock().Object,
|
||||||
|
TestDataBuilder
|
||||||
|
.Mocks.CreateOptionsMock(TestDataBuilder.Configurations.CreateAISettings())
|
||||||
|
.Object,
|
||||||
|
TestDataBuilder.Mocks.CreateCompressionServiceMock().Object
|
||||||
|
);
|
||||||
|
|
||||||
|
chatServiceMock
|
||||||
|
.Setup(x =>
|
||||||
|
x.ProcessMessageAsync(
|
||||||
|
It.IsAny<long>(),
|
||||||
|
It.IsAny<string>(),
|
||||||
|
It.IsAny<string>(),
|
||||||
|
It.IsAny<string>(),
|
||||||
|
It.IsAny<string>(),
|
||||||
|
It.IsAny<CancellationToken>()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.ThrowsAsync(new Exception("Test error"));
|
||||||
|
|
||||||
|
var processor = new TelegramCommandProcessor(
|
||||||
|
_commandRegistry,
|
||||||
|
chatServiceMock.Object,
|
||||||
|
_loggerMock.Object,
|
||||||
|
_botInfoService
|
||||||
|
);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await processor.ProcessMessageAsync(
|
||||||
|
"Test message",
|
||||||
|
12345L,
|
||||||
|
"user",
|
||||||
|
"private",
|
||||||
|
"Test Chat"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().Contain("Произошла ошибка");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ProcessMessageAsync_WithGroupChat_ShouldProcessCorrectly()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var messageText = "Hello";
|
||||||
|
var chatId = -100123456789L; // Group chat ID
|
||||||
|
var username = "testuser";
|
||||||
|
var chatType = "group";
|
||||||
|
var chatTitle = "Test Group";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await _processor.ProcessMessageAsync(
|
||||||
|
messageText,
|
||||||
|
chatId,
|
||||||
|
username,
|
||||||
|
chatType,
|
||||||
|
chatTitle
|
||||||
|
);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().NotBeNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ProcessMessageAsync_WithSupergroupChat_ShouldProcessCorrectly()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var messageText = "Hello";
|
||||||
|
var chatId = -100123456789L;
|
||||||
|
var username = "testuser";
|
||||||
|
var chatType = "supergroup";
|
||||||
|
var chatTitle = "Test Supergroup";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await _processor.ProcessMessageAsync(
|
||||||
|
messageText,
|
||||||
|
chatId,
|
||||||
|
username,
|
||||||
|
chatType,
|
||||||
|
chatTitle
|
||||||
|
);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().NotBeNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ProcessMessageAsync_WithMultipleMentions_IncludingBot_ShouldProcessMessage()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var botUser = new User
|
||||||
|
{
|
||||||
|
Id = 999,
|
||||||
|
Username = "testbot",
|
||||||
|
IsBot = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
var botInfoServiceMock = new Mock<BotInfoService>(
|
||||||
|
TestDataBuilder.Mocks.CreateTelegramBotClient().Object,
|
||||||
|
TestDataBuilder.Mocks.CreateLoggerMock<BotInfoService>().Object
|
||||||
|
);
|
||||||
|
botInfoServiceMock.Setup(x => x.GetBotInfoAsync(It.IsAny<CancellationToken>()))
|
||||||
|
.ReturnsAsync(botUser);
|
||||||
|
|
||||||
|
var processor = new TelegramCommandProcessor(
|
||||||
|
_commandRegistry,
|
||||||
|
_chatService,
|
||||||
|
_loggerMock.Object,
|
||||||
|
botInfoServiceMock.Object
|
||||||
|
);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await processor.ProcessMessageAsync(
|
||||||
|
"Hello @testbot and @otheruser",
|
||||||
|
12345L,
|
||||||
|
"user",
|
||||||
|
"group",
|
||||||
|
"Test Group"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().NotBeNullOrEmpty();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ namespace ChatBot.Services
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Process a user message and get AI response
|
/// Process a user message and get AI response
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public async Task<string> ProcessMessageAsync(
|
public virtual async Task<string> ProcessMessageAsync(
|
||||||
long chatId,
|
long chatId,
|
||||||
string username,
|
string username,
|
||||||
string message,
|
string message,
|
||||||
@@ -192,7 +192,7 @@ namespace ChatBot.Services
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get session information
|
/// Get session information
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ChatSession? GetSession(long chatId)
|
public virtual ChatSession? GetSession(long chatId)
|
||||||
{
|
{
|
||||||
return _sessionStorage.Get(chatId);
|
return _sessionStorage.Get(chatId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -178,7 +178,14 @@ namespace ChatBot.Services
|
|||||||
// Add messages to session
|
// Add messages to session
|
||||||
foreach (var messageEntity in entity.Messages.OrderBy(m => m.MessageOrder))
|
foreach (var messageEntity in entity.Messages.OrderBy(m => m.MessageOrder))
|
||||||
{
|
{
|
||||||
var role = Enum.Parse<ChatRole>(messageEntity.Role);
|
var role = messageEntity.Role.ToLowerInvariant() switch
|
||||||
|
{
|
||||||
|
"user" => ChatRole.User,
|
||||||
|
"assistant" => ChatRole.Assistant,
|
||||||
|
"system" => ChatRole.System,
|
||||||
|
"tool" => ChatRole.Tool,
|
||||||
|
_ => throw new ArgumentException($"Unknown role: {messageEntity.Role}")
|
||||||
|
};
|
||||||
var message = new ChatMessage { Content = messageEntity.Content, Role = role };
|
var message = new ChatMessage { Content = messageEntity.Content, Role = role };
|
||||||
session.AddMessage(message);
|
session.AddMessage(message);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ namespace ChatBot.Services.Telegram.Services
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Получает информацию о боте (с кэшированием и автоматической инвалидацией)
|
/// Получает информацию о боте (с кэшированием и автоматической инвалидацией)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public async Task<User?> GetBotInfoAsync(CancellationToken cancellationToken = default)
|
public virtual async Task<User?> GetBotInfoAsync(CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
// Проверяем, есть ли валидный кэш
|
// Проверяем, есть ли валидный кэш
|
||||||
if (
|
if (
|
||||||
|
|||||||
@@ -23,10 +23,16 @@ namespace ChatBot.Services.Telegram.Services
|
|||||||
CancellationToken cancellationToken = default
|
CancellationToken cancellationToken = default
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
ReplyParameters? replyParameters = null;
|
||||||
|
if (replyToMessageId > 0)
|
||||||
|
{
|
||||||
|
replyParameters = new ReplyParameters { MessageId = replyToMessageId };
|
||||||
|
}
|
||||||
|
|
||||||
return await _botClient.SendMessage(
|
return await _botClient.SendMessage(
|
||||||
chatId: chatId,
|
chatId: chatId,
|
||||||
text: text,
|
text: text,
|
||||||
replyParameters: replyToMessageId,
|
replyParameters: replyParameters,
|
||||||
cancellationToken: cancellationToken
|
cancellationToken: cancellationToken
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user