fix: initialize selected runtime for live playout

This commit is contained in:
2026-07-28 20:44:20 +09:00
parent 18d40e892f
commit 818acad646
24 changed files with 906 additions and 391 deletions

View File

@@ -179,7 +179,7 @@ public sealed class LegacyIniDatabaseOptionsLoaderTests
}
[Fact]
public void Runtime_DevelopmentLiveUsesOnlyExactLocalAppDataIni()
public void Runtime_DevelopmentLiveUsesProtectedLocalAppDataIniWhenNoOperatorResIsSelected()
{
using var localApplicationData = TemporaryDirectory.Create();
var resourceDirectory = Path.Combine(
@@ -200,7 +200,8 @@ public sealed class LegacyIniDatabaseOptionsLoaderTests
environmentName,
"stale-environment.invalid");
runtime = DatabaseRuntime.CreateDevelopmentLiveLocalOverride(
localApplicationData.Path);
localApplicationData.Path,
operatorResourceDirectory: null);
}
finally
{
@@ -213,6 +214,170 @@ public sealed class LegacyIniDatabaseOptionsLoaderTests
Assert.Equal("maria.dev.invalid", runtime.Options.MariaDb.Host);
}
[Fact]
public void Runtime_DevelopmentLiveSelectedResIniIsAuthoritativeAndIgnoresEnvironment()
{
using var localApplicationData = TemporaryDirectory.Create();
var protectedResourceDirectory = Path.Combine(
localApplicationData.Path,
"MBN_STOCK_WEBVIEW",
"Res");
Directory.CreateDirectory(protectedResourceDirectory);
File.WriteAllBytes(
Path.Combine(protectedResourceDirectory, "MmoneyCoder.ini"),
TemporaryLegacyIni.ValidUtf8Bytes());
using var selected = TemporaryLegacyIni.Create(Encoding.UTF8.GetBytes(
"""
[Oracle]
ConnectionName=selected.oracle.invalid:1523/SELECTED
ID=selected-oracle-user
Pass=selected-oracle-password
[Maria]
ConnectionName=selected.maria.invalid:3308/selected_stock
ID=selected-maria-user
Pass=selected-maria-password
"""));
var environmentOverrides = new Dictionary<string, string>
{
["MBN_STOCK_ORACLE_HOST"] = "stale-environment.invalid",
["MBN_STOCK_ORACLE_PORT"] = "not-an-integer",
["MBN_STOCK_MARIADB_TLS_MODE"] = "Required"
};
var originalEnvironment = environmentOverrides.Keys.ToDictionary(
static name => name,
Environment.GetEnvironmentVariable,
StringComparer.Ordinal);
DatabaseRuntime runtime;
try
{
foreach (var pair in environmentOverrides)
{
Environment.SetEnvironmentVariable(pair.Key, pair.Value);
}
runtime = DatabaseRuntime.CreateDevelopmentLiveLocalOverride(
localApplicationData.Path,
selected.Directory);
}
finally
{
foreach (var pair in originalEnvironment)
{
Environment.SetEnvironmentVariable(pair.Key, pair.Value);
}
}
Assert.Equal("selected.oracle.invalid", runtime.Options.Oracle.Host);
Assert.Equal(1523, runtime.Options.Oracle.Port);
Assert.Equal("SELECTED", runtime.Options.Oracle.Sid);
Assert.Equal("selected.maria.invalid", runtime.Options.MariaDb.Host);
Assert.Equal(3308, runtime.Options.MariaDb.Port);
Assert.Equal("selected_stock", runtime.Options.MariaDb.Database);
Assert.Equal(MariaDbTlsMode.Disabled, runtime.Options.MariaDb.TlsMode);
}
[Fact]
public void Runtime_DevelopmentLiveReadsSelectedIniOnEveryCreation()
{
using var selected = TemporaryLegacyIni.Create(Encoding.UTF8.GetBytes(
"""
[Oracle]
ConnectionName=first.oracle.invalid:1521/FIRST
ID=oracle-user
Pass=oracle-password
[Maria]
ConnectionName=first.maria.invalid:3306/first_stock
ID=maria-user
Pass=maria-password
"""));
var first = DatabaseRuntime.CreateDevelopmentLiveLocalOverride(selected.Directory);
File.WriteAllBytes(selected.Path, Encoding.UTF8.GetBytes(
"""
[Oracle]
ConnectionName=second.oracle.invalid:1521/SECOND
ID=oracle-user
Pass=oracle-password
[Maria]
ConnectionName=second.maria.invalid:3306/second_stock
ID=maria-user
Pass=maria-password
"""));
var second = DatabaseRuntime.CreateDevelopmentLiveLocalOverride(selected.Directory);
Assert.Equal("first.oracle.invalid", first.Options.Oracle.Host);
Assert.Equal("first.maria.invalid", first.Options.MariaDb.Host);
Assert.Equal("second.oracle.invalid", second.Options.Oracle.Host);
Assert.Equal("second.maria.invalid", second.Options.MariaDb.Host);
}
[Fact]
public void Runtime_DevelopmentLiveSelectedResDoesNotDependOnLocalAppDataRoot()
{
using var selected = TemporaryLegacyIni.CreateValidUtf8();
var runtime = DatabaseRuntime.CreateDevelopmentLiveLocalOverride(
localApplicationData: string.Empty,
operatorResourceDirectory: selected.Directory);
Assert.Equal("oracle.dev.invalid", runtime.Options.Oracle.Host);
Assert.Equal("maria.dev.invalid", runtime.Options.MariaDb.Host);
}
[Fact]
public void Runtime_DevelopmentLiveSelectedResDoesNotFallBackWhenExactIniIsMissing()
{
using var localApplicationData = TemporaryDirectory.Create();
var protectedResourceDirectory = Path.Combine(
localApplicationData.Path,
"MBN_STOCK_WEBVIEW",
"Res");
Directory.CreateDirectory(protectedResourceDirectory);
File.WriteAllBytes(
Path.Combine(protectedResourceDirectory, "MmoneyCoder.ini"),
TemporaryLegacyIni.ValidUtf8Bytes());
using var selectedResourceDirectory = TemporaryDirectory.Create();
File.WriteAllBytes(
Path.Combine(selectedResourceDirectory.Path, "MmoneyCoder.ini.bak"),
TemporaryLegacyIni.ValidUtf8Bytes());
Assert.Throws<DatabaseConfigurationException>(
() => DatabaseRuntime.CreateDevelopmentLiveLocalOverride(
localApplicationData.Path,
selectedResourceDirectory.Path));
}
[Fact]
public void Runtime_DevelopmentLiveSelectedResDoesNotFallBackWhenIniIsInvalid()
{
const string secret = "selected-secret-sentinel";
using var localApplicationData = TemporaryDirectory.Create();
var protectedResourceDirectory = Path.Combine(
localApplicationData.Path,
"MBN_STOCK_WEBVIEW",
"Res");
Directory.CreateDirectory(protectedResourceDirectory);
File.WriteAllBytes(
Path.Combine(protectedResourceDirectory, "MmoneyCoder.ini"),
TemporaryLegacyIni.ValidUtf8Bytes());
using var selected = TemporaryLegacyIni.Create(Encoding.UTF8.GetBytes(
"""
[Oracle]
ConnectionName=invalid
ID=oracle-user
Pass=selected-secret-sentinel
"""));
var exception = Assert.Throws<DatabaseConfigurationException>(
() => DatabaseRuntime.CreateDevelopmentLiveLocalOverride(
localApplicationData.Path,
selected.Directory));
Assert.DoesNotContain(secret, exception.ToString(), StringComparison.Ordinal);
Assert.DoesNotContain(selected.Path, exception.ToString(), StringComparison.Ordinal);
}
[Fact]
public void Runtime_DevelopmentLiveFailsClosedWhenBothProtectedProfilesAreMissing()
{
@@ -224,7 +389,8 @@ public sealed class LegacyIniDatabaseOptionsLoaderTests
Assert.Throws<DatabaseConfigurationException>(
() => DatabaseRuntime.CreateDevelopmentLiveLocalOverride(
localApplicationData.Path));
localApplicationData.Path,
operatorResourceDirectory: null));
}
[Fact]
@@ -260,7 +426,8 @@ public sealed class LegacyIniDatabaseOptionsLoaderTests
""");
var runtime = DatabaseRuntime.CreateDevelopmentLiveLocalOverride(
localApplicationData.Path);
localApplicationData.Path,
operatorResourceDirectory: null);
Assert.Equal("json.oracle.invalid", runtime.Options.Oracle.Host);
Assert.Equal("json.maria.invalid", runtime.Options.MariaDb.Host);
@@ -280,7 +447,8 @@ public sealed class LegacyIniDatabaseOptionsLoaderTests
Assert.Throws<DatabaseConfigurationException>(
() => DatabaseRuntime.CreateDevelopmentLiveLocalOverride(
localApplicationData.Path));
localApplicationData.Path,
operatorResourceDirectory: null));
}
[Fact]
@@ -296,7 +464,8 @@ public sealed class LegacyIniDatabaseOptionsLoaderTests
Assert.Throws<DatabaseConfigurationException>(
() => DatabaseRuntime.CreateDevelopmentLiveLocalOverride(
localApplicationData.Path));
localApplicationData.Path,
operatorResourceDirectory: null));
}
[Fact]

View File

@@ -117,7 +117,7 @@ public sealed class LegacyDevelopmentLiveStartupContractTests
}
[Fact]
public void AppliedDevelopmentLiveUsesSelectedRuntimePathsAndProtectedDatabaseInputs()
public void AppliedDevelopmentLiveUsesSelectedRuntimePathsAndSelectedResDatabaseAuthority()
{
var window = File.ReadAllText(Path.Combine(ParityRoot, "MainWindow.xaml.cs"));
var playout = File.ReadAllText(Path.Combine(
@@ -142,14 +142,15 @@ public sealed class LegacyDevelopmentLiveStartupContractTests
window,
StringComparison.Ordinal);
Assert.Contains(
"_isDevelopmentLiveLaunch ||\n" +
"var selectedLegacyDatabaseIni =\n" +
" selectedResourceDirectory is null\n" +
" ? null",
window.ReplaceLineEndings("\n"),
StringComparison.Ordinal);
Assert.Contains(
"DatabaseRuntime.CreateDevelopmentLiveLocalOverride()",
window,
"? DatabaseRuntime.CreateDevelopmentLiveLocalOverride(\n" +
" selectedResourceDirectory)",
window.ReplaceLineEndings("\n"),
StringComparison.Ordinal);
Assert.Contains(
"? PlayoutOptionsLoader.LoadDevelopmentLive(",
@@ -168,7 +169,7 @@ public sealed class LegacyDevelopmentLiveStartupContractTests
}
[Fact]
public void DatabaseFailureDoesNotDowngradeOrBlockValidatedLiveEngine()
public void InvalidDevelopmentLiveDatabaseConfigurationBlocksEngineAndAutomaticConnect()
{
var window = File.ReadAllText(Path.Combine(ParityRoot, "MainWindow.xaml.cs"))
.ReplaceLineEndings("\n");
@@ -177,44 +178,50 @@ public sealed class LegacyDevelopmentLiveStartupContractTests
"MainWindow.Playout.cs"))
.ReplaceLineEndings("\n");
var runtimeReset = window.IndexOf(
"_databaseRuntime = null;",
var databaseFailureCondition = window.IndexOf(
"_isDevelopmentLiveLaunch &&\n" +
" exception is DatabaseConfigurationException;",
StringComparison.Ordinal);
var pagePlanReset = window.IndexOf(
"fixedPagePlanProvider = null;",
runtimeReset,
var startupBlock = window.IndexOf(
"_developmentLiveStartupBlocked = true;",
databaseFailureCondition,
StringComparison.Ordinal);
var databaseFailureEnd = window.IndexOf(
"_industryWorkflow = new LegacyIndustrySelectionWorkflow(",
pagePlanReset,
var safeMessage = window.IndexOf(
"\"Development Live 데이터베이스 설정을 확인할 수 없습니다.\"",
startupBlock,
StringComparison.Ordinal);
var playoutInitialization = window.IndexOf(
"InitializePlayoutRuntime();",
safeMessage,
StringComparison.Ordinal);
var automaticConnectGuard = window.IndexOf(
"if (!_developmentLiveStartupBlocked)",
playoutInitialization,
StringComparison.Ordinal);
var automaticConnect = window.IndexOf(
"await ConnectPlayoutAsync(_lifetimeCancellation.Token);",
automaticConnectGuard,
StringComparison.Ordinal);
var earlyBlock = playout.IndexOf(
"if (_developmentLiveStartupBlocked)",
StringComparison.Ordinal);
var earlyReturn = playout.IndexOf(
"return;",
earlyBlock,
StringComparison.Ordinal);
var createEngine = playout.IndexOf(
"_playoutEngine = PlayoutEngineFactory.Create(",
StringComparison.Ordinal);
Assert.True(runtimeReset >= 0);
Assert.True(pagePlanReset > runtimeReset);
Assert.True(databaseFailureEnd > pagePlanReset);
Assert.True(playoutInitialization > databaseFailureEnd);
Assert.DoesNotContain(
"_developmentLiveStartupBlocked = true;",
window[runtimeReset..databaseFailureEnd],
StringComparison.Ordinal);
Assert.DoesNotContain("databaseInitializationSucceeded", window,
StringComparison.Ordinal);
Assert.Contains(
"_isDevelopmentLiveLaunch\n" +
" ? PlayoutOptionsLoader.LoadDevelopmentLive(",
playout,
StringComparison.Ordinal);
Assert.Contains(
"if (_playoutOptions.Mode != PlayoutMode.Live)",
playout,
StringComparison.Ordinal);
Assert.Contains(
"_playoutEngine = PlayoutEngineFactory.Create(",
playout,
StringComparison.Ordinal);
Assert.True(databaseFailureCondition >= 0);
Assert.True(startupBlock > databaseFailureCondition);
Assert.True(safeMessage > startupBlock);
Assert.True(playoutInitialization > safeMessage);
Assert.True(automaticConnectGuard > playoutInitialization);
Assert.True(automaticConnect > automaticConnectGuard);
Assert.True(earlyBlock >= 0);
Assert.True(earlyReturn > earlyBlock);
Assert.True(createEngine > earlyReturn);
}
[Fact]

View File

@@ -70,7 +70,7 @@ public sealed class LegacyFirstRunSetupNativeContractTests
}
[Fact]
public void BothFolderSelectionsValidateOnlyCanonicalPaths()
public void BothFolderSelectionsValidateCanonicalPathsAndTheSelectedDatabaseIni()
{
var setup = File.ReadAllText(Path.Combine(
AppRoot,
@@ -84,8 +84,11 @@ public sealed class LegacyFirstRunSetupNativeContractTests
"LegacyOperatorSettingsFolderKind.Resource",
setup,
StringComparison.Ordinal);
Assert.DoesNotContain("MmoneyCoder.ini", setup, StringComparison.Ordinal);
Assert.DoesNotContain("LegacyIniDatabaseOptionsLoader", setup,
Assert.Contains(
"Path.Combine(resource, \"MmoneyCoder.ini\")",
setup,
StringComparison.Ordinal);
Assert.Contains("LegacyIniDatabaseOptionsLoader", setup,
StringComparison.Ordinal);
var clearCuts = setup.IndexOf(
"_cutsDirectory = null;",
@@ -116,29 +119,78 @@ public sealed class LegacyFirstRunSetupNativeContractTests
}
[Fact]
public void SetupPersistsOnlyTheSelectedPathsWithoutStartingAChildProcess()
public void SetupRunsTheVerifiedInitializerAndVerifiesEveryDurableOutput()
{
var setup = File.ReadAllText(Path.Combine(
AppRoot,
"FirstRunSetupWindow.xaml.cs"));
var saveSettings = RequiredIndex(
setup,
"var saved = store.Save(current with");
var saveBinding = RequiredIndex(
setup,
"SaveRuntimeBinding(repositoryRoot, runtimeRoot);");
Assert.True(saveSettings < saveBinding);
Assert.Contains(
"var store = new LegacyOperatorSettingsStore();",
"await RunVerifiedSetupAsync(",
setup,
StringComparison.Ordinal);
Assert.Contains(
"SceneDirectory = cutsDirectory",
"new ProcessStartInfo",
setup,
StringComparison.Ordinal);
Assert.Contains(
"ResourceDirectory = resourceDirectory",
"UseShellExecute = false",
setup,
StringComparison.Ordinal);
Assert.Contains(
"CreateNoWindow = true",
setup,
StringComparison.Ordinal);
Assert.Contains(
"RedirectStandardOutput = true",
setup,
StringComparison.Ordinal);
Assert.Contains(
"RedirectStandardError = true",
setup,
StringComparison.Ordinal);
Assert.Contains(
"startInfo.ArgumentList.Add(value)",
setup,
StringComparison.Ordinal);
Assert.Contains(
"Initialize-ExistingDevelopmentPc.ps1",
setup,
StringComparison.Ordinal);
Assert.Contains(
"AddArgument(\"-SkipDatabaseProfile\")",
setup,
StringComparison.Ordinal);
Assert.Contains(
"AddArgument(\"-ConfigureDevelopmentLive\")",
setup,
StringComparison.Ordinal);
Assert.Contains(
"AddArgument(\"127.0.0.1\")",
setup,
StringComparison.Ordinal);
Assert.Contains(
"AddArgument(\"30001\")",
setup,
StringComparison.Ordinal);
Assert.Contains(
"AddArgument(\"-PinRegisteredK3D\")",
setup,
StringComparison.Ordinal);
Assert.Contains(
"DrainOutputAsync(process.StandardOutput)",
setup,
StringComparison.Ordinal);
Assert.Contains(
"DrainOutputAsync(process.StandardError)",
setup,
StringComparison.Ordinal);
Assert.Contains(
"loaded.Settings.SceneDirectory",
setup,
StringComparison.Ordinal);
Assert.Contains(
"loaded.Settings.ResourceDirectory",
setup,
StringComparison.Ordinal);
Assert.Contains(
@@ -146,18 +198,21 @@ public sealed class LegacyFirstRunSetupNativeContractTests
setup,
StringComparison.Ordinal);
Assert.Contains(
"File.Move(temporary, destination, overwrite: true);",
"\"k3d-pins.local.json\"",
setup,
StringComparison.Ordinal);
Assert.DoesNotContain("ProcessStartInfo", setup, StringComparison.Ordinal);
Assert.DoesNotContain("Process.Start", setup, StringComparison.Ordinal);
Assert.DoesNotContain("RunSetupAsync", setup, StringComparison.Ordinal);
Assert.DoesNotContain("powershell.exe", setup, StringComparison.OrdinalIgnoreCase);
Assert.DoesNotContain("dotnet", setup, StringComparison.OrdinalIgnoreCase);
Assert.DoesNotContain("ConfigureDevelopmentLive", setup,
Assert.Contains(
"\"playout.local.json\"",
setup,
StringComparison.Ordinal);
Assert.Contains(
"\"playout.development-live.local.json\"",
setup,
StringComparison.Ordinal);
Assert.DoesNotContain(
"Environment.SetEnvironmentVariable",
setup,
StringComparison.Ordinal);
Assert.DoesNotContain("PinRegisteredK3D", setup, StringComparison.Ordinal);
Assert.DoesNotContain("DatabaseIniPath", setup, StringComparison.Ordinal);
}
[Fact]
@@ -180,6 +235,19 @@ public sealed class LegacyFirstRunSetupNativeContractTests
StringComparison.Ordinal);
}
[Fact]
public void SolutionDeclaresTheVisualStudioWinUiWorkload()
{
var configuration = File.ReadAllText(Path.Combine(
RepositoryRoot,
".vsconfig"));
Assert.Contains(
"\"Microsoft.VisualStudio.Workload.Universal\"",
configuration,
StringComparison.Ordinal);
}
[Fact]
public void AutomaticK3dPinningIsPersistentAndCannotApproveAChangedDll()
{
@@ -277,11 +345,4 @@ public sealed class LegacyFirstRunSetupNativeContractTests
throw new DirectoryNotFoundException("Repository root was not found.");
}
private static int RequiredIndex(string value, string marker)
{
var index = value.IndexOf(marker, StringComparison.Ordinal);
Assert.True(index >= 0, $"Required marker was not found: {marker}");
return index;
}
}

View File

@@ -97,6 +97,11 @@ public sealed class LegacyOperatorSettingsNativeContractTests
Assert.Contains("_appliedOperatorSettings = operatorSettingsLoad.Settings", Window,
StringComparison.Ordinal);
Assert.Contains("ResolveFromResourceDirectory(", Window, StringComparison.Ordinal);
Assert.Contains(
"DatabaseRuntime.CreateDevelopmentLiveLocalOverride(\n" +
" selectedResourceDirectory)",
Window.ReplaceLineEndings("\n"),
StringComparison.Ordinal);
Assert.Contains("preferLegacyOverride: selectedLegacyDatabaseIni is not null", Window,
StringComparison.Ordinal);
Assert.Contains(
@@ -128,13 +133,18 @@ public sealed class LegacyOperatorSettingsNativeContractTests
databaseComposition,
"DatabaseRuntime.CreateDefault()",
gateA);
var selectedDevelopmentLiveIni = RequiredIndex(
databaseComposition,
"DatabaseRuntime.CreateDevelopmentLiveLocalOverride(",
pinnedJson);
var legacyIni = RequiredIndex(
databaseComposition,
"DatabaseRuntime.CreateLegacyExecutableDefault(",
pinnedJson);
selectedDevelopmentLiveIni);
Assert.True(gateA < pinnedJson);
Assert.True(pinnedJson < legacyIni);
Assert.True(pinnedJson < selectedDevelopmentLiveIni);
Assert.True(selectedDevelopmentLiveIni < legacyIni);
}
[Fact]

View File

@@ -373,7 +373,11 @@ public sealed class LegacyRuntimeAssetDeploymentContractTests
"External legacy runtime paths are enabled",
StringComparison.Ordinal));
Assert.Contains(
"Cuts files remain in the selected folder",
@"Cuts and the credential-bearing Res\MmoneyCoder.ini remain",
fullRuntimeMessage.Attribute("Text")?.Value,
StringComparison.Ordinal);
Assert.Contains(
"in the selected external folders",
fullRuntimeMessage.Attribute("Text")?.Value,
StringComparison.Ordinal);
@@ -654,6 +658,10 @@ public sealed class LegacyRuntimeAssetDeploymentContractTests
RepositoryRoot,
"scripts",
"Initialize-ExistingDevelopmentPc.ps1"));
var registrationInspector = File.ReadAllText(Path.Combine(
RepositoryRoot,
"scripts",
"Inspect-K3DRegistration.ps1"));
Assert.Contains("[string] $LegacyRuntimeSourceRoot", initializer,
StringComparison.Ordinal);
@@ -784,6 +792,34 @@ public sealed class LegacyRuntimeAssetDeploymentContractTests
"[string] $classReport.ThreadingModel -cne 'Apartment'",
initializer,
StringComparison.Ordinal);
Assert.Contains(
"$standardK3dRoot = 'C:\\K3DAsyncEngine'",
initializer,
StringComparison.Ordinal);
Assert.Contains(
"DLL\\x64\\Release\\K3DAsyncEngine.dll",
initializer,
StringComparison.Ordinal);
Assert.Contains(
"Bin\\x64\\C#\\Interop.K3DAsyncEngineLib.dll",
initializer,
StringComparison.Ordinal);
Assert.Contains(
"-Right $standardK3dNativePath",
initializer,
StringComparison.Ordinal);
Assert.Contains(
"$expectedVendorRoot = 'C:\\K3DAsyncEngine'",
registrationInspector,
StringComparison.Ordinal);
Assert.Contains(
"-Expected $expectedNativePath",
registrationInspector,
StringComparison.Ordinal);
Assert.Contains(
"Assert-Amd64File -Path $interopPath",
registrationInspector,
StringComparison.Ordinal);
Assert.DoesNotContain("Activator.CreateInstance", initializer,
StringComparison.Ordinal);
Assert.DoesNotContain("Connect(", initializer, StringComparison.Ordinal);

View File

@@ -78,22 +78,22 @@ public sealed class InstalledK3dInteropMetadataTests
}
[Fact]
public void RegisteredPaths_MapOnlyExactX64VendorLayout()
public void RegisteredPaths_MapOnlyCompanyStandardInstallation()
{
const string native = @"C:\Vendor\K3DAsyncEngine\DLL\x64\Release\K3DAsyncEngine.dll";
const string native = K3dComConstants.StandardNativeBinaryPath;
var paths = InstalledK3dInteropMetadata.ResolveRegisteredPaths(ReadySnapshot(native));
Assert.Equal(Path.GetFullPath(native), paths.NativeBinaryPath);
Assert.Equal(
Path.GetFullPath(
@"C:\Vendor\K3DAsyncEngine\Bin\x64\C#\Interop.K3DAsyncEngineLib.dll"),
Path.GetFullPath(K3dComConstants.StandardInteropAssemblyPath),
paths.InteropAssemblyPath);
Assert.Equal(@"C:\K3DAsyncEngine", K3dComConstants.StandardInstallationRoot);
}
[Fact]
public void RegisteredPaths_RejectDivergentClassBinary()
{
const string native = @"C:\Vendor\K3DAsyncEngine\DLL\x64\Release\K3DAsyncEngine.dll";
const string native = K3dComConstants.StandardNativeBinaryPath;
var snapshot = ReadySnapshot(native) with
{
EventHandler = ReadySnapshot(native).EventHandler with
@@ -116,6 +116,16 @@ public sealed class InstalledK3dInteropMetadataTests
() => InstalledK3dInteropMetadata.ResolveRegisteredPaths(ReadySnapshot(native)));
}
[Fact]
public void RegisteredPaths_RejectAlternateRootWithMatchingX64Layout()
{
const string native =
@"D:\K3DAsyncEngine\DLL\x64\Release\K3DAsyncEngine.dll";
Assert.Throws<InstalledK3dInteropMetadataUnavailableException>(
() => InstalledK3dInteropMetadata.ResolveRegisteredPaths(ReadySnapshot(native)));
}
private static K3dRegistrySnapshot ReadySnapshot(string nativePath) => new(
true,
nativePath,

View File

@@ -5,9 +5,9 @@ namespace MBN_STOCK_WEBVIEW.Playout.Tests;
public sealed class K3dRegistrationProbeTests
{
private const string TypeLibraryPath = "C:\\vendor-secret\\typelib-x64.dll";
private const string EnginePath = "C:\\vendor-secret\\engine-x64.dll";
private const string EventHandlerPath = "C:\\vendor-secret\\events-x64.dll";
private const string TypeLibraryPath = K3dComConstants.StandardNativeBinaryPath;
private const string EnginePath = K3dComConstants.StandardNativeBinaryPath;
private const string EventHandlerPath = K3dComConstants.StandardNativeBinaryPath;
[Fact]
public void Probe_WithReciprocalAmd64MachineRegistration_IsReady()
@@ -72,6 +72,32 @@ public sealed class K3dRegistrationProbeTests
AssertSafe(report);
}
[Fact]
public void Probe_WhenRegistryUsesAlternateRoot_FailsClosed()
{
const string alternate =
@"D:\K3DAsyncEngine\DLL\x64\Release\K3DAsyncEngine.dll";
var snapshot = ReadySnapshot() with
{
Win64TypeLibraryPath = alternate,
Engine = ReadySnapshot().Engine with { InprocServerPath = alternate },
EventHandler = ReadySnapshot().EventHandler with { InprocServerPath = alternate }
};
var binaries = new FakeBinaryInspector()
.Set(alternate, exists: true, isAmd64: true);
var probe = CreateProbe(snapshot, binaries);
var report = probe.Probe();
Assert.False(report.IsReady);
Assert.True(report.Issues.HasFlag(K3dRegistrationIssue.TypeLibraryPathMismatch));
Assert.True(report.Issues.HasFlag(K3dRegistrationIssue.EngineServerPathMismatch));
Assert.True(report.Issues.HasFlag(K3dRegistrationIssue.EventHandlerServerPathMismatch));
Assert.False(report.IsEngineClassReady);
Assert.False(report.IsEventHandlerClassReady);
AssertSafe(report);
}
[Fact]
public void Probe_WhenCurrentUserOverrideExists_FailsClosedWithoutDetails()
{