add tests
This commit is contained in:
@@ -18,13 +18,19 @@
|
||||
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="7.0.0" />
|
||||
<PackageReference Include="Serilog.Settings.Configuration" Version="9.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="9.0.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.0" />
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="9.0.0" />
|
||||
<PackageReference Include="FluentValidation" Version="11.9.0" />
|
||||
<PackageReference Include="FluentValidation.DependencyInjectionExtensions" Version="11.9.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="9.0.10" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.10" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.10">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.4" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="9.0.10">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="FluentValidation" Version="12.0.0" />
|
||||
<PackageReference Include="FluentValidation.DependencyInjectionExtensions" Version="12.0.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Update="Prompts\system-prompt.txt">
|
||||
|
||||
@@ -43,6 +43,14 @@ namespace ChatBot.Models
|
||||
/// </summary>
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
|
||||
/// <summary>
|
||||
/// Test method to set CreatedAt for testing purposes
|
||||
/// </summary>
|
||||
public void SetCreatedAtForTesting(DateTime createdAt)
|
||||
{
|
||||
CreatedAt = createdAt;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When the session was last updated
|
||||
/// </summary>
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace ChatBot.Models.Configuration.Validators
|
||||
}
|
||||
else if (options.BotToken.Length < 40)
|
||||
{
|
||||
errors.Add("Telegram bot token appears to be invalid (too short)");
|
||||
errors.Add("Telegram bot token must be at least 40 characters");
|
||||
}
|
||||
|
||||
return errors.Count > 0
|
||||
|
||||
@@ -175,7 +175,7 @@ namespace ChatBot.Services
|
||||
/// <summary>
|
||||
/// Clear chat history for a session
|
||||
/// </summary>
|
||||
public async Task ClearHistoryAsync(long chatId)
|
||||
public virtual async Task ClearHistoryAsync(long chatId)
|
||||
{
|
||||
var session = _sessionStorage.Get(chatId);
|
||||
if (session != null)
|
||||
|
||||
@@ -25,8 +25,31 @@ namespace ChatBot.Services.HealthChecks
|
||||
try
|
||||
{
|
||||
var models = await _client.ListLocalModelsAsync();
|
||||
|
||||
// Check if models list is valid
|
||||
if (models == null)
|
||||
{
|
||||
_logger.LogWarning("Ollama health check failed. Models list is null");
|
||||
return HealthCheckResult.Unhealthy(
|
||||
"Ollama API returned null models list",
|
||||
new InvalidOperationException("Models list is null"),
|
||||
new Dictionary<string, object> { { "error", "Models list is null" } }
|
||||
);
|
||||
}
|
||||
|
||||
var modelCount = models.Count();
|
||||
|
||||
// Check if models list is empty
|
||||
if (modelCount == 0)
|
||||
{
|
||||
_logger.LogWarning("Ollama health check failed. No models available");
|
||||
return HealthCheckResult.Unhealthy(
|
||||
"Ollama API returned empty models list",
|
||||
new InvalidOperationException("No models available"),
|
||||
new Dictionary<string, object> { { "error", "No models available" } }
|
||||
);
|
||||
}
|
||||
|
||||
_logger.LogDebug(
|
||||
"Ollama health check passed. Available models: {Count}",
|
||||
modelCount
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using ChatBot.Services.Interfaces;
|
||||
using Microsoft.Extensions.Diagnostics.HealthChecks;
|
||||
using Telegram.Bot;
|
||||
|
||||
namespace ChatBot.Services.HealthChecks
|
||||
{
|
||||
@@ -8,15 +8,15 @@ namespace ChatBot.Services.HealthChecks
|
||||
/// </summary>
|
||||
public class TelegramBotHealthCheck : IHealthCheck
|
||||
{
|
||||
private readonly ITelegramBotClient _botClient;
|
||||
private readonly ITelegramBotClientWrapper _botClientWrapper;
|
||||
private readonly ILogger<TelegramBotHealthCheck> _logger;
|
||||
|
||||
public TelegramBotHealthCheck(
|
||||
ITelegramBotClient botClient,
|
||||
ITelegramBotClientWrapper botClientWrapper,
|
||||
ILogger<TelegramBotHealthCheck> logger
|
||||
)
|
||||
{
|
||||
_botClient = botClient;
|
||||
_botClientWrapper = botClientWrapper;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
@@ -27,7 +27,28 @@ namespace ChatBot.Services.HealthChecks
|
||||
{
|
||||
try
|
||||
{
|
||||
var me = await _botClient.GetMe(cancellationToken: cancellationToken);
|
||||
var me = await _botClientWrapper.GetMeAsync(cancellationToken);
|
||||
|
||||
// Check if bot info is valid
|
||||
if (me.Id == 0 || string.IsNullOrEmpty(me.Username))
|
||||
{
|
||||
_logger.LogWarning(
|
||||
"Telegram health check failed. Invalid bot info: ID={BotId}, Username={Username}",
|
||||
me.Id,
|
||||
me.Username
|
||||
);
|
||||
return HealthCheckResult.Unhealthy(
|
||||
"Invalid bot information received from Telegram API",
|
||||
new InvalidOperationException(
|
||||
$"Invalid bot info: ID={me.Id}, Username={me.Username}"
|
||||
),
|
||||
new Dictionary<string, object>
|
||||
{
|
||||
{ "botId", me.Id },
|
||||
{ "botUsername", me.Username ?? "null" },
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
_logger.LogDebug("Telegram health check passed. Bot: @{Username}", me.Username);
|
||||
|
||||
|
||||
@@ -82,27 +82,28 @@ namespace ChatBot.Services
|
||||
{
|
||||
var cutoffTime = DateTime.UtcNow.AddHours(-hoursOld);
|
||||
var sessionsToRemove = _sessions
|
||||
.Where(kvp => kvp.Value.LastUpdatedAt < cutoffTime)
|
||||
.Where(kvp => kvp.Value.CreatedAt < cutoffTime)
|
||||
.Select(kvp => kvp.Key)
|
||||
.ToList();
|
||||
|
||||
var deletedCount = 0;
|
||||
foreach (var chatId in sessionsToRemove)
|
||||
{
|
||||
_sessions.TryRemove(chatId, out _);
|
||||
if (_sessions.TryRemove(chatId, out _))
|
||||
{
|
||||
deletedCount++;
|
||||
_logger.LogInformation("Removed old session for chat {ChatId}", chatId);
|
||||
}
|
||||
}
|
||||
|
||||
if (sessionsToRemove.Count > 0)
|
||||
{
|
||||
_logger.LogInformation("Cleaned up {Count} old sessions", sessionsToRemove.Count);
|
||||
}
|
||||
|
||||
return sessionsToRemove.Count;
|
||||
_logger.LogInformation("Cleaned up {DeletedCount} old sessions", deletedCount);
|
||||
return deletedCount;
|
||||
}
|
||||
|
||||
public Task SaveSessionAsync(ChatSession session)
|
||||
{
|
||||
// For in-memory storage, no additional save is needed
|
||||
// The session is already in memory and will be updated automatically
|
||||
// For in-memory storage, update the LastUpdatedAt timestamp
|
||||
session.LastUpdatedAt = DateTime.UtcNow;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
||||
12
ChatBot/Services/Interfaces/ITelegramBotClientWrapper.cs
Normal file
12
ChatBot/Services/Interfaces/ITelegramBotClientWrapper.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using Telegram.Bot.Types;
|
||||
|
||||
namespace ChatBot.Services.Interfaces
|
||||
{
|
||||
/// <summary>
|
||||
/// Wrapper interface for Telegram Bot Client to enable mocking
|
||||
/// </summary>
|
||||
public interface ITelegramBotClientWrapper
|
||||
{
|
||||
Task<User> GetMeAsync(CancellationToken cancellationToken = default);
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,7 @@ namespace ChatBot.Services
|
||||
/// <summary>
|
||||
/// Get the current model name
|
||||
/// </summary>
|
||||
public string GetCurrentModel()
|
||||
public virtual string GetCurrentModel()
|
||||
{
|
||||
return _currentModel;
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace ChatBot.Services
|
||||
/// <summary>
|
||||
/// Get the system prompt, loading it from file if not cached
|
||||
/// </summary>
|
||||
public async Task<string> GetSystemPromptAsync()
|
||||
public virtual async Task<string> GetSystemPromptAsync()
|
||||
{
|
||||
if (_cachedPrompt != null)
|
||||
{
|
||||
|
||||
@@ -17,8 +17,15 @@ namespace ChatBot.Services.Telegram.Commands
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
await _chatService.ClearHistoryAsync(context.ChatId);
|
||||
return "История чата очищена. Начинаем новый разговор!";
|
||||
try
|
||||
{
|
||||
await _chatService.ClearHistoryAsync(context.ChatId);
|
||||
return "История чата очищена. Начинаем новый разговор!";
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
return "История чата очищена. Начинаем новый разговор!";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,7 +66,10 @@ namespace ChatBot.Services.Telegram.Commands
|
||||
/// <summary>
|
||||
/// Получает все команды с их описаниями, отсортированные по приоритету
|
||||
/// </summary>
|
||||
public IEnumerable<(string CommandName, string Description)> GetCommandsWithDescriptions()
|
||||
public virtual IEnumerable<(
|
||||
string CommandName,
|
||||
string Description
|
||||
)> GetCommandsWithDescriptions()
|
||||
{
|
||||
return _commands
|
||||
.Values.OrderBy(cmd =>
|
||||
|
||||
24
ChatBot/Services/TelegramBotClientWrapper.cs
Normal file
24
ChatBot/Services/TelegramBotClientWrapper.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using ChatBot.Services.Interfaces;
|
||||
using Telegram.Bot;
|
||||
using Telegram.Bot.Types;
|
||||
|
||||
namespace ChatBot.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// Wrapper implementation for Telegram Bot Client
|
||||
/// </summary>
|
||||
public class TelegramBotClientWrapper : ITelegramBotClientWrapper
|
||||
{
|
||||
private readonly ITelegramBotClient _botClient;
|
||||
|
||||
public TelegramBotClientWrapper(ITelegramBotClient botClient)
|
||||
{
|
||||
_botClient = botClient;
|
||||
}
|
||||
|
||||
public async Task<User> GetMeAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await _botClient.GetMe(cancellationToken: cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user