namespace MBN_STOCK_WEBVIEW.LegacyWeb.Tests; public sealed class LegacyFixedSectionBatchWebTests { private static readonly string RepositoryRoot = FindRepositoryRoot(); private static readonly string App = File.ReadAllText(Path.Combine( RepositoryRoot, "src", "MBN_STOCK_WEBVIEW.LegacyParityApp", "Web", "app.js")); private static readonly string MainWindow = File.ReadAllText(Path.Combine( RepositoryRoot, "src", "MBN_STOCK_WEBVIEW.LegacyParityApp", "MainWindow.xaml.cs")); private static readonly string Playout = File.ReadAllText(Path.Combine( RepositoryRoot, "src", "MBN_STOCK_WEBVIEW.LegacyParityApp", "MainWindow.Playout.cs")); [Fact] public void Fixed_section_progress_is_native_state_and_locks_other_catalog_actions() { var fixedCatalog = Extract("function renderFixedCatalog", "function renderIndustry"); Assert.Contains("const batch = state.fixedSectionBatch", fixedCatalog, StringComparison.Ordinal); Assert.Contains("button.disabled = state.isBusy === true || batch != null", fixedCatalog, StringComparison.Ordinal); Assert.Contains("summary.setAttribute(\"aria-disabled\"", fixedCatalog, StringComparison.Ordinal); Assert.Contains("section.getAttribute(\"aria-disabled\") !== \"true\"", App, StringComparison.Ordinal); } [Fact] public void Manual_modal_explains_atomic_staging_and_exposes_only_confirm_next_or_cancel() { var manual = Extract("function renderManualLists", "function openNamedPlaylist"); Assert.Contains("const fixedBatch = state.fixedSectionBatch", manual, StringComparison.Ordinal); Assert.Contains("아직 편성표에 추가되지 않았습니다", manual, StringComparison.Ordinal); Assert.Contains("button.disabled = fixedBatch != null", manual, StringComparison.Ordinal); Assert.Contains("setTransientControlsBusy(manualListDialog, state.isBusy === true)", manual, StringComparison.Ordinal); Assert.Contains("확인 후 다음", manual, StringComparison.Ordinal); Assert.Contains("확인 후 섹션 추가", manual, StringComparison.Ordinal); Assert.Contains("send(\"confirm-manual-list\", {})", manual, StringComparison.Ordinal); Assert.DoesNotContain("resume-fixed-section", App, StringComparison.Ordinal); } [Fact] public void Native_dispatch_awaits_the_whole_fixed_section_workflow() { Assert.Contains("await _controller.ActivateFixedSection(", MainWindow, StringComparison.Ordinal); Assert.Contains("section.SectionIndex", MainWindow, StringComparison.Ordinal); Assert.Contains("cancellationToken", MainWindow, StringComparison.Ordinal); } [Fact] public void Native_boundary_allows_only_current_manual_edits_until_batch_completion() { var allowlist = Extract( Playout, "private static bool IsFixedSectionBatchMutationIntent", "private void ObserveOperatorMutationQuarantine"); Assert.Contains("_controller.Current.FixedSectionBatch is not null", MainWindow, StringComparison.Ordinal); Assert.Contains("!IsFixedSectionBatchMutationIntent(intent)", MainWindow, StringComparison.Ordinal); Assert.Contains("LegacySetManualNetSellCellIntent", allowlist, StringComparison.Ordinal); Assert.Contains("LegacyConfirmManualListIntent", allowlist, StringComparison.Ordinal); Assert.DoesNotContain("LegacyDropSelectedCutsIntent", allowlist, StringComparison.Ordinal); } private static string Extract(string startToken, string endToken) { return Extract(App, startToken, endToken); } private static string Extract(string source, string startToken, string endToken) { var start = source.IndexOf(startToken, StringComparison.Ordinal); var end = source.IndexOf(endToken, start + startToken.Length, StringComparison.Ordinal); Assert.True(start >= 0 && end > start); return source[start..end]; } 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."); } }