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; /// /// Tests for ConnectionStringValidator which validates connection strings stored in SecureStore. /// Connection strings are referenced by name in configuration and retrieved from SecureStore. /// 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 - no connection strings in config means nothing to validate var validator = CreateValidator(new Dictionary()); // Act var result = validator.Validate(); // Assert result.IsValid.ShouldBeTrue(); result.Errors.ShouldBeEmpty(); result.Warnings.ShouldBeEmpty(); } [Fact] public void Validate_ConnectionStringInSecureStore_ValidatesFormat() { // Arrange - connection string name in config, value in SecureStore _secureStore.Set("SqlServer", "Server=localhost;Database=TestDb;Trusted_Connection=true;"); var validator = CreateValidator(new Dictionary { ["ConnectionStrings:SqlServer"] = "SqlServer" // Name only, value in SecureStore }); // Act var result = validator.Validate(); // Assert result.IsValid.ShouldBeTrue(); result.Errors.ShouldBeEmpty(); } [Fact] public void Validate_ConnectionStringNotInSecureStore_ReturnsError() { // Arrange - connection string name in config but not in SecureStore var validator = CreateValidator(new Dictionary { ["ConnectionStrings:SqlServer"] = "SqlServer" }); // Note: Not adding SqlServer to SecureStore // Act var result = validator.Validate(); // Assert result.IsValid.ShouldBeFalse(); result.Errors.ShouldContain(e => e.Contains("'SqlServer'") && e.Contains("not found in SecureStore")); } [Fact] public void Validate_EmptyConnectionStringInSecureStore_ReturnsError() { // Arrange - connection string exists in SecureStore but is empty _secureStore.Set("SqlServer", ""); var validator = CreateValidator(new Dictionary { ["ConnectionStrings:SqlServer"] = "SqlServer" }); // Act var result = validator.Validate(); // Assert result.IsValid.ShouldBeFalse(); result.Errors.ShouldContain(e => e.Contains("'SqlServer'") && e.Contains("empty in SecureStore")); } [Fact] public void Validate_WhitespaceOnlyConnectionStringInSecureStore_ReturnsError() { // Arrange - connection string exists in SecureStore but is whitespace only _secureStore.Set("SqlServer", " "); var validator = CreateValidator(new Dictionary { ["ConnectionStrings:SqlServer"] = "SqlServer" }); // Act var result = validator.Validate(); // Assert result.IsValid.ShouldBeFalse(); result.Errors.ShouldContain(e => e.Contains("'SqlServer'") && e.Contains("empty in SecureStore")); } [Fact] public void Validate_ValidSqlServerFormat_ValidatesSuccessfully() { // Arrange - SQL Server connection string format _secureStore.Set("SqlServer", "Server=localhost;Database=TestDb;Trusted_Connection=true;"); var validator = CreateValidator(new Dictionary { ["ConnectionStrings:SqlServer"] = "SqlServer" }); // Act var result = validator.Validate(); // Assert result.IsValid.ShouldBeTrue(); result.Errors.ShouldBeEmpty(); } [Fact] public void Validate_MissingServerOrDataSource_ReturnsError() { // Arrange - connection string with no Server= or Data Source= _secureStore.Set("Invalid", "Database=TestDb;Trusted_Connection=true;"); var validator = CreateValidator(new Dictionary { ["ConnectionStrings:Invalid"] = "Invalid" }); // 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 _secureStore.Set("Oracle", "Data Source=//localhost:1521/ORCL;User Id=test;Password=test;"); var validator = CreateValidator(new Dictionary { ["ConnectionStrings:Oracle"] = "Oracle" }); // Act var result = validator.Validate(); // Assert result.IsValid.ShouldBeTrue(); } [Fact] public void Validate_HostFormat_ValidatesSuccessfully() { // Arrange - PostgreSQL-style connection string _secureStore.Set("Postgres", "Host=localhost;Port=5432;Database=testdb;Username=test;Password=test;"); var validator = CreateValidator(new Dictionary { ["ConnectionStrings:Postgres"] = "Postgres" }); // Act var result = validator.Validate(); // Assert result.IsValid.ShouldBeTrue(); } [Fact] public void Validate_MultipleConnectionStrings_ValidatesAll() { // Arrange - multiple connection strings, one missing from SecureStore _secureStore.Set("SqlServer", "Server=localhost;Database=TestDb;Trusted_Connection=true;"); _secureStore.Set("Oracle", "Data Source=//localhost:1521/ORCL;User Id=test;Password=test;"); // Note: Not adding "Missing" to SecureStore var validator = CreateValidator(new Dictionary { ["ConnectionStrings:SqlServer"] = "SqlServer", ["ConnectionStrings:Oracle"] = "Oracle", ["ConnectionStrings:Missing"] = "Missing" }); // Act var result = validator.Validate(); // Assert result.IsValid.ShouldBeFalse(); result.Errors.Count.ShouldBe(1); // Only the missing one should fail result.Errors.ShouldContain(e => e.Contains("'Missing'") && e.Contains("not found in SecureStore")); } [Fact] public void Validate_UnknownConnectionFormat_AddsWarning() { // Arrange - connection string with Server= but no Database= _secureStore.Set("Custom", "Server=localhost;CustomProperty=value;"); var validator = CreateValidator(new Dictionary { ["ConnectionStrings:Custom"] = "Custom" }); // 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_CaseInsensitiveServerCheck_ValidatesSuccessfully() { // Arrange - mixed case Server _secureStore.Set("SqlServer", "server=localhost;Database=TestDb;"); var validator = CreateValidator(new Dictionary { ["ConnectionStrings:SqlServer"] = "SqlServer" }); // Act var result = validator.Validate(); // Assert result.IsValid.ShouldBeTrue(); } }