using ChatBot.Models.Configuration; using ChatBot.Models.Configuration.Validators; using FluentAssertions; using FluentValidation.TestHelper; using Microsoft.Extensions.Options; namespace ChatBot.Tests.Configuration.Validators; public class DatabaseSettingsValidatorTests { private readonly DatabaseSettingsValidator _validator; public DatabaseSettingsValidatorTests() { _validator = new DatabaseSettingsValidator(); } [Fact] public void Validate_WithValidSettings_ShouldReturnSuccess() { // Arrange var settings = CreateValidDatabaseSettings(); // Act var result = _validator.Validate(null, settings); // Assert result.Succeeded.Should().BeTrue(); result.Failed.Should().BeFalse(); result.FailureMessage.Should().BeNullOrEmpty(); } [Fact] public void Validate_WithEmptyConnectionString_ShouldReturnFailure() { // Arrange var settings = CreateValidDatabaseSettings(); settings.ConnectionString = string.Empty; // Act var result = _validator.Validate(null, settings); // Assert result.Succeeded.Should().BeFalse(); result.Failed.Should().BeTrue(); result.FailureMessage.Should().Contain("Database connection string is required"); } [Fact] public void Validate_WithNullConnectionString_ShouldReturnFailure() { // Arrange var settings = CreateValidDatabaseSettings(); settings.ConnectionString = null!; // Act var result = _validator.Validate(null, settings); // Assert result.Succeeded.Should().BeFalse(); result.Failed.Should().BeTrue(); result.FailureMessage.Should().Contain("Database connection string is required"); } [Fact] public void Validate_WithWhitespaceConnectionString_ShouldReturnFailure() { // Arrange var settings = CreateValidDatabaseSettings(); settings.ConnectionString = " "; // Act var result = _validator.Validate(null, settings); // Assert result.Succeeded.Should().BeFalse(); result.Failed.Should().BeTrue(); result.FailureMessage.Should().Contain("Database connection string is required"); } [Theory] [InlineData(0)] [InlineData(-1)] [InlineData(-10)] public void Validate_WithInvalidCommandTimeout_ShouldReturnFailure(int commandTimeout) { // Arrange var settings = CreateValidDatabaseSettings(); settings.CommandTimeout = commandTimeout; // Act var result = _validator.Validate(null, settings); // Assert result.Succeeded.Should().BeFalse(); result.Failed.Should().BeTrue(); result.FailureMessage.Should().Contain("Command timeout must be greater than 0"); } [Theory] [InlineData(301)] [InlineData(500)] [InlineData(1000)] public void Validate_WithTooHighCommandTimeout_ShouldReturnFailure(int commandTimeout) { // Arrange var settings = CreateValidDatabaseSettings(); settings.CommandTimeout = commandTimeout; // Act var result = _validator.Validate(null, settings); // Assert result.Succeeded.Should().BeFalse(); result.Failed.Should().BeTrue(); result .FailureMessage.Should() .Contain("Command timeout must be less than or equal to 300 seconds"); } [Theory] [InlineData(1)] [InlineData(30)] [InlineData(300)] public void Validate_WithValidCommandTimeout_ShouldReturnSuccess(int commandTimeout) { // Arrange var settings = CreateValidDatabaseSettings(); settings.CommandTimeout = commandTimeout; // Act var result = _validator.Validate(null, settings); // Assert result.Succeeded.Should().BeTrue(); result.Failed.Should().BeFalse(); result.FailureMessage.Should().BeNullOrEmpty(); } [Fact] public void Validate_WithValidConnectionString_ShouldReturnSuccess() { // Arrange var settings = CreateValidDatabaseSettings(); settings.ConnectionString = "Host=localhost;Port=5432;Database=test;Username=user;Password=pass"; // Act var result = _validator.Validate(null, settings); // Assert result.Succeeded.Should().BeTrue(); result.Failed.Should().BeFalse(); result.FailureMessage.Should().BeNullOrEmpty(); } [Fact] public void Validate_WithMultipleValidationErrors_ShouldReturnAllErrors() { // Arrange var settings = new DatabaseSettings { ConnectionString = string.Empty, // Invalid CommandTimeout = 0, // Invalid EnableSensitiveDataLogging = false, }; // Act var result = _validator.Validate(null, settings); // Assert result.Succeeded.Should().BeFalse(); result.Failed.Should().BeTrue(); result.FailureMessage.Should().Contain("Database connection string is required"); result.FailureMessage.Should().Contain("Command timeout must be greater than 0"); } [Fact] public void Validate_WithNullSettings_ShouldThrowException() { // Arrange & Act & Assert var act = () => _validator.Validate(null, null!); act.Should() .Throw() .WithMessage( "Cannot pass a null model to Validate/ValidateAsync. The root model must be non-null." ); } [Fact] public void FluentValidation_ConnectionString_ShouldHaveCorrectRule() { // Arrange var settings = new DatabaseSettings { ConnectionString = string.Empty }; // Act & Assert var result = _validator.TestValidate(settings); result .ShouldHaveValidationErrorFor(x => x.ConnectionString) .WithErrorMessage("Database connection string is required"); } [Fact] public void FluentValidation_CommandTimeout_ShouldHaveCorrectRules() { // Arrange var settings = new DatabaseSettings { CommandTimeout = 0 }; // Act & Assert var result = _validator.TestValidate(settings); result .ShouldHaveValidationErrorFor(x => x.CommandTimeout) .WithErrorMessage("Command timeout must be greater than 0"); } [Fact] public void FluentValidation_CommandTimeoutTooHigh_ShouldHaveCorrectRule() { // Arrange var settings = new DatabaseSettings { CommandTimeout = 301 }; // Act & Assert var result = _validator.TestValidate(settings); result .ShouldHaveValidationErrorFor(x => x.CommandTimeout) .WithErrorMessage("Command timeout must be less than or equal to 300 seconds"); } [Fact] public void FluentValidation_ValidSettings_ShouldNotHaveErrors() { // Arrange var settings = CreateValidDatabaseSettings(); // Act & Assert var result = _validator.TestValidate(settings); result.ShouldNotHaveAnyValidationErrors(); } [Theory] [InlineData("Host=localhost;Port=5432;Database=test;Username=user;Password=pass")] [InlineData("Server=localhost;Database=test;User Id=user;Password=pass")] [InlineData("Data Source=localhost;Initial Catalog=test;User ID=user;Password=pass")] public void FluentValidation_ValidConnectionStrings_ShouldNotHaveErrors(string connectionString) { // Arrange var settings = CreateValidDatabaseSettings(); settings.ConnectionString = connectionString; // Act & Assert var result = _validator.TestValidate(settings); result.ShouldNotHaveValidationErrorFor(x => x.ConnectionString); } [Theory] [InlineData(1)] [InlineData(5)] [InlineData(30)] [InlineData(60)] [InlineData(120)] [InlineData(300)] public void FluentValidation_ValidCommandTimeouts_ShouldNotHaveErrors(int commandTimeout) { // Arrange var settings = CreateValidDatabaseSettings(); settings.CommandTimeout = commandTimeout; // Act & Assert var result = _validator.TestValidate(settings); result.ShouldNotHaveValidationErrorFor(x => x.CommandTimeout); } [Fact] public void ValidateOptionsResult_Success_ShouldHaveCorrectProperties() { // Arrange var settings = CreateValidDatabaseSettings(); // Act var result = _validator.Validate(null, settings); // Assert result.Succeeded.Should().BeTrue(); result.Failed.Should().BeFalse(); result.FailureMessage.Should().BeNullOrEmpty(); } [Fact] public void ValidateOptionsResult_Failure_ShouldHaveCorrectProperties() { // Arrange var settings = new DatabaseSettings { ConnectionString = string.Empty, CommandTimeout = 0 }; // Act var result = _validator.Validate(null, settings); // Assert result.Succeeded.Should().BeFalse(); result.Failed.Should().BeTrue(); result.FailureMessage.Should().NotBeNullOrEmpty(); result.FailureMessage.Should().Contain("Database connection string is required"); result.FailureMessage.Should().Contain("Command timeout must be greater than 0"); } [Fact] public void Validator_ShouldImplementIValidateOptions() { // Arrange & Act var validator = new DatabaseSettingsValidator(); // Assert validator.Should().BeAssignableTo>(); } [Fact] public void Validator_ShouldInheritFromAbstractValidator() { // Arrange & Act var validator = new DatabaseSettingsValidator(); // Assert validator.Should().BeAssignableTo>(); } private static DatabaseSettings CreateValidDatabaseSettings() { return new DatabaseSettings { ConnectionString = "Host=localhost;Port=5432;Database=test;Username=test;Password=test", CommandTimeout = 30, EnableSensitiveDataLogging = false, }; } }