Files
Joseph Doherty bfc1c8064a refactor(securestore): store entire connection strings in SecureStore
Eliminates placeholder substitution (${KEY}) in favor of storing complete
connection strings as single encrypted values. SecureStore now auto-creates
entries for all connection strings defined in appsettings. ConfigManager
editor reads/writes values directly to SecureStore.
2026-01-23 14:44:04 -05:00

275 lines
8.8 KiB
C#

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;
/// <summary>
/// Tests for ConnectionStringValidator which validates connection strings stored in SecureStore.
/// Connection strings are referenced by name in configuration and retrieved from SecureStore.
/// </summary>
public class ConnectionStringValidatorTests : IDisposable
{
private readonly InMemorySecureStore _secureStore;
public ConnectionStringValidatorTests()
{
_secureStore = new InMemorySecureStore();
}
public void Dispose()
{
_secureStore.Dispose();
}
private ConnectionStringValidator CreateValidator(Dictionary<string, string?> configValues)
{
var configuration = new ConfigurationBuilder()
.AddInMemoryCollection(configValues)
.Build();
return new ConnectionStringValidator(
configuration,
_secureStore,
NullLogger<ConnectionStringValidator>.Instance);
}
[Fact]
public void Order_Returns150()
{
// Arrange
var validator = CreateValidator(new Dictionary<string, string?>());
// Assert
validator.Order.ShouldBe(150);
}
[Fact]
public void Name_ReturnsConnectionStrings()
{
// Arrange
var validator = CreateValidator(new Dictionary<string, string?>());
// 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<string, string?>());
// 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<string, string?>
{
["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<string, string?>
{
["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<string, string?>
{
["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<string, string?>
{
["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<string, string?>
{
["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<string, string?>
{
["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<string, string?>
{
["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<string, string?>
{
["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<string, string?>
{
["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<string, string?>
{
["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<string, string?>
{
["ConnectionStrings:SqlServer"] = "SqlServer"
});
// Act
var result = validator.Validate();
// Assert
result.IsValid.ShouldBeTrue();
}
}