using System.Xml.Linq; namespace MBN_STOCK_WEBVIEW.LegacyWeb.Tests; public sealed class LegacyFirstRunSetupNativeContractTests { private static readonly string RepositoryRoot = FindRepositoryRoot(); private static readonly string AppRoot = Path.Combine( RepositoryRoot, "src", "MBN_STOCK_WEBVIEW.LegacyParityApp"); [Fact] public void SourceOnlyLaunchShowsOnlyTheNativeFirstRunWindow() { var app = File.ReadAllText(Path.Combine(AppRoot, "App.xaml.cs")) .ReplaceLineEndings("\n"); var sourceOnlyStart = app.IndexOf("#if SOURCE_ONLY_RUNTIME", StringComparison.Ordinal); var fullRuntimeStart = app.IndexOf("#else", sourceOnlyStart, StringComparison.Ordinal); var runtimeEnd = app.IndexOf("#endif", fullRuntimeStart, StringComparison.Ordinal); Assert.True(sourceOnlyStart >= 0); Assert.True(fullRuntimeStart > sourceOnlyStart); Assert.True(runtimeEnd > fullRuntimeStart); var sourceOnly = app[sourceOnlyStart..fullRuntimeStart]; var fullRuntime = app[fullRuntimeStart..runtimeEnd]; Assert.Contains("new FirstRunSetupWindow()", sourceOnly, StringComparison.Ordinal); Assert.DoesNotContain("new MainWindow()", sourceOnly, StringComparison.Ordinal); Assert.DoesNotContain( "DevelopmentLiveLaunchBootstrap.TryApply(", sourceOnly, StringComparison.Ordinal); Assert.Contains("DevelopmentLiveLaunchBootstrap.TryApply(", fullRuntime, StringComparison.Ordinal); Assert.Contains("new MainWindow()", fullRuntime, StringComparison.Ordinal); Assert.DoesNotContain("new FirstRunSetupWindow()", fullRuntime, StringComparison.Ordinal); Assert.DoesNotContain( "new FirstRunSetupWindow(App.IsDevelopmentLiveLaunch", app, StringComparison.Ordinal); } [Fact] public void FirstRunUiRequestsOnlyCutsAndTheWholeResFolder() { var xaml = XDocument.Load(Path.Combine( AppRoot, "FirstRunSetupWindow.xaml")); XNamespace presentation = "http://schemas.microsoft.com/winfx/2006/xaml/presentation"; var buttons = xaml.Descendants(presentation + "Button") .Select(element => element.Attribute("Content")?.Value) .Where(value => value is not null) .ToArray(); var source = File.ReadAllText(Path.Combine( AppRoot, "FirstRunSetupWindow.xaml")); Assert.Contains("Cuts 폴더 선택", buttons); Assert.Contains("Res 폴더 선택", buttons); Assert.DoesNotContain("INI 파일 선택", source, StringComparison.Ordinal); Assert.DoesNotContain("비밀번호", source, StringComparison.Ordinal); Assert.DoesNotContain("포트 입력", source, StringComparison.Ordinal); Assert.DoesNotContain("해시 입력", source, StringComparison.Ordinal); } [Fact] public void BothFolderSelectionsValidateOnlyCanonicalPaths() { var setup = File.ReadAllText(Path.Combine( AppRoot, "FirstRunSetupWindow.xaml.cs")); Assert.Contains( "LegacyOperatorSettingsFolderKind.Scene", setup, StringComparison.Ordinal); Assert.Contains( "LegacyOperatorSettingsFolderKind.Resource", setup, StringComparison.Ordinal); Assert.DoesNotContain("MmoneyCoder.ini", setup, StringComparison.Ordinal); Assert.DoesNotContain("LegacyIniDatabaseOptionsLoader", setup, StringComparison.Ordinal); var clearCuts = setup.IndexOf( "_cutsDirectory = null;", StringComparison.Ordinal); var validateCuts = setup.IndexOf( "LegacyOperatorSettingsFolderKind.Scene", StringComparison.Ordinal); var clearResource = setup.IndexOf( "_resourceDirectory = null;", StringComparison.Ordinal); var validateResource = setup.IndexOf( "LegacyOperatorSettingsFolderKind.Resource", StringComparison.Ordinal); Assert.True(clearCuts >= 0); Assert.True(validateCuts > clearCuts); Assert.True(clearResource > validateCuts); Assert.True(validateResource > clearResource); Assert.Contains( "Path.GetFileName(cuts),\n" + " \"Cuts\"", setup.ReplaceLineEndings("\n"), StringComparison.Ordinal); Assert.Contains( "Path.GetFileName(resource),\n" + " \"Res\"", setup.ReplaceLineEndings("\n"), StringComparison.Ordinal); } [Fact] public void SetupPersistsOnlyTheSelectedPathsWithoutStartingAChildProcess() { 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();", setup, StringComparison.Ordinal); Assert.Contains( "SceneDirectory = cutsDirectory", setup, StringComparison.Ordinal); Assert.Contains( "ResourceDirectory = resourceDirectory", setup, StringComparison.Ordinal); Assert.Contains( "Directory.Build.local.props", setup, StringComparison.Ordinal); Assert.Contains( "File.Move(temporary, destination, overwrite: true);", 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, StringComparison.Ordinal); Assert.DoesNotContain("PinRegisteredK3D", setup, StringComparison.Ordinal); Assert.DoesNotContain("DatabaseIniPath", setup, StringComparison.Ordinal); } [Fact] public void SourceOnlyBuildEmbedsOnlyItsOwnClonePathForTheLocalBinding() { var project = XDocument.Load(Path.Combine( AppRoot, "MBN_STOCK_WEBVIEW.LegacyParityApp.csproj")); var metadata = Assert.Single(project.Descendants("AssemblyMetadata")); Assert.Equal("SourceRepositoryRoot", metadata.Attribute("Include")?.Value); Assert.Equal("$(SourceRepositoryRoot)", metadata.Attribute("Value")?.Value); Assert.Contains( "'$(LegacyRuntimeAssetsEnabled)' != 'true'", metadata.Attribute("Condition")?.Value, StringComparison.Ordinal); Assert.Contains( "'$(Configuration)' == 'Debug'", metadata.Attribute("Condition")?.Value, StringComparison.Ordinal); } [Fact] public void AutomaticK3dPinningIsPersistentAndCannotApproveAChangedDll() { var initializer = File.ReadAllText(Path.Combine( RepositoryRoot, "scripts", "Initialize-ExistingDevelopmentPc.ps1")); Assert.Contains("[switch] $PinRegisteredK3D", initializer, StringComparison.Ordinal); Assert.Contains("[switch] $ReplaceK3DPin", initializer, StringComparison.Ordinal); Assert.Contains( "Join-Path $ConfigurationRoot 'k3d-pins.local.json'", initializer, StringComparison.Ordinal); Assert.Contains( "$approvedNativeSha256 = $actualNativeHash", initializer, StringComparison.Ordinal); Assert.Contains( "$approvedInteropSha256 = $actualInteropHash", initializer, StringComparison.Ordinal); Assert.Contains( "-PinRegisteredK3D cannot be combined with independently", initializer, StringComparison.Ordinal); Assert.Contains( "Automatic pinning cannot approve a changed DLL.", initializer, StringComparison.Ordinal); Assert.Contains( "-ReplaceK3DPin cannot be combined with -PinRegisteredK3D.", initializer, StringComparison.Ordinal); Assert.Contains( "A replacement requires independently supplied native and interop hashes.", initializer, StringComparison.Ordinal); Assert.Contains( "Write-ProtectedUtf8TextAtomically", initializer, StringComparison.Ordinal); Assert.Contains( "This is a durable trust anchor, not transactional runtime state.", initializer, StringComparison.Ordinal); Assert.DoesNotContain( "K3D persistent pin rollback failed:", initializer, StringComparison.Ordinal); var pinPreflight = initializer.LastIndexOf( "$k3dPinPreflight = Get-K3DPinPreflight", StringComparison.Ordinal); var pinWrite = initializer.IndexOf( "Write-ProtectedUtf8TextAtomically", pinPreflight, StringComparison.Ordinal); var livePreflight = initializer.IndexOf( "$livePreflight = Get-DevelopmentLivePreflight", pinWrite, StringComparison.Ordinal); Assert.True(pinPreflight >= 0); Assert.True(pinWrite > pinPreflight); Assert.True(livePreflight > pinWrite); Assert.Contains( "nativeSha256 = $ApprovedNativeHash.ToUpperInvariant()", initializer, StringComparison.Ordinal); Assert.Contains( "interopSha256 = $ApprovedInteropHash.ToUpperInvariant()", initializer, StringComparison.Ordinal); Assert.Contains( "Get-VerifiedK3DRegistration -Reports $registrationReports", initializer, StringComparison.Ordinal); Assert.DoesNotContain("Activator.CreateInstance", initializer, StringComparison.Ordinal); } private static string FindRepositoryRoot() { var current = new DirectoryInfo(AppContext.BaseDirectory); while (current is not null) { if (File.Exists(Path.Combine(current.FullName, "MBN_STOCK_WEBVIEW.sln"))) { return current.FullName; } current = current.Parent; } 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; } }