using JdeScoping.Infrastructure.Tests.Helpers; using JdeScoping.Infrastructure.Validation; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging.Abstractions; using Shouldly; namespace JdeScoping.Infrastructure.Tests.Validation; public class ConnectionStringValidatorTests : IDisposable { private readonly InMemorySecureStore _secureStore; public ConnectionStringValidatorTests() { _secureStore = new InMemorySecureStore(); } public void Dispose() { _secureStore.Dispose(); } private ConnectionStringValidator CreateValidator(Dictionary configValues) { var configuration = new ConfigurationBuilder() .AddInMemoryCollection(configValues) .Build(); return new ConnectionStringValidator( configuration, _secureStore, NullLogger.Instance); } [Fact] public void Order_Returns150() { // Arrange var validator = CreateValidator(new Dictionary()); // Assert validator.Order.ShouldBe(150); } [Fact] public void Name_ReturnsConnectionStrings() { // Arrange var validator = CreateValidator(new Dictionary()); // Assert validator.Name.ShouldBe("ConnectionStrings"); } [Fact] public void Validate_NoConnectionStrings_ReturnsValid() { // Arrange var validator = CreateValidator(new Dictionary()); // Act var result = validator.Validate(); // Assert result.IsValid.ShouldBeTrue(); result.Errors.ShouldBeEmpty(); result.Warnings.ShouldBeEmpty(); } [Fact] public void Validate_ConnectionStringWithNoPlaceholders_ValidatesFormat() { // Arrange var validator = CreateValidator(new Dictionary { ["ConnectionStrings:SqlServer"] = "Server=localhost;Database=TestDb;Trusted_Connection=true;" }); // Act var result = validator.Validate(); // Assert result.IsValid.ShouldBeTrue(); result.Errors.ShouldBeEmpty(); } [Fact] public void Validate_EmptyConnectionString_ReturnsError() { // Arrange var validator = CreateValidator(new Dictionary { ["ConnectionStrings:SqlServer"] = "" }); // Act var result = validator.Validate(); // Assert result.IsValid.ShouldBeFalse(); result.Errors.ShouldContain("Connection string 'SqlServer' is empty"); } [Fact] public void Validate_PlaceholderKeyNotInSecureStore_ReturnsError() { // Arrange var validator = CreateValidator(new Dictionary { ["ConnectionStrings:SqlServer"] = "Server=localhost;Database=TestDb;Password=${DB_PASSWORD};" }); // Note: Not adding DB_PASSWORD to SecureStore // Act var result = validator.Validate(); // Assert result.IsValid.ShouldBeFalse(); result.Errors.ShouldContain(e => e.Contains("'${DB_PASSWORD}'") && e.Contains("not found in SecureStore")); } [Fact] public void Validate_PlaceholderKeyHasEmptyValue_ReturnsError() { // Arrange _secureStore.Set("DB_PASSWORD", ""); var validator = CreateValidator(new Dictionary { ["ConnectionStrings:SqlServer"] = "Server=localhost;Database=TestDb;Password=${DB_PASSWORD};" }); // Act var result = validator.Validate(); // Assert result.IsValid.ShouldBeFalse(); result.Errors.ShouldContain(e => e.Contains("'${DB_PASSWORD}'") && e.Contains("empty value")); } [Fact] public void Validate_AllPlaceholdersResolved_ValidatesFormat() { // Arrange _secureStore.Set("DB_PASSWORD", "secretpassword"); var validator = CreateValidator(new Dictionary { ["ConnectionStrings:SqlServer"] = "Server=localhost;Database=TestDb;Password=${DB_PASSWORD};" }); // Act var result = validator.Validate(); // Assert result.IsValid.ShouldBeTrue(); result.Errors.ShouldBeEmpty(); } [Fact] public void Validate_MultiplePlaceholders_ResolvesAll() { // Arrange _secureStore.Set("DB_SERVER", "prodserver.local"); _secureStore.Set("DB_USER", "app_user"); _secureStore.Set("DB_PASSWORD", "secretpassword"); var validator = CreateValidator(new Dictionary { ["ConnectionStrings:SqlServer"] = "Server=${DB_SERVER};Database=TestDb;User Id=${DB_USER};Password=${DB_PASSWORD};" }); // Act var result = validator.Validate(); // Assert result.IsValid.ShouldBeTrue(); result.Errors.ShouldBeEmpty(); } [Fact] public void Validate_MultiplePlaceholders_ReportsAllMissing() { // Arrange - only set one of three required placeholders _secureStore.Set("DB_SERVER", "prodserver.local"); var validator = CreateValidator(new Dictionary { ["ConnectionStrings:SqlServer"] = "Server=${DB_SERVER};Database=TestDb;User Id=${DB_USER};Password=${DB_PASSWORD};" }); // Act var result = validator.Validate(); // Assert result.IsValid.ShouldBeFalse(); result.Errors.Count.ShouldBe(2); // DB_USER and DB_PASSWORD missing result.Errors.ShouldContain(e => e.Contains("'${DB_USER}'")); result.Errors.ShouldContain(e => e.Contains("'${DB_PASSWORD}'")); } [Fact] public void Validate_MissingServerOrDataSource_ReturnsError() { // Arrange - connection string with no Server= or Data Source= var validator = CreateValidator(new Dictionary { ["ConnectionStrings:Invalid"] = "Database=TestDb;Trusted_Connection=true;" }); // Act var result = validator.Validate(); // Assert result.IsValid.ShouldBeFalse(); result.Errors.ShouldContain(e => e.Contains("'Invalid'") && e.Contains("missing required")); } [Fact] public void Validate_DataSourceFormat_ValidatesSuccessfully() { // Arrange - Oracle-style connection string var validator = CreateValidator(new Dictionary { ["ConnectionStrings:Oracle"] = "Data Source=//localhost:1521/ORCL;User Id=test;Password=test;" }); // Act var result = validator.Validate(); // Assert result.IsValid.ShouldBeTrue(); } [Fact] public void Validate_HostFormat_ValidatesSuccessfully() { // Arrange - PostgreSQL-style connection string var validator = CreateValidator(new Dictionary { ["ConnectionStrings:Postgres"] = "Host=localhost;Port=5432;Database=testdb;Username=test;Password=test;" }); // Act var result = validator.Validate(); // Assert result.IsValid.ShouldBeTrue(); } [Fact] public void Validate_MultipleConnectionStrings_ValidatesAll() { // Arrange _secureStore.Set("SQL_PASSWORD", "sqlpass"); var validator = CreateValidator(new Dictionary { ["ConnectionStrings:SqlServer"] = "Server=localhost;Database=TestDb;Password=${SQL_PASSWORD};", ["ConnectionStrings:Oracle"] = "Data Source=//localhost:1521/ORCL;User Id=test;Password=test;", ["ConnectionStrings:InvalidEmpty"] = "" }); // Act var result = validator.Validate(); // Assert result.IsValid.ShouldBeFalse(); result.Errors.Count.ShouldBe(1); // Only the empty one should fail result.Errors.ShouldContain("Connection string 'InvalidEmpty' is empty"); } [Fact] public void Validate_UnknownConnectionFormat_AddsWarning() { // Arrange - connection string with Server= but no Database= var validator = CreateValidator(new Dictionary { ["ConnectionStrings:Custom"] = "Server=localhost;CustomProperty=value;" }); // Act var result = validator.Validate(); // Assert result.IsValid.ShouldBeTrue(); // Still valid, just a warning result.Warnings.ShouldContain(w => w.Contains("'Custom'") && w.Contains("unknown format")); } [Fact] public void Validate_WhitespaceOnlyConnectionString_ReturnsError() { // Arrange var validator = CreateValidator(new Dictionary { ["ConnectionStrings:SqlServer"] = " " }); // Act var result = validator.Validate(); // Assert result.IsValid.ShouldBeFalse(); result.Errors.ShouldContain("Connection string 'SqlServer' is empty"); } [Fact] public void Validate_CaseInsensitiveServerCheck_ValidatesSuccessfully() { // Arrange - mixed case Server var validator = CreateValidator(new Dictionary { ["ConnectionStrings:SqlServer"] = "server=localhost;Database=TestDb;" }); // Act var result = validator.Validate(); // Assert result.IsValid.ShouldBeTrue(); } }