add tests
All checks were successful
SonarQube / Build and analyze (push) Successful in 3m54s
Unit Tests / Run Tests (push) Successful in 2m23s

This commit is contained in:
Leonid Pershin
2025-10-20 15:11:42 +03:00
parent f8fd16edb2
commit 1c910d7b7f
8 changed files with 549 additions and 154 deletions

View File

@@ -1,9 +1,24 @@
using ChatBot.Data;
using ChatBot.Data.Interfaces;
using ChatBot.Data.Repositories;
using ChatBot.Models.Configuration;
using ChatBot.Models.Configuration.Validators;
using ChatBot.Services;
using ChatBot.Services.Interfaces;
using ChatBot.Services.Telegram.Commands;
using ChatBot.Services.Telegram.Interfaces;
using ChatBot.Services.Telegram.Services;
using ChatBot.Tests.TestUtilities;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using Moq;
using Telegram.Bot;
namespace ChatBot.Tests.Integration;
@@ -30,9 +45,264 @@ public class ProgramIntegrationTests : TestBase
.Build();
services.AddSingleton<IConfiguration>(configuration);
services.AddLogging(builder => builder.AddDebug());
services.AddSingleton(typeof(ILogger<>), typeof(NullLogger<>));
services.AddLogging(builder => builder.AddDebug());
services.AddLogging(builder => builder.AddDebug());
services.AddLogging(builder => builder.AddDebug());
services.AddLogging(builder => builder.AddDebug());
services.AddLogging(builder => builder.AddConsole().SetMinimumLevel(LogLevel.Debug));
}
[Fact]
public void Program_SuccessfulConfigurationPipeline_ShouldBuildAndValidate()
{
// Arrange
var services = new ServiceCollection();
var configuration = new ConfigurationBuilder()
.AddInMemoryCollection(
new Dictionary<string, string?>
{
["TelegramBot:BotToken"] = "1234567890123456789012345678901234567890",
["Ollama:Url"] = "http://localhost:11434",
["Ollama:DefaultModel"] = "llama3",
["AI:CompressionThreshold"] = "100",
["Database:ConnectionString"] =
"Host=localhost;Port=5432;Database=test;Username=test;Password=test",
["Database:CommandTimeout"] = "30",
["Database:EnableSensitiveDataLogging"] = "false",
}
)
.Build();
services.AddSingleton<IConfiguration>(configuration);
services.AddLogging(builder => builder.AddDebug());
// Configure options and validators similar to Program.cs
services.Configure<TelegramBotSettings>(configuration.GetSection("TelegramBot"));
services.AddSingleton<
IValidateOptions<TelegramBotSettings>,
TelegramBotSettingsValidator
>();
services.Configure<OllamaSettings>(configuration.GetSection("Ollama"));
services.AddSingleton<IValidateOptions<OllamaSettings>, OllamaSettingsValidator>();
services.Configure<AISettings>(configuration.GetSection("AI"));
services.AddSingleton<IValidateOptions<AISettings>, AISettingsValidator>();
services.Configure<DatabaseSettings>(configuration.GetSection("Database"));
services.AddSingleton<IValidateOptions<DatabaseSettings>, DatabaseSettingsValidator>();
// Register health checks similar to Program.cs
services
.AddHealthChecks()
.AddCheck<ChatBot.Services.HealthChecks.OllamaHealthCheck>("ollama")
.AddCheck<ChatBot.Services.HealthChecks.TelegramBotHealthCheck>("telegram");
var serviceProvider = services.BuildServiceProvider();
// Act & Assert - validate options using validators
var telegramValidator = serviceProvider.GetRequiredService<
IValidateOptions<TelegramBotSettings>
>();
var ollamaValidator = serviceProvider.GetRequiredService<
IValidateOptions<OllamaSettings>
>();
var aiValidator = serviceProvider.GetRequiredService<IValidateOptions<AISettings>>();
var dbValidator = serviceProvider.GetRequiredService<IValidateOptions<DatabaseSettings>>();
var telegram = serviceProvider.GetRequiredService<IOptions<TelegramBotSettings>>().Value;
var ollama = serviceProvider.GetRequiredService<IOptions<OllamaSettings>>().Value;
var ai = serviceProvider.GetRequiredService<IOptions<AISettings>>().Value;
var db = serviceProvider.GetRequiredService<IOptions<DatabaseSettings>>().Value;
Assert.True(telegramValidator.Validate("TelegramBot", telegram).Succeeded);
Assert.True(ollamaValidator.Validate("Ollama", ollama).Succeeded);
Assert.True(aiValidator.Validate("AI", ai).Succeeded);
Assert.True(dbValidator.Validate("Database", db).Succeeded);
// Assert health checks are registered
var hcOptions = serviceProvider
.GetRequiredService<IOptions<HealthCheckServiceOptions>>()
.Value;
var registrations = hcOptions.Registrations.Select(r => r.Name).ToList();
Assert.Contains("ollama", registrations);
Assert.Contains("telegram", registrations);
}
[Fact]
public void ServiceRegistrations_ShouldIncludeCoreServicesAndHealthCheckTags()
{
// Arrange
var services = new ServiceCollection();
var configuration = new ConfigurationBuilder()
.AddInMemoryCollection(
new Dictionary<string, string?>
{
["TelegramBot:BotToken"] = "1234567890123456789012345678901234567890",
["Ollama:Url"] = "http://localhost:11434",
["Ollama:DefaultModel"] = "llama3",
["AI:CompressionThreshold"] = "100",
["Database:ConnectionString"] =
"Host=localhost;Port=5432;Database=test;Username=test;Password=test",
["Database:CommandTimeout"] = "30",
["Database:EnableSensitiveDataLogging"] = "false",
}
)
.Build();
services.AddSingleton<IConfiguration>(configuration);
// Options + validators
services.Configure<TelegramBotSettings>(configuration.GetSection("TelegramBot"));
services.AddSingleton<
IValidateOptions<TelegramBotSettings>,
TelegramBotSettingsValidator
>();
services.Configure<OllamaSettings>(configuration.GetSection("Ollama"));
services.AddSingleton<IValidateOptions<OllamaSettings>, OllamaSettingsValidator>();
services.Configure<AISettings>(configuration.GetSection("AI"));
services.AddSingleton<IValidateOptions<AISettings>, AISettingsValidator>();
services.Configure<DatabaseSettings>(configuration.GetSection("Database"));
services.AddSingleton<IValidateOptions<DatabaseSettings>, DatabaseSettingsValidator>();
// DbContext (in-memory for test)
services.AddDbContext<ChatBotDbContext>(options => options.UseInMemoryDatabase("test-db"));
// Repository and session storage
services.AddSingleton<ILogger<ChatSessionRepository>>(_ =>
NullLogger<ChatSessionRepository>.Instance
);
services.AddScoped<IChatSessionRepository, ChatSessionRepository>();
services.AddSingleton<ILogger<DatabaseSessionStorage>>(_ =>
NullLogger<DatabaseSessionStorage>.Instance
);
services.AddScoped<ISessionStorage, DatabaseSessionStorage>();
// Core services
services.AddSingleton<ILogger<ModelService>>(_ => NullLogger<ModelService>.Instance);
services.AddSingleton<ModelService>();
services.AddSingleton<ILogger<SystemPromptService>>(_ =>
NullLogger<SystemPromptService>.Instance
);
services.AddSingleton<SystemPromptService>();
services.AddSingleton<IHistoryCompressionService, HistoryCompressionService>();
services.AddSingleton<ILogger<HistoryCompressionService>>(_ =>
NullLogger<HistoryCompressionService>.Instance
);
services.AddSingleton<ILogger<AIService>>(_ => NullLogger<AIService>.Instance);
services.AddSingleton<IAIService, AIService>();
services.AddSingleton<ILogger<ChatService>>(_ => NullLogger<ChatService>.Instance);
services.AddScoped<ChatService>();
// IOllamaClient
services.AddSingleton<IOllamaClient>(sp =>
{
var opts = sp.GetRequiredService<IOptions<OllamaSettings>>();
return new OllamaClientAdapter(opts.Value.Url);
});
// Telegram services and commands
services.AddSingleton<ITelegramBotClient>(_ => new Mock<ITelegramBotClient>().Object);
services.AddSingleton<ILogger<TelegramMessageSender>>(_ =>
NullLogger<TelegramMessageSender>.Instance
);
services.AddSingleton<ITelegramMessageSenderWrapper>(_ =>
new Mock<ITelegramMessageSenderWrapper>().Object
);
services.AddSingleton<ITelegramMessageSender, TelegramMessageSender>();
services.AddSingleton<ILogger<TelegramErrorHandler>>(_ =>
NullLogger<TelegramErrorHandler>.Instance
);
services.AddSingleton<ITelegramErrorHandler, TelegramErrorHandler>();
services.AddSingleton<ILogger<CommandRegistry>>(_ => NullLogger<CommandRegistry>.Instance);
services.AddScoped<CommandRegistry>();
services.AddSingleton<ILogger<BotInfoService>>(_ => NullLogger<BotInfoService>.Instance);
services.AddSingleton<BotInfoService>();
services.AddSingleton<ILogger<TelegramCommandProcessor>>(_ =>
NullLogger<TelegramCommandProcessor>.Instance
);
services.AddScoped<ITelegramCommandProcessor, TelegramCommandProcessor>();
services.AddSingleton<ILogger<TelegramMessageHandler>>(_ =>
NullLogger<TelegramMessageHandler>.Instance
);
services.AddScoped<ITelegramMessageHandler, TelegramMessageHandler>();
services.AddScoped<ITelegramCommand, StartCommand>();
services.AddScoped<ITelegramCommand, HelpCommand>();
services.AddScoped<ITelegramCommand, ClearCommand>();
services.AddScoped<ITelegramCommand, SettingsCommand>();
services.AddScoped<ITelegramCommand, StatusCommand>();
// Hosted service simulation for Telegram bot service
services.AddSingleton<ILogger<TelegramBotService>>(_ =>
NullLogger<TelegramBotService>.Instance
);
services.AddSingleton<ITelegramBotService, TelegramBotService>();
services.AddSingleton<IHostedService>(sp =>
(IHostedService)sp.GetRequiredService<ITelegramBotService>()
);
// Simulate HealthCheck registrations via options to avoid bringing in DefaultHealthCheckService
var hcOptions = new HealthCheckServiceOptions();
hcOptions.Registrations.Add(
new HealthCheckRegistration(
"ollama",
sp => new Mock<IHealthCheck>().Object,
null,
new[] { "api", "ollama" }
)
);
hcOptions.Registrations.Add(
new HealthCheckRegistration(
"telegram",
sp => new Mock<IHealthCheck>().Object,
null,
new[] { "api", "telegram" }
)
);
services.AddSingleton<IOptions<HealthCheckServiceOptions>>(Options.Create(hcOptions));
// ILogger for health checks is provided by AddLogging above; no direct registration for internal types
var sp = services.BuildServiceProvider();
// Assert: core services can be resolved
Assert.NotNull(sp.GetRequiredService<ChatBotDbContext>());
Assert.NotNull(sp.GetRequiredService<IChatSessionRepository>());
Assert.NotNull(sp.GetRequiredService<ISessionStorage>());
Assert.NotNull(sp.GetRequiredService<ModelService>());
Assert.NotNull(sp.GetRequiredService<SystemPromptService>());
Assert.NotNull(sp.GetRequiredService<IHistoryCompressionService>());
Assert.NotNull(sp.GetRequiredService<IAIService>());
Assert.NotNull(sp.GetRequiredService<ChatService>());
Assert.NotNull(sp.GetRequiredService<IOllamaClient>());
// Assert: Telegram stack resolves
Assert.NotNull(sp.GetRequiredService<ITelegramBotClient>());
Assert.NotNull(sp.GetRequiredService<ITelegramMessageSender>());
Assert.NotNull(sp.GetRequiredService<ITelegramErrorHandler>());
Assert.NotNull(sp.GetRequiredService<ITelegramCommandProcessor>());
Assert.NotNull(sp.GetRequiredService<ITelegramMessageHandler>());
Assert.NotEmpty(sp.GetServices<ITelegramCommand>());
Assert.NotNull(sp.GetRequiredService<IHostedService>());
// Assert: health checks names and tags
var hcResolved = sp.GetRequiredService<IOptions<HealthCheckServiceOptions>>().Value;
var regDict = hcResolved.Registrations.ToDictionary(r => r.Name, r => r.Tags);
Assert.True(regDict.ContainsKey("ollama"));
Assert.True(regDict.ContainsKey("telegram"));
Assert.Contains("api", regDict["ollama"]);
Assert.Contains("ollama", regDict["ollama"]);
Assert.Contains("api", regDict["telegram"]);
Assert.Contains("telegram", regDict["telegram"]);
}
[Fact]
public void Program_ShouldHaveRequiredUsingStatements()
{