379 lines
14 KiB
C#
379 lines
14 KiB
C#
using System.Text;
|
|
using MBN_STOCK_WEBVIEW.Infrastructure;
|
|
using MMoneyCoderSharp.Data;
|
|
|
|
namespace MBN_STOCK_WEBVIEW.Infrastructure.Tests;
|
|
|
|
public sealed class LegacyIniDatabaseOptionsLoaderTests
|
|
{
|
|
private const string IntegrationPathEnvironmentVariable =
|
|
"MBN_STOCK_LEGACY_INI_INTEGRATION_PATH";
|
|
|
|
[Fact]
|
|
public void Load_Utf8LegacyFile_MapsBothProvidersAndKeepsLastValue()
|
|
{
|
|
using var file = TemporaryLegacyIni.Create(
|
|
Encoding.UTF8.GetBytes(
|
|
"""
|
|
[Oracle]
|
|
//ConnectionName=ignored.invalid:1/IGNORED
|
|
ConnectionName=oracle.dev.invalid:1522/DEVORA
|
|
ID=old-user
|
|
ID=oracle-user
|
|
Pass=oracle=password
|
|
|
|
[Maria]
|
|
ConnectionName=maria.dev.invalid:3307/dev_stock
|
|
ID=maria-user
|
|
Pass=maria=password
|
|
"""));
|
|
var loader = new LegacyIniDatabaseOptionsLoader(_ => null);
|
|
|
|
var options = loader.Load(file.Path);
|
|
|
|
Assert.True(options.IsConfigured(DataSourceKind.Oracle));
|
|
Assert.True(options.IsConfigured(DataSourceKind.MariaDb));
|
|
Assert.Equal("oracle.dev.invalid", options.Oracle.Host);
|
|
Assert.Equal(1522, options.Oracle.Port);
|
|
Assert.Equal("DEVORA", options.Oracle.Sid);
|
|
Assert.Null(options.Oracle.ServiceName);
|
|
Assert.Equal("oracle-user", options.Oracle.UserName);
|
|
Assert.Equal("oracle=password", options.Oracle.Password);
|
|
Assert.Equal("maria.dev.invalid", options.MariaDb.Host);
|
|
Assert.Equal(3307, options.MariaDb.Port);
|
|
Assert.Equal("dev_stock", options.MariaDb.Database);
|
|
Assert.Equal("maria-user", options.MariaDb.UserName);
|
|
Assert.Equal("maria=password", options.MariaDb.Password);
|
|
Assert.Equal(MariaDbTlsMode.Disabled, options.MariaDb.TlsMode);
|
|
}
|
|
|
|
[Fact]
|
|
public void Load_Cp949LegacyFile_PreservesKoreanValues()
|
|
{
|
|
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
|
|
var cp949 = Encoding.GetEncoding(
|
|
949,
|
|
EncoderFallback.ExceptionFallback,
|
|
DecoderFallback.ExceptionFallback);
|
|
using var file = TemporaryLegacyIni.Create(cp949.GetBytes(
|
|
"""
|
|
[Oracle]
|
|
ConnectionName=oracle.dev.invalid:1521/개발
|
|
ID=오라클사용자
|
|
Pass=암호
|
|
[Maria]
|
|
ConnectionName=maria.dev.invalid:3306/주식
|
|
ID=마리아사용자
|
|
Pass=암호2
|
|
"""));
|
|
|
|
var options = new LegacyIniDatabaseOptionsLoader(_ => null).Load(file.Path);
|
|
|
|
Assert.Equal("개발", options.Oracle.Sid);
|
|
Assert.Equal("오라클사용자", options.Oracle.UserName);
|
|
Assert.Equal("주식", options.MariaDb.Database);
|
|
Assert.Equal("마리아사용자", options.MariaDb.UserName);
|
|
}
|
|
|
|
[Fact]
|
|
public void Load_EnvironmentStillOverridesLegacyValues()
|
|
{
|
|
using var file = TemporaryLegacyIni.CreateValidUtf8();
|
|
var environment = new Dictionary<string, string>(StringComparer.Ordinal)
|
|
{
|
|
["MBN_STOCK_ORACLE_HOST"] = "override-oracle.invalid",
|
|
["MBN_STOCK_MARIADB_TLS_MODE"] = "Required"
|
|
};
|
|
var loader = new LegacyIniDatabaseOptionsLoader(
|
|
name => environment.TryGetValue(name, out var value) ? value : null);
|
|
|
|
var options = loader.Load(file.Path);
|
|
|
|
Assert.Equal("override-oracle.invalid", options.Oracle.Host);
|
|
Assert.Equal(MariaDbTlsMode.Required, options.MariaDb.TlsMode);
|
|
}
|
|
|
|
[Fact]
|
|
public void Load_InvalidFile_DoesNotExposeFileValues()
|
|
{
|
|
const string secret = "sensitive-password-sentinel";
|
|
using var file = TemporaryLegacyIni.Create(Encoding.UTF8.GetBytes(
|
|
$"[Oracle]\nConnectionName=invalid\nID=user\nPass={secret}"));
|
|
|
|
var exception = Assert.Throws<DatabaseConfigurationException>(
|
|
() => new LegacyIniDatabaseOptionsLoader(_ => null).Load(file.Path));
|
|
|
|
Assert.DoesNotContain(secret, exception.ToString(), StringComparison.Ordinal);
|
|
Assert.DoesNotContain(file.Path, exception.ToString(), StringComparison.Ordinal);
|
|
}
|
|
|
|
[Fact]
|
|
public void Runtime_PrefersExecutableResIni_AndFallsBackWhenAbsent()
|
|
{
|
|
using var directory = TemporaryDirectory.Create();
|
|
var res = Path.Combine(directory.Path, "Res");
|
|
Directory.CreateDirectory(res);
|
|
File.WriteAllBytes(
|
|
Path.Combine(res, "MmoneyCoder.ini"),
|
|
TemporaryLegacyIni.ValidUtf8Bytes());
|
|
|
|
var runtime = DatabaseRuntime.CreateLegacyExecutableDefault(
|
|
directory.Path,
|
|
Path.Combine(directory.Path, "missing.json"));
|
|
|
|
Assert.Equal("oracle.dev.invalid", runtime.Options.Oracle.Host);
|
|
|
|
File.Delete(Path.Combine(res, "MmoneyCoder.ini"));
|
|
var fallback = DatabaseRuntime.CreateLegacyExecutableDefault(
|
|
directory.Path,
|
|
Path.Combine(directory.Path, "missing.json"),
|
|
Path.Combine(directory.Path, "missing-legacy.ini"));
|
|
Assert.False(fallback.Options.IsConfigured(DataSourceKind.Oracle));
|
|
Assert.False(fallback.Options.IsConfigured(DataSourceKind.MariaDb));
|
|
}
|
|
|
|
[Fact]
|
|
public void Runtime_UsesLocalLegacyOverrideWhenPackageDoesNotContainCredentials()
|
|
{
|
|
using var directory = TemporaryDirectory.Create();
|
|
using var overrideFile = TemporaryLegacyIni.CreateValidUtf8();
|
|
|
|
var runtime = DatabaseRuntime.CreateLegacyExecutableDefault(
|
|
directory.Path,
|
|
Path.Combine(directory.Path, "missing.json"),
|
|
overrideFile.Path);
|
|
|
|
Assert.Equal("oracle.dev.invalid", runtime.Options.Oracle.Host);
|
|
Assert.Equal("maria.dev.invalid", runtime.Options.MariaDb.Host);
|
|
}
|
|
|
|
[Fact]
|
|
public void Runtime_UserSelectedResCanExplicitlyPrecedeExecutableCredentials()
|
|
{
|
|
using var directory = TemporaryDirectory.Create();
|
|
var executableRes = Path.Combine(directory.Path, "Res");
|
|
Directory.CreateDirectory(executableRes);
|
|
File.WriteAllBytes(
|
|
Path.Combine(executableRes, "MmoneyCoder.ini"),
|
|
TemporaryLegacyIni.ValidUtf8Bytes());
|
|
using var selected = TemporaryLegacyIni.Create(Encoding.UTF8.GetBytes(
|
|
"""
|
|
[Oracle]
|
|
ConnectionName=selected.oracle.invalid:1521/DEVORA
|
|
ID=oracle-user
|
|
Pass=oracle-password
|
|
[Maria]
|
|
ConnectionName=selected.maria.invalid:3306/dev_stock
|
|
ID=maria-user
|
|
Pass=maria-password
|
|
"""));
|
|
|
|
var runtime = DatabaseRuntime.CreateLegacyExecutableDefault(
|
|
directory.Path,
|
|
Path.Combine(directory.Path, "missing.json"),
|
|
selected.Path,
|
|
preferLegacyOverride: true);
|
|
|
|
Assert.Equal("selected.oracle.invalid", runtime.Options.Oracle.Host);
|
|
Assert.Equal("selected.maria.invalid", runtime.Options.MariaDb.Host);
|
|
}
|
|
|
|
[Fact]
|
|
public void Runtime_DevelopmentLiveUsesOnlyExactLocalAppDataIni()
|
|
{
|
|
using var localApplicationData = TemporaryDirectory.Create();
|
|
var resourceDirectory = Path.Combine(
|
|
localApplicationData.Path,
|
|
"MBN_STOCK_WEBVIEW",
|
|
"Res");
|
|
Directory.CreateDirectory(resourceDirectory);
|
|
File.WriteAllBytes(
|
|
Path.Combine(resourceDirectory, "MmoneyCoder.ini"),
|
|
TemporaryLegacyIni.ValidUtf8Bytes());
|
|
|
|
const string environmentName = "MBN_STOCK_ORACLE_HOST";
|
|
var originalEnvironment = Environment.GetEnvironmentVariable(environmentName);
|
|
DatabaseRuntime runtime;
|
|
try
|
|
{
|
|
Environment.SetEnvironmentVariable(
|
|
environmentName,
|
|
"stale-environment.invalid");
|
|
runtime = DatabaseRuntime.CreateDevelopmentLiveLocalOverride(
|
|
localApplicationData.Path);
|
|
}
|
|
finally
|
|
{
|
|
Environment.SetEnvironmentVariable(
|
|
environmentName,
|
|
originalEnvironment);
|
|
}
|
|
|
|
Assert.Equal("oracle.dev.invalid", runtime.Options.Oracle.Host);
|
|
Assert.Equal("maria.dev.invalid", runtime.Options.MariaDb.Host);
|
|
}
|
|
|
|
[Fact]
|
|
public void Runtime_DevelopmentLiveFailsClosedWhenBothProtectedProfilesAreMissing()
|
|
{
|
|
using var localApplicationData = TemporaryDirectory.Create();
|
|
Directory.CreateDirectory(Path.Combine(
|
|
localApplicationData.Path,
|
|
"MBN_STOCK_WEBVIEW",
|
|
"Config"));
|
|
|
|
Assert.Throws<DatabaseConfigurationException>(
|
|
() => DatabaseRuntime.CreateDevelopmentLiveLocalOverride(
|
|
localApplicationData.Path));
|
|
}
|
|
|
|
[Fact]
|
|
public void Runtime_DevelopmentLiveFallsBackToProtectedLocalAppDataJson()
|
|
{
|
|
using var localApplicationData = TemporaryDirectory.Create();
|
|
var configurationDirectory = Path.Combine(
|
|
localApplicationData.Path,
|
|
"MBN_STOCK_WEBVIEW",
|
|
"Config");
|
|
Directory.CreateDirectory(configurationDirectory);
|
|
File.WriteAllText(
|
|
Path.Combine(configurationDirectory, "database.local.json"),
|
|
"""
|
|
{
|
|
"oracle": {
|
|
"host": "json.oracle.invalid",
|
|
"port": 1521,
|
|
"sid": "DEVORA",
|
|
"serviceName": null,
|
|
"userName": "oracle-user",
|
|
"password": "oracle-password"
|
|
},
|
|
"mariaDb": {
|
|
"host": "json.maria.invalid",
|
|
"port": 3306,
|
|
"database": "dev_stock",
|
|
"userName": "maria-user",
|
|
"password": "maria-password",
|
|
"tlsMode": "Required"
|
|
}
|
|
}
|
|
""");
|
|
|
|
var runtime = DatabaseRuntime.CreateDevelopmentLiveLocalOverride(
|
|
localApplicationData.Path);
|
|
|
|
Assert.Equal("json.oracle.invalid", runtime.Options.Oracle.Host);
|
|
Assert.Equal("json.maria.invalid", runtime.Options.MariaDb.Host);
|
|
Assert.Equal(MariaDbTlsMode.Required, runtime.Options.MariaDb.TlsMode);
|
|
}
|
|
|
|
[Fact]
|
|
public void Runtime_DevelopmentLiveRejectsNonOrdinaryIniTarget()
|
|
{
|
|
using var localApplicationData = TemporaryDirectory.Create();
|
|
var iniDirectory = Path.Combine(
|
|
localApplicationData.Path,
|
|
"MBN_STOCK_WEBVIEW",
|
|
"Res",
|
|
"MmoneyCoder.ini");
|
|
Directory.CreateDirectory(iniDirectory);
|
|
|
|
Assert.Throws<DatabaseConfigurationException>(
|
|
() => DatabaseRuntime.CreateDevelopmentLiveLocalOverride(
|
|
localApplicationData.Path));
|
|
}
|
|
|
|
[Fact]
|
|
public void Runtime_DevelopmentLiveRejectsNonOrdinaryJsonTarget()
|
|
{
|
|
using var localApplicationData = TemporaryDirectory.Create();
|
|
var jsonDirectory = Path.Combine(
|
|
localApplicationData.Path,
|
|
"MBN_STOCK_WEBVIEW",
|
|
"Config",
|
|
"database.local.json");
|
|
Directory.CreateDirectory(jsonDirectory);
|
|
|
|
Assert.Throws<DatabaseConfigurationException>(
|
|
() => DatabaseRuntime.CreateDevelopmentLiveLocalOverride(
|
|
localApplicationData.Path));
|
|
}
|
|
|
|
[Fact]
|
|
public void Load_OptionalRealLegacyIni_IsConfiguredWithoutPrintingValues()
|
|
{
|
|
var path = Environment.GetEnvironmentVariable(
|
|
IntegrationPathEnvironmentVariable);
|
|
if (string.IsNullOrWhiteSpace(path))
|
|
{
|
|
return;
|
|
}
|
|
|
|
var options = new LegacyIniDatabaseOptionsLoader().Load(path);
|
|
|
|
Assert.True(options.IsConfigured(DataSourceKind.Oracle));
|
|
Assert.True(options.IsConfigured(DataSourceKind.MariaDb));
|
|
}
|
|
|
|
private sealed class TemporaryLegacyIni : IDisposable
|
|
{
|
|
private TemporaryLegacyIni(string directory, string path)
|
|
{
|
|
Directory = directory;
|
|
Path = path;
|
|
}
|
|
|
|
public string Directory { get; }
|
|
|
|
public string Path { get; }
|
|
|
|
public static TemporaryLegacyIni CreateValidUtf8() =>
|
|
Create(ValidUtf8Bytes());
|
|
|
|
public static byte[] ValidUtf8Bytes() => Encoding.UTF8.GetBytes(
|
|
"""
|
|
[Oracle]
|
|
ConnectionName=oracle.dev.invalid:1521/DEVORA
|
|
ID=oracle-user
|
|
Pass=oracle-password
|
|
[Maria]
|
|
ConnectionName=maria.dev.invalid:3306/dev_stock
|
|
ID=maria-user
|
|
Pass=maria-password
|
|
""");
|
|
|
|
public static TemporaryLegacyIni Create(byte[] bytes)
|
|
{
|
|
var directory = System.IO.Path.Combine(
|
|
System.IO.Path.GetTempPath(),
|
|
Guid.NewGuid().ToString("N"));
|
|
System.IO.Directory.CreateDirectory(directory);
|
|
var path = System.IO.Path.Combine(directory, "MmoneyCoder.ini");
|
|
File.WriteAllBytes(path, bytes);
|
|
return new TemporaryLegacyIni(directory, path);
|
|
}
|
|
|
|
public void Dispose() =>
|
|
System.IO.Directory.Delete(Directory, recursive: true);
|
|
}
|
|
|
|
private sealed class TemporaryDirectory : IDisposable
|
|
{
|
|
private TemporaryDirectory(string path) => Path = path;
|
|
|
|
public string Path { get; }
|
|
|
|
public static TemporaryDirectory Create()
|
|
{
|
|
var path = System.IO.Path.Combine(
|
|
System.IO.Path.GetTempPath(),
|
|
Guid.NewGuid().ToString("N"));
|
|
System.IO.Directory.CreateDirectory(path);
|
|
return new TemporaryDirectory(path);
|
|
}
|
|
|
|
public void Dispose() =>
|
|
System.IO.Directory.Delete(Path, recursive: true);
|
|
}
|
|
}
|