using ChatBot.Data; using ChatBot.Models.Configuration; using FluentAssertions; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; namespace ChatBot.Tests.Program; public class ProgramConfigurationTests { [Fact] public void Configuration_ShouldHaveValidSettings() { // Arrange var configuration = new ConfigurationBuilder() .AddInMemoryCollection( new Dictionary { { "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(); // Act var telegramSettings = configuration.GetSection("TelegramBot").Get(); var ollamaSettings = configuration.GetSection("Ollama").Get(); var aiSettings = configuration.GetSection("AI").Get(); var databaseSettings = configuration.GetSection("Database").Get(); // Assert telegramSettings.Should().NotBeNull(); telegramSettings!.BotToken.Should().Be("1234567890123456789012345678901234567890"); ollamaSettings.Should().NotBeNull(); ollamaSettings!.Url.Should().Be("http://localhost:11434"); ollamaSettings.DefaultModel.Should().Be("llama3"); aiSettings.Should().NotBeNull(); aiSettings!.CompressionThreshold.Should().Be(100); databaseSettings.Should().NotBeNull(); databaseSettings! .ConnectionString.Should() .Be("Host=localhost;Port=5432;Database=test;Username=test;Password=test"); databaseSettings.CommandTimeout.Should().Be(30); databaseSettings.EnableSensitiveDataLogging.Should().BeFalse(); } [Fact] public void EnvironmentVariableOverrides_ShouldWorkCorrectly() { // Arrange Environment.SetEnvironmentVariable( "TELEGRAM_BOT_TOKEN", "env-token-1234567890123456789012345678901234567890" ); Environment.SetEnvironmentVariable("OLLAMA_URL", "http://env-ollama:11434"); Environment.SetEnvironmentVariable("OLLAMA_DEFAULT_MODEL", "env-model"); Environment.SetEnvironmentVariable("DB_HOST", "env-host"); Environment.SetEnvironmentVariable("DB_PORT", "5433"); Environment.SetEnvironmentVariable("DB_NAME", "env-db"); Environment.SetEnvironmentVariable("DB_USER", "env-user"); Environment.SetEnvironmentVariable("DB_PASSWORD", "env-password"); var configuration = new ConfigurationBuilder() .AddInMemoryCollection( new Dictionary { { "TelegramBot:BotToken", "config-token-1234567890123456789012345678901234567890" }, { "Ollama:Url", "http://config-ollama:11434" }, { "Ollama:DefaultModel", "config-model" }, { "Database:ConnectionString", "Host=config-host;Port=5432;Database=config-db;Username=config-user;Password=config-password" }, } ) .AddEnvironmentVariables() .Build(); // Act - Simulate the environment variable override logic from Program.cs var telegramSettings = new TelegramBotSettings(); configuration.GetSection("TelegramBot").Bind(telegramSettings); telegramSettings.BotToken = Environment.GetEnvironmentVariable("TELEGRAM_BOT_TOKEN") ?? telegramSettings.BotToken; var ollamaSettings = new OllamaSettings(); configuration.GetSection("Ollama").Bind(ollamaSettings); ollamaSettings.Url = Environment.GetEnvironmentVariable("OLLAMA_URL") ?? ollamaSettings.Url; ollamaSettings.DefaultModel = Environment.GetEnvironmentVariable("OLLAMA_DEFAULT_MODEL") ?? ollamaSettings.DefaultModel; var databaseSettings = new DatabaseSettings(); configuration.GetSection("Database").Bind(databaseSettings); var host = Environment.GetEnvironmentVariable("DB_HOST") ?? "localhost"; var port = Environment.GetEnvironmentVariable("DB_PORT") ?? "5432"; var name = Environment.GetEnvironmentVariable("DB_NAME") ?? "chatbot"; var user = Environment.GetEnvironmentVariable("DB_USER") ?? "postgres"; var password = Environment.GetEnvironmentVariable("DB_PASSWORD") ?? "postgres"; databaseSettings.ConnectionString = $"Host={host};Port={port};Database={name};Username={user};Password={password}"; // Assert telegramSettings .BotToken.Should() .Be("env-token-1234567890123456789012345678901234567890"); ollamaSettings.Url.Should().Be("http://env-ollama:11434"); ollamaSettings.DefaultModel.Should().Be("env-model"); databaseSettings .ConnectionString.Should() .Be("Host=env-host;Port=5433;Database=env-db;Username=env-user;Password=env-password"); // Cleanup Environment.SetEnvironmentVariable("TELEGRAM_BOT_TOKEN", null); Environment.SetEnvironmentVariable("OLLAMA_URL", null); Environment.SetEnvironmentVariable("OLLAMA_DEFAULT_MODEL", null); Environment.SetEnvironmentVariable("DB_HOST", null); Environment.SetEnvironmentVariable("DB_PORT", null); Environment.SetEnvironmentVariable("DB_NAME", null); Environment.SetEnvironmentVariable("DB_USER", null); Environment.SetEnvironmentVariable("DB_PASSWORD", null); } [Fact] public void EnvironmentVariableFallbacks_ShouldUseConfigWhenEnvMissing() { // Arrange: ensure env vars are not set Environment.SetEnvironmentVariable("TELEGRAM_BOT_TOKEN", null); Environment.SetEnvironmentVariable("OLLAMA_URL", null); Environment.SetEnvironmentVariable("OLLAMA_DEFAULT_MODEL", null); Environment.SetEnvironmentVariable("DB_HOST", null); Environment.SetEnvironmentVariable("DB_PORT", null); Environment.SetEnvironmentVariable("DB_NAME", null); Environment.SetEnvironmentVariable("DB_USER", null); Environment.SetEnvironmentVariable("DB_PASSWORD", null); var configuration = new ConfigurationBuilder() .AddInMemoryCollection( new Dictionary { { "TelegramBot:BotToken", "config-token-0000000000000000000000000000000000000000" }, { "Ollama:Url", "http://config-ollama:11434" }, { "Ollama:DefaultModel", "config-model" }, { "Database:ConnectionString", "Host=config-host;Port=5432;Database=config-db;Username=config-user;Password=config-password" }, } ) .Build(); // Act - Simulate Program.cs binding and env override logic var telegramSettings = new TelegramBotSettings(); configuration.GetSection("TelegramBot").Bind(telegramSettings); telegramSettings.BotToken = Environment.GetEnvironmentVariable("TELEGRAM_BOT_TOKEN") ?? telegramSettings.BotToken; var ollamaSettings = new OllamaSettings(); configuration.GetSection("Ollama").Bind(ollamaSettings); ollamaSettings.Url = Environment.GetEnvironmentVariable("OLLAMA_URL") ?? ollamaSettings.Url; ollamaSettings.DefaultModel = Environment.GetEnvironmentVariable("OLLAMA_DEFAULT_MODEL") ?? ollamaSettings.DefaultModel; var databaseSettings = new DatabaseSettings(); configuration.GetSection("Database").Bind(databaseSettings); var host = Environment.GetEnvironmentVariable("DB_HOST") ?? "localhost"; var port = Environment.GetEnvironmentVariable("DB_PORT") ?? "5432"; var name = Environment.GetEnvironmentVariable("DB_NAME") ?? "chatbot"; var user = Environment.GetEnvironmentVariable("DB_USER") ?? "postgres"; var password = Environment.GetEnvironmentVariable("DB_PASSWORD") ?? "postgres"; var expectedConnectionString = $"Host={host};Port={port};Database={name};Username={user};Password={password}"; databaseSettings.ConnectionString = $"Host={host};Port={port};Database={name};Username={user};Password={password}"; // Assert - values should remain from configuration when env vars are missing telegramSettings .BotToken.Should() .Be("config-token-0000000000000000000000000000000000000000"); ollamaSettings.Url.Should().Be("http://config-ollama:11434"); ollamaSettings.DefaultModel.Should().Be("config-model"); // Because our fallback block reconstructs from env with defaults when missing, // ensure it equals the configuration's original connection string when all envs are missing databaseSettings.ConnectionString.Should().Be(expectedConnectionString); } [Fact] public void DatabaseContext_ShouldBeConfiguredCorrectly() { // Arrange var services = new ServiceCollection(); var configuration = new ConfigurationBuilder() .AddInMemoryCollection( new Dictionary { { "Database:ConnectionString", "Host=localhost;Port=5432;Database=test;Username=test;Password=test" }, { "Database:CommandTimeout", "60" }, { "Database:EnableSensitiveDataLogging", "true" }, } ) .Build(); services.AddSingleton(configuration); services.Configure(configuration.GetSection("Database")); // Act services.AddDbContext( (serviceProvider, options) => { var dbSettings = serviceProvider .GetRequiredService>() .Value; options.UseInMemoryDatabase("test-db"); if (dbSettings.EnableSensitiveDataLogging) { options.EnableSensitiveDataLogging(); } } ); var serviceProvider = services.BuildServiceProvider(); var context = serviceProvider.GetRequiredService(); // Assert context.Should().NotBeNull(); context.Database.Should().NotBeNull(); context.ChatSessions.Should().NotBeNull(); context.ChatMessages.Should().NotBeNull(); } [Fact] public void ServiceRegistration_ShouldWorkWithoutValidation() { // Arrange var services = new ServiceCollection(); var configuration = new ConfigurationBuilder() .AddInMemoryCollection( new Dictionary { { "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(); // Act - Register services without validation services.AddSingleton(configuration); services.AddLogging(); services.Configure(configuration.GetSection("TelegramBot")); services.Configure(configuration.GetSection("Ollama")); services.Configure(configuration.GetSection("AI")); services.Configure(configuration.GetSection("Database")); services.AddDbContext( (serviceProvider, options) => { options.UseInMemoryDatabase("test-db"); } ); var serviceProvider = services.BuildServiceProvider(); // Assert - Check that configuration services are registered serviceProvider.GetRequiredService>().Should().NotBeNull(); serviceProvider.GetRequiredService>().Should().NotBeNull(); serviceProvider.GetRequiredService>().Should().NotBeNull(); serviceProvider.GetRequiredService>().Should().NotBeNull(); serviceProvider.GetRequiredService().Should().NotBeNull(); } [Fact] public void ConfigurationSections_ShouldBeAccessible() { // Arrange var configuration = new ConfigurationBuilder() .AddInMemoryCollection( new Dictionary { { "TelegramBot:BotToken", "1234567890123456789012345678901234567890" }, { "Ollama:Url", "http://localhost:11434" }, { "AI:CompressionThreshold", "100" }, { "Database:ConnectionString", "Host=localhost;Port=5432;Database=test;Username=test;Password=test" }, } ) .Build(); // Act & Assert configuration.GetSection("TelegramBot").Should().NotBeNull(); configuration.GetSection("Ollama").Should().NotBeNull(); configuration.GetSection("AI").Should().NotBeNull(); configuration.GetSection("Database").Should().NotBeNull(); configuration .GetSection("TelegramBot")["BotToken"] .Should() .Be("1234567890123456789012345678901234567890"); configuration.GetSection("Ollama")["Url"].Should().Be("http://localhost:11434"); configuration.GetSection("AI")["CompressionThreshold"].Should().Be("100"); configuration .GetSection("Database")["ConnectionString"] .Should() .Be("Host=localhost;Port=5432;Database=test;Username=test;Password=test"); } [Fact] public void DatabaseContext_ShouldHaveCorrectEntityTypes() { // Arrange var services = new ServiceCollection(); services.AddDbContext(options => options.UseInMemoryDatabase("test-db")); var serviceProvider = services.BuildServiceProvider(); var context = serviceProvider.GetRequiredService(); // Act var model = context.Model; // Assert var chatSessionEntity = model.FindEntityType( typeof(ChatBot.Models.Entities.ChatSessionEntity) ); var chatMessageEntity = model.FindEntityType( typeof(ChatBot.Models.Entities.ChatMessageEntity) ); chatSessionEntity.Should().NotBeNull(); chatMessageEntity.Should().NotBeNull(); chatSessionEntity!.GetTableName().Should().Be("chat_sessions"); chatMessageEntity!.GetTableName().Should().Be("chat_messages"); } }