using System.Text.Json; namespace MBN_STOCK_WEBVIEW.LegacyWeb.Tests; public sealed class LegacyDevelopmentLiveStartupContractTests { private const string ParityProjectGuid = "{0F72B3C8-8A81-4D1E-8E57-0C0E9A9960C1}"; private const string PrototypeProjectGuid = "{E6F5F1A0-D0FD-4E6B-A76D-1E8E3DAE8606}"; private const string ExactArgument = "--development-live"; private static readonly string RepositoryRoot = FindRepositoryRoot(); private static readonly string ParityRoot = Path.Combine( RepositoryRoot, "src", "MBN_STOCK_WEBVIEW.LegacyParityApp"); [Fact] public void VisualStudioProfilesPutDevelopmentLiveFirstAndKeepExplicitDryRun() { using var document = JsonDocument.Parse(File.ReadAllText(Path.Combine( ParityRoot, "Properties", "launchSettings.json"))); var profiles = document.RootElement.GetProperty("profiles") .EnumerateObject() .ToArray(); Assert.Equal(2, profiles.Length); Assert.Equal( "MBN_STOCK_WEBVIEW.LegacyParityApp - Development Live (Package)", profiles[0].Name); Assert.Equal("MsixPackage", profiles[0].Value.GetProperty("commandName").GetString()); Assert.Equal(ExactArgument, profiles[0].Value.GetProperty("commandLineArgs").GetString()); Assert.Equal( "MBN_STOCK_WEBVIEW.LegacyParityApp - Explicit DryRun (Package)", profiles[1].Name); Assert.Equal("MsixPackage", profiles[1].Value.GetProperty("commandName").GetString()); Assert.Equal(string.Empty, profiles[1].Value.GetProperty("commandLineArgs").GetString()); } [Fact] public void SharedSolutionLaunchStartsOnlyTheLegacyParityApp() { using var document = JsonDocument.Parse(File.ReadAllText(Path.Combine( RepositoryRoot, "MBN_STOCK_WEBVIEW.slnLaunch"))); var profile = Assert.Single(document.RootElement.EnumerateArray()); Assert.Equal("Legacy Parity App (VS F5)", profile.GetProperty("Name").GetString()); var project = Assert.Single(profile.GetProperty("Projects").EnumerateArray()); Assert.Equal( "src\\MBN_STOCK_WEBVIEW.LegacyParityApp\\MBN_STOCK_WEBVIEW.LegacyParityApp.csproj", project.GetProperty("Path").GetString()); Assert.Equal("Start", project.GetProperty("Action").GetString()); } [Fact] public void NormalDebugDeployTargetsParityInsteadOfThePrototype() { var solution = File.ReadAllText(Path.Combine( RepositoryRoot, "MBN_STOCK_WEBVIEW.sln")); Assert.Contains( $"{ParityProjectGuid}.Debug|x64.Deploy.0 = Debug|x64", solution, StringComparison.Ordinal); Assert.DoesNotContain( $"{PrototypeProjectGuid}.Debug|x64.Deploy.0 = Debug|x64", solution, StringComparison.Ordinal); } [Fact] public void AppAppliesCompileTimeBuildGateBeforeConstructingMainWindow() { var app = File.ReadAllText(Path.Combine(ParityRoot, "App.xaml.cs")); var bootstrap = app.IndexOf( "DevelopmentLiveLaunchBootstrap.TryApply(", StringComparison.Ordinal); var constructSetupWindow = app.IndexOf( "new FirstRunSetupWindow()", StringComparison.Ordinal); var constructMainWindow = app.IndexOf( "new MainWindow();", StringComparison.Ordinal); Assert.True(bootstrap >= 0); Assert.True(constructSetupWindow > bootstrap); Assert.True(constructMainWindow > constructSetupWindow); Assert.Contains("#if DEBUG", app, StringComparison.Ordinal); Assert.Contains("const bool isDebugBuild = true;", app, StringComparison.Ordinal); Assert.Contains("const bool isDebugBuild = false;", app, StringComparison.Ordinal); Assert.Contains("#if SOURCE_ONLY_RUNTIME", app, StringComparison.Ordinal); Assert.Contains("const bool isSourceOnlyBuild = true;", app, StringComparison.Ordinal); Assert.Contains("const bool isSourceOnlyBuild = false;", app, StringComparison.Ordinal); Assert.Contains("isSourceOnlyBuild", app, StringComparison.Ordinal); Assert.Contains("? null", app, StringComparison.Ordinal); Assert.Contains("args.Arguments", app, StringComparison.Ordinal); Assert.Contains("Environment.GetCommandLineArgs()", app, StringComparison.Ordinal); Assert.Contains("ResolveLaunchArguments(", app, StringComparison.Ordinal); Assert.Contains( "IsDevelopmentLiveLaunchRequested = string.Equals(", app, StringComparison.Ordinal); Assert.Contains( "IsDevelopmentLiveLaunch = developmentLive.IsApplied;", app, StringComparison.Ordinal); Assert.Contains( "_window = showFirstRunSetup", app, StringComparison.Ordinal); } [Fact] public void AppliedDevelopmentLiveUsesOnlyProtectedStartupInputs() { var window = File.ReadAllText(Path.Combine(ParityRoot, "MainWindow.xaml.cs")); var playout = File.ReadAllText(Path.Combine( ParityRoot, "MainWindow.Playout.cs")); Assert.Contains( "_isDevelopmentLiveLaunch = App.IsDevelopmentLiveLaunch;", window, StringComparison.Ordinal); Assert.Contains( "_isDevelopmentLiveLaunchRequested = App.IsDevelopmentLiveLaunchRequested;", window, StringComparison.Ordinal); Assert.Contains( "? LegacyRuntimeStockCutMenuLoader.ResolveFromBaseDirectory(", window, StringComparison.Ordinal); Assert.Contains( "? null\n : _appliedOperatorSettings.ResourceDirectory;", window.ReplaceLineEndings("\n"), StringComparison.Ordinal); Assert.Contains( "DatabaseRuntime.CreateDevelopmentLiveLocalOverride()", window, StringComparison.Ordinal); Assert.Contains( "? PlayoutOptionsLoader.LoadDevelopmentLive(", playout, StringComparison.Ordinal); Assert.Contains( "IsSourceOnlyBuild ||\n _isDevelopmentLiveLaunchRequested", playout.ReplaceLineEndings("\n"), StringComparison.Ordinal); } [Fact] public void DevelopmentLiveDatabaseFailureForcesDryRunAndSkipsAutomaticConnect() { var window = File.ReadAllText(Path.Combine(ParityRoot, "MainWindow.xaml.cs")) .ReplaceLineEndings("\n"); var playout = File.ReadAllText(Path.Combine( ParityRoot, "MainWindow.Playout.cs")) .ReplaceLineEndings("\n"); var blockAssignment = window.IndexOf( "_developmentLiveStartupBlocked = true;", StringComparison.Ordinal); var successAssignment = window.IndexOf( "databaseInitializationSucceeded = true;", StringComparison.Ordinal); var runtimeReset = window.IndexOf( "_databaseRuntime = null;", successAssignment, StringComparison.Ordinal); var pagePlanReset = window.IndexOf( "fixedPagePlanProvider = null;", successAssignment, StringComparison.Ordinal); var playoutInitialization = window.IndexOf( "InitializePlayoutRuntime();", StringComparison.Ordinal); Assert.True(successAssignment >= 0); Assert.True(runtimeReset > successAssignment); Assert.True(pagePlanReset > successAssignment); Assert.True(blockAssignment > runtimeReset); Assert.True(playoutInitialization > blockAssignment); Assert.Contains( "var databaseInitializationSucceeded = false;", window, StringComparison.Ordinal); Assert.Contains( "if (_isDevelopmentLiveLaunch && !databaseInitializationSucceeded)\n" + " {\n" + " // Development Live is one fail-closed startup transaction.", window, StringComparison.Ordinal); Assert.Contains( "_isDevelopmentLiveLaunch &&\n" + " !_developmentLiveStartupBlocked\n" + " ? PlayoutOptionsLoader.LoadDevelopmentLive(", playout, StringComparison.Ordinal); Assert.Contains( "_isDevelopmentLiveLaunchRequested ||\n" + " _developmentLiveStartupBlocked", playout, StringComparison.Ordinal); Assert.Contains( "if (!_developmentLiveStartupBlocked)\n" + " {\n" + " await ConnectPlayoutAsync(_lifetimeCancellation.Token);", window, StringComparison.Ordinal); } [Fact] public void TrackedExampleContainsOnlyNonUsableHashPlaceholders() { var example = File.ReadAllText(Path.Combine( RepositoryRoot, "Config", "playout.development-live.example.json")); using var document = JsonDocument.Parse(example); var root = document.RootElement; Assert.Equal(1, root.GetProperty("schemaVersion").GetInt32()); Assert.Equal("Live", root.GetProperty("mode").GetString()); Assert.StartsWith("<64-HEX-", root.GetProperty("nativeSha256").GetString()); Assert.StartsWith("<64-HEX-", root.GetProperty("interopSha256").GetString()); Assert.DoesNotContain(":\\", example, StringComparison.Ordinal); } [Fact] public void RealDevelopmentAuthorizationFileIsIgnoredEverywhere() { var ignore = File.ReadAllText(Path.Combine(RepositoryRoot, ".gitignore")); Assert.Contains("Config/playout.development-live.local.json", ignore, StringComparison.Ordinal); Assert.Contains("**/playout.development-live.local.json", ignore, 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."); } }