337 lines
10 KiB
C#
337 lines
10 KiB
C#
using ChatBot.Models;
|
|
using ChatBot.Models.Configuration;
|
|
using ChatBot.Models.Dto;
|
|
using ChatBot.Models.Entities;
|
|
using ChatBot.Services.Interfaces;
|
|
using Microsoft.Extensions.Logging;
|
|
using Microsoft.Extensions.Options;
|
|
using Moq;
|
|
using OllamaSharp.Models.Chat;
|
|
using Telegram.Bot;
|
|
|
|
namespace ChatBot.Tests.TestUtilities;
|
|
|
|
/// <summary>
|
|
/// Builder pattern for creating test data and mocks
|
|
/// </summary>
|
|
public static class TestDataBuilder
|
|
{
|
|
public static class ChatSessions
|
|
{
|
|
public static ChatSession CreateBasicSession(
|
|
long chatId = 12345,
|
|
string chatType = "private"
|
|
)
|
|
{
|
|
return new ChatSession
|
|
{
|
|
ChatId = chatId,
|
|
ChatType = chatType,
|
|
ChatTitle = chatType == "private" ? "" : "Test Group",
|
|
Model = "llama3.2",
|
|
CreatedAt = DateTime.UtcNow.AddHours(-1),
|
|
LastUpdatedAt = DateTime.UtcNow,
|
|
};
|
|
}
|
|
|
|
public static ChatSession CreateSessionWithMessages(
|
|
long chatId = 12345,
|
|
int messageCount = 3
|
|
)
|
|
{
|
|
var session = CreateBasicSession(chatId);
|
|
|
|
for (int i = 0; i < messageCount; i++)
|
|
{
|
|
session.AddUserMessage($"Test message {i + 1}", "testuser");
|
|
session.AddAssistantMessage($"AI response {i + 1}");
|
|
}
|
|
|
|
return session;
|
|
}
|
|
}
|
|
|
|
public static class ChatMessages
|
|
{
|
|
public static ChatMessage CreateUserMessage(
|
|
string content = "Hello",
|
|
string username = "testuser"
|
|
)
|
|
{
|
|
return new ChatMessage { Role = ChatRole.User, Content = content };
|
|
}
|
|
|
|
public static ChatMessage CreateAssistantMessage(
|
|
string content = "Hello! How can I help you?"
|
|
)
|
|
{
|
|
return new ChatMessage { Role = ChatRole.Assistant, Content = content };
|
|
}
|
|
|
|
public static ChatMessage CreateSystemMessage(
|
|
string content = "You are a helpful assistant."
|
|
)
|
|
{
|
|
return new ChatMessage { Role = ChatRole.System, Content = content };
|
|
}
|
|
|
|
public static List<ChatMessage> CreateMessageHistory(int count = 5)
|
|
{
|
|
var messages = new List<ChatMessage>();
|
|
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
messages.Add(CreateUserMessage($"User message {i + 1}"));
|
|
messages.Add(CreateAssistantMessage($"Assistant response {i + 1}"));
|
|
}
|
|
|
|
return messages;
|
|
}
|
|
}
|
|
|
|
public static class Configurations
|
|
{
|
|
public static AISettings CreateAISettings()
|
|
{
|
|
return new AISettings
|
|
{
|
|
Temperature = 0.7,
|
|
MaxRetryAttempts = 3,
|
|
RetryDelayMs = 1000,
|
|
MaxRetryDelayMs = 10000,
|
|
EnableExponentialBackoff = true,
|
|
RequestTimeoutSeconds = 30,
|
|
EnableHistoryCompression = true,
|
|
CompressionThreshold = 10,
|
|
CompressionTarget = 5,
|
|
MinMessageLengthForSummarization = 50,
|
|
MaxSummarizedMessageLength = 200,
|
|
CompressionTimeoutSeconds = 15,
|
|
};
|
|
}
|
|
|
|
public static OllamaSettings CreateOllamaSettings()
|
|
{
|
|
return new OllamaSettings { Url = "http://localhost:11434", DefaultModel = "llama3.2" };
|
|
}
|
|
|
|
public static TelegramBotSettings CreateTelegramBotSettings()
|
|
{
|
|
return new TelegramBotSettings { BotToken = "test-bot-token" };
|
|
}
|
|
|
|
public static DatabaseSettings CreateDatabaseSettings()
|
|
{
|
|
return new DatabaseSettings
|
|
{
|
|
ConnectionString =
|
|
"Host=localhost;Port=5432;Database=test_chatbot;Username=test;Password=test",
|
|
CommandTimeout = 30,
|
|
EnableSensitiveDataLogging = false,
|
|
};
|
|
}
|
|
}
|
|
|
|
public static class Mocks
|
|
{
|
|
public static Mock<ILogger<T>> CreateLoggerMock<T>()
|
|
{
|
|
return new Mock<ILogger<T>>();
|
|
}
|
|
|
|
public static Mock<IOptions<T>> CreateOptionsMock<T>(T value)
|
|
where T : class
|
|
{
|
|
var mock = new Mock<IOptions<T>>();
|
|
mock.Setup(x => x.Value).Returns(value);
|
|
return mock;
|
|
}
|
|
|
|
public static Mock<ISessionStorage> CreateSessionStorageMock()
|
|
{
|
|
var mock = new Mock<ISessionStorage>();
|
|
var sessions = new Dictionary<long, ChatSession>();
|
|
|
|
mock.Setup(x => x.GetOrCreate(It.IsAny<long>(), It.IsAny<string>(), It.IsAny<string>()))
|
|
.Returns<long, string, string>(
|
|
(chatId, chatType, chatTitle) =>
|
|
{
|
|
if (!sessions.ContainsKey(chatId))
|
|
{
|
|
var session = TestDataBuilder.ChatSessions.CreateBasicSession(
|
|
chatId,
|
|
chatType
|
|
);
|
|
session.ChatTitle = chatTitle;
|
|
sessions[chatId] = session;
|
|
}
|
|
return sessions[chatId];
|
|
}
|
|
);
|
|
|
|
mock.Setup(x => x.Get(It.IsAny<long>()))
|
|
.Returns<long>(chatId =>
|
|
sessions.TryGetValue(chatId, out var session) ? session : null
|
|
);
|
|
|
|
mock.Setup(x => x.SaveSessionAsync(It.IsAny<ChatSession>()))
|
|
.Returns<ChatSession>(session =>
|
|
{
|
|
sessions[session.ChatId] = session;
|
|
return Task.CompletedTask;
|
|
});
|
|
|
|
mock.Setup(x => x.Remove(It.IsAny<long>()))
|
|
.Returns<long>(chatId => sessions.Remove(chatId));
|
|
|
|
mock.Setup(x => x.GetActiveSessionsCount()).Returns(() => sessions.Count);
|
|
|
|
mock.Setup(x => x.CleanupOldSessions(It.IsAny<int>())).Returns<int>(hoursOld => 0);
|
|
|
|
return mock;
|
|
}
|
|
|
|
public static Mock<IAIService> CreateAIServiceMock()
|
|
{
|
|
var mock = new Mock<IAIService>();
|
|
|
|
mock.Setup(x =>
|
|
x.GenerateChatCompletionAsync(
|
|
It.IsAny<List<ChatMessage>>(),
|
|
It.IsAny<CancellationToken>()
|
|
)
|
|
)
|
|
.ReturnsAsync("Test AI response");
|
|
|
|
mock.Setup(x =>
|
|
x.GenerateChatCompletionWithCompressionAsync(
|
|
It.IsAny<List<ChatMessage>>(),
|
|
It.IsAny<CancellationToken>()
|
|
)
|
|
)
|
|
.ReturnsAsync("Test AI response with compression");
|
|
|
|
return mock;
|
|
}
|
|
|
|
public static Mock<IHistoryCompressionService> CreateCompressionServiceMock()
|
|
{
|
|
var mock = new Mock<IHistoryCompressionService>();
|
|
|
|
mock.Setup(x => x.ShouldCompress(It.IsAny<int>(), It.IsAny<int>()))
|
|
.Returns<int, int>((count, threshold) => count > threshold);
|
|
|
|
mock.Setup(x =>
|
|
x.CompressHistoryAsync(
|
|
It.IsAny<List<ChatMessage>>(),
|
|
It.IsAny<int>(),
|
|
It.IsAny<CancellationToken>()
|
|
)
|
|
)
|
|
.ReturnsAsync(
|
|
(List<ChatMessage> messages, int targetCount, CancellationToken ct) =>
|
|
{
|
|
// Simple compression: take last targetCount messages
|
|
return messages.TakeLast(targetCount).ToList();
|
|
}
|
|
);
|
|
|
|
return mock;
|
|
}
|
|
|
|
public static Mock<IOllamaClient> CreateOllamaClientMock()
|
|
{
|
|
var mock = new Mock<IOllamaClient>();
|
|
|
|
mock.Setup(x => x.ChatAsync(It.IsAny<OllamaSharp.Models.Chat.ChatRequest>()))
|
|
.Returns(
|
|
TestDataBuilder.Mocks.CreateAsyncEnumerable(
|
|
new List<OllamaSharp.Models.Chat.ChatResponseStream>()
|
|
)
|
|
);
|
|
|
|
return mock;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Create a mock chat session entity
|
|
/// </summary>
|
|
public static ChatSessionEntity CreateChatSessionEntity(
|
|
int id = 1,
|
|
long chatId = 12345,
|
|
string sessionId = "test-session",
|
|
string chatType = "private",
|
|
string chatTitle = "Test Chat"
|
|
)
|
|
{
|
|
return new ChatSessionEntity
|
|
{
|
|
Id = id,
|
|
ChatId = chatId,
|
|
SessionId = sessionId,
|
|
ChatType = chatType,
|
|
ChatTitle = chatTitle,
|
|
CreatedAt = DateTime.UtcNow,
|
|
LastUpdatedAt = DateTime.UtcNow,
|
|
Messages = new List<ChatMessageEntity>(),
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// Create a mock chat message entity
|
|
/// </summary>
|
|
public static ChatMessageEntity CreateChatMessageEntity(
|
|
int id = 1,
|
|
int sessionId = 1,
|
|
string content = "Test message",
|
|
string role = "user",
|
|
int messageOrder = 1
|
|
)
|
|
{
|
|
return new ChatMessageEntity
|
|
{
|
|
Id = id,
|
|
SessionId = sessionId,
|
|
Content = content,
|
|
Role = role,
|
|
MessageOrder = messageOrder,
|
|
CreatedAt = DateTime.UtcNow,
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// Create a mock Telegram bot client
|
|
/// </summary>
|
|
public static Mock<ITelegramBotClient> CreateTelegramBotClient()
|
|
{
|
|
var mock = new Mock<ITelegramBotClient>();
|
|
return mock;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Create a mock Telegram bot user
|
|
/// </summary>
|
|
public static global::Telegram.Bot.Types.User CreateTelegramBot()
|
|
{
|
|
return new global::Telegram.Bot.Types.User
|
|
{
|
|
Id = 12345,
|
|
Username = "test_bot",
|
|
FirstName = "Test Bot",
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// Create async enumerable from list
|
|
/// </summary>
|
|
public static async IAsyncEnumerable<T> CreateAsyncEnumerable<T>(IEnumerable<T> items)
|
|
{
|
|
foreach (var item in items)
|
|
{
|
|
yield return item;
|
|
}
|
|
await Task.CompletedTask;
|
|
}
|
|
}
|
|
}
|