feat: consolidate legacy parity migration baseline

This commit is contained in:
2026-07-20 22:06:12 +09:00
parent 5eb4054120
commit 4a54977341
66 changed files with 5391 additions and 460 deletions

View File

@@ -0,0 +1,166 @@
namespace MBN_STOCK_WEBVIEW.LegacyWeb.Tests;
public sealed class LegacyDatabaseHealthMonitorNativeContractTests
{
private static readonly string RepositoryRoot = FindRepositoryRoot();
private static readonly string AppRoot = Path.Combine(
RepositoryRoot,
"src",
"MBN_STOCK_WEBVIEW.LegacyParityApp");
private static readonly string Window = File.ReadAllText(
Path.Combine(AppRoot, "MainWindow.xaml.cs"));
private static readonly string Monitor = File.ReadAllText(
Path.Combine(AppRoot, "MainWindow.DatabaseHealth.cs"));
private static readonly string Playout = File.ReadAllText(
Path.Combine(AppRoot, "MainWindow.Playout.cs"));
[Fact]
public void LegacyTenSecondMonitorStartsAndStopsWithTheActiveWindow()
{
Assert.Contains(
"TimeSpan.FromSeconds(10)",
Monitor,
StringComparison.Ordinal);
Assert.Contains(
"await Task.Delay(DatabaseHealthPollingInterval, cancellationToken)",
Monitor,
StringComparison.Ordinal);
Assert.Contains(
"StartDatabaseHealthMonitor();",
Window,
StringComparison.Ordinal);
Assert.Contains(
"_lifetimeCancellation.Cancel();\n CancelActiveDatabaseHealthCheck();",
Normalize(Window),
StringComparison.Ordinal);
}
[Fact]
public void MonitorUsesBothConfiguredProvidersAndLogsOnlyClosedTransitions()
{
Assert.Contains(
".HealthService\n .CheckAllAsync(healthCancellation.Token)",
Normalize(Monitor),
StringComparison.Ordinal);
Assert.Contains(
"foreach (var status in statuses)",
Monitor,
StringComparison.Ordinal);
Assert.Contains(
"ObserveDatabaseHealthState(status.Source, status.State);",
Monitor,
StringComparison.Ordinal);
Assert.Contains(
"_databaseHealthTransitions.TryObserveDatabaseState(",
Monitor,
StringComparison.Ordinal);
Assert.Contains(
"_databaseHealthLogWriter.TryWrite(record)",
Monitor,
StringComparison.Ordinal);
Assert.DoesNotContain("status.UserMessage", Monitor, StringComparison.Ordinal);
Assert.DoesNotContain("exception.Message", Monitor, StringComparison.Ordinal);
}
[Fact]
public void OperatorAndRefreshWorkCancelAndSerializeAgainstHealthProbes()
{
var dispatch = Slice(
Window,
"private async Task ProcessIntentAsync(",
"private static bool IsNamedPlaylistModalIntent(");
var databaseWait = RequiredIndex(
dispatch,
"await AcquirePriorityDatabaseActivityAsync(_lifetimeCancellation.Token)");
var handle = RequiredIndex(dispatch, "await HandleIntentAsync(");
Assert.True(databaseWait < handle);
Assert.Contains("_databaseActivityGate.Release();", dispatch,
StringComparison.Ordinal);
var refresh = Slice(
Playout,
"private async Task RunRefreshLoopAsync(",
"private bool TrySetRefreshFault(");
databaseWait = RequiredIndex(
refresh,
"await AcquirePriorityDatabaseActivityAsync(token)");
var refreshCall = RequiredIndex(refresh, "workflow.RefreshOnAirAsync");
Assert.True(databaseWait < refreshCall);
Assert.Contains("_databaseActivityGate.Release();", refresh,
StringComparison.Ordinal);
}
[Fact]
public void OperatorPriorityClosesTheHealthProbeCancellationPublishRace()
{
var acquire = Slice(
Monitor,
"private async Task AcquirePriorityDatabaseActivityAsync(",
"private void CancelActiveDatabaseHealthCheck()");
var waiterIncrement = RequiredIndex(
acquire,
"Interlocked.Increment(ref _databaseActivityPriorityWaiterCount);");
var cancel = RequiredIndex(acquire, "CancelActiveDatabaseHealthCheck();");
var gateWait = RequiredIndex(
acquire,
"await _databaseActivityGate.WaitAsync(cancellationToken)");
var waiterDecrement = RequiredIndex(
acquire,
"Interlocked.Decrement(ref _databaseActivityPriorityWaiterCount);");
Assert.True(waiterIncrement < cancel);
Assert.True(cancel < gateWait);
Assert.True(gateWait < waiterDecrement);
var probe = Slice(
Monitor,
"private async Task CheckAndLogDatabaseHealthAsync(",
"private async Task AcquirePriorityDatabaseActivityAsync(");
var preAcquirePriorityCheck = RequiredIndex(
probe,
"Volatile.Read(ref _databaseActivityPriorityWaiterCount) != 0");
var healthCancellationPublish = RequiredIndex(
probe,
"Interlocked.Exchange(\n ref _databaseHealthCheckCancellation,\n healthCancellation);");
var postPublishPriorityCheck = RequiredIndex(
probe,
"Volatile.Read(ref _databaseActivityPriorityWaiterCount) != 0",
healthCancellationPublish);
var providerProbe = RequiredIndex(probe, ".CheckAllAsync(healthCancellation.Token)");
Assert.True(preAcquirePriorityCheck < healthCancellationPublish);
Assert.True(healthCancellationPublish < postPublishPriorityCheck);
Assert.True(postPublishPriorityCheck < providerProbe);
}
private static string Normalize(string value) =>
value.Replace("\r\n", "\n", StringComparison.Ordinal);
private static string Slice(string value, string start, string end)
{
var startIndex = RequiredIndex(value, start);
var endIndex = RequiredIndex(value, end, startIndex + start.Length);
return value[startIndex..endIndex];
}
private static int RequiredIndex(string value, string needle, int startIndex = 0)
{
var index = value.IndexOf(needle, startIndex, StringComparison.Ordinal);
Assert.True(index >= 0, $"Required marker was not found: {needle}");
return index;
}
private static string FindRepositoryRoot()
{
var directory = new DirectoryInfo(AppContext.BaseDirectory);
while (directory is not null)
{
if (File.Exists(Path.Combine(directory.FullName, "MBN_STOCK_WEBVIEW.sln")))
{
return directory.FullName;
}
directory = directory.Parent;
}
throw new DirectoryNotFoundException("Repository root was not found.");
}
}

View File

@@ -86,6 +86,21 @@ public sealed class LegacyManualFinancialParityWebTests
Assert.Contains("generation: generation", App, StringComparison.Ordinal);
}
[Fact]
public void GraphE_two_row_toolbar_keeps_find_controls_pointer_accessible()
{
Assert.Contains(
".manual-financial-workspace { min-height: 0; display: grid; " +
"grid-template-rows: auto minmax(0, 1fr);",
Styles,
StringComparison.Ordinal);
Assert.DoesNotContain(
".manual-financial-workspace { min-height: 0; display: grid; " +
"grid-template-rows: 38px minmax(0, 1fr);",
Styles,
StringComparison.Ordinal);
}
[Fact]
public void Manual_Web_section_sends_values_and_opaque_ids_without_DB_or_scene_authority()
{
@@ -113,7 +128,9 @@ public sealed class LegacyManualFinancialParityWebTests
Assert.Contains("LegacyDeleteManualFinancialIntent", MainWindow, StringComparison.Ordinal);
Assert.Contains("LegacyDeleteAllManualFinancialIntent", MainWindow, StringComparison.Ordinal);
Assert.Contains("_intentGate.WaitAsync(", MainWindow, StringComparison.Ordinal);
Assert.Contains("0,\n _lifetimeCancellation.Token", MainWindow, StringComparison.Ordinal);
Assert.Matches(
new Regex(@"_intentGate\.WaitAsync\(\s*0,\s*_lifetimeCancellation\.Token\)"),
MainWindow);
Assert.DoesNotContain("SaveManualFinancialAsync(", MainWindow[..MainWindow.IndexOf(
"HandleIntentAsync",
StringComparison.Ordinal)], StringComparison.Ordinal);

View File

@@ -72,6 +72,49 @@ public sealed class LegacyPlayoutNativeContractTests
Assert.Contains("WaitForPlayCompletionAsync", Playout, StringComparison.Ordinal);
}
[Fact]
public void PlayoutIntentsWaitBehindEarlierUiWorkWithoutQueuingDuplicates()
{
var process = Normalize(Slice(
Window,
"private async Task ProcessIntentAsync(LegacyUiIntent intent)",
"private static bool IsOrderedPlayoutIntent"));
var classifier = Slice(
Window,
"private static bool IsOrderedPlayoutIntent",
"private static bool IsNamedPlaylistModalIntent");
var admission = RequiredIndex(
process,
"_orderedPlayoutIntentGate.WaitAsync(");
var orderedWait = RequiredIndex(
process,
"await _intentGate.WaitAsync(_lifetimeCancellation.Token)",
admission);
var zeroTimeoutWait = RequiredIndex(
process,
"gateEntered = await _intentGate.WaitAsync(\n 0,",
orderedWait);
var intentRelease = RequiredIndex(process, "_intentGate.Release();", zeroTimeoutWait);
var admissionRelease = RequiredIndex(
process,
"_orderedPlayoutIntentGate.Release();",
intentRelease);
Assert.True(admission < orderedWait);
Assert.True(orderedWait < zeroTimeoutWait);
Assert.True(zeroTimeoutWait < intentRelease);
Assert.True(intentRelease < admissionRelease);
Assert.Contains("if (orderedPlayoutIntentEntered)", process,
StringComparison.Ordinal);
Assert.Contains("LegacyExecutePlayoutIntent", classifier,
StringComparison.Ordinal);
Assert.Contains("LegacyGateAPrepareIntent", classifier,
StringComparison.Ordinal);
Assert.Contains("_lifetimeCancellation.Token", process,
StringComparison.Ordinal);
}
[Fact]
public void AutomaticRefreshUsesTheCoreSchedulerAndGenerationOwnedState()
{
@@ -291,7 +334,7 @@ public sealed class LegacyPlayoutNativeContractTests
}
[Fact]
public void PreparedProgramAndUnknownWriteOutcomesBlockOperatorMutations()
public void PreparedProgramBlocksCutAppendButAllowsPureCutSelection()
{
Assert.Contains("CanAcceptOperatorMutation()", Window, StringComparison.Ordinal);
Assert.Contains("IsOperatorMutationQuarantined()", Window,
@@ -308,9 +351,17 @@ public sealed class LegacyPlayoutNativeContractTests
StringComparison.Ordinal);
Assert.Contains("LegacyConfirmManualListIntent", Playout,
StringComparison.Ordinal);
Assert.Contains("LegacyCutPointerDownIntent", Playout,
var mutationClassifier = Slice(
Playout,
"private static bool IsOperatorMutationIntent",
"private static bool IsFixedSectionBatchMutationIntent");
Assert.Contains("LegacyCutPointerDownIntent { AllowAppend: true }", mutationClassifier,
StringComparison.Ordinal);
Assert.Contains("LegacyClearCutSelectionIntent", Playout,
Assert.DoesNotContain("LegacyCutKeyDownIntent", mutationClassifier,
StringComparison.Ordinal);
Assert.DoesNotContain("LegacyClearCutSelectionIntent", mutationClassifier,
StringComparison.Ordinal);
Assert.Contains("pointer.AllowAppend", Window,
StringComparison.Ordinal);
}

View File

@@ -33,7 +33,7 @@ public sealed class LegacyRowDoubleClickParityTests
}
[Fact]
public void Theme_double_click_is_atomic_while_single_click_selects_immediately()
public void Theme_double_click_is_atomic_and_cancels_the_delayed_single_click()
{
var doubleClick = Extract(
"categoryTree.addEventListener(\"dblclick\"",
@@ -55,12 +55,17 @@ public sealed class LegacyRowDoubleClickParityTests
StringComparison.Ordinal);
Assert.DoesNotContain("send(\"select-theme\"", doubleClick,
StringComparison.Ordinal);
Assert.Contains("clearDelayedClick(themeResultClickTimer)", doubleClick,
StringComparison.Ordinal);
Assert.Contains(
"send(\"select-theme\", { resultId: themeResult.dataset.themeResultId });",
"send(\"select-theme\", { resultId: resultId });",
themeClick,
StringComparison.Ordinal);
Assert.DoesNotContain("window.setTimeout", themeClick, StringComparison.Ordinal);
Assert.DoesNotContain("themeResultClickTimer", App, StringComparison.Ordinal);
Assert.Contains("clearDelayedClick(themeResultClickTimer)", themeClick,
StringComparison.Ordinal);
Assert.Contains("themeResultClickTimer = window.setTimeout", themeClick,
StringComparison.Ordinal);
Assert.Contains("}, 250);", themeClick, StringComparison.Ordinal);
}
[Fact]

View File

@@ -28,6 +28,7 @@ public sealed class LegacyWebContractTests
"moving-average-5",
"moving-average-20",
"cut-list",
"cut-drag-feedback",
"playlist-drop-zone",
"playlist-rows",
"playlist-move-up",
@@ -77,7 +78,7 @@ public sealed class LegacyWebContractTests
Assert.Contains("timestampMilliseconds", App, StringComparison.Ordinal);
Assert.Contains("event.ctrlKey", App, StringComparison.Ordinal);
Assert.Contains("event.shiftKey", App, StringComparison.Ordinal);
Assert.Contains("if (playlistMutationLocked) return", App,
Assert.Contains("allowAppend: !playlistMutationLocked", App,
StringComparison.Ordinal);
Assert.Contains("send(\"clear-cut-selection\", {})", App,
StringComparison.Ordinal);
@@ -432,7 +433,7 @@ public sealed class LegacyWebContractTests
public void SeparatorRowsPreserveOriginalDragGesture()
{
Assert.Contains(
"element.dataset.cutDragEnabled = cutSelectionEnabled ? \"true\" : \"false\"",
"element.dataset.cutDragEnabled = cutSelectionEnabled && !cutDragLocked",
App,
StringComparison.Ordinal);
Assert.Contains("element.draggable = false", App, StringComparison.Ordinal);
@@ -440,6 +441,22 @@ public sealed class LegacyWebContractTests
StringComparison.Ordinal);
}
[Fact]
public void CutDragFeedbackFollowsTheCapturedPointerWithoutOwningInput()
{
Assert.Contains("id=\"cut-drag-feedback\"", Markup, StringComparison.Ordinal);
Assert.Contains("aria-hidden=\"true\"", Markup, StringComparison.Ordinal);
Assert.Contains("function showCutDragFeedback", App, StringComparison.Ordinal);
Assert.Contains("cutDragFeedback.dataset.dropAllowed", App,
StringComparison.Ordinal);
Assert.Contains("showCutDragFeedback(event, dropAllowed)", App,
StringComparison.Ordinal);
Assert.Contains("cutDragFeedback.hidden = true", App,
StringComparison.Ordinal);
Assert.Contains(".cut-drag-feedback", Styles, StringComparison.Ordinal);
Assert.Contains("pointer-events: none", Styles, StringComparison.Ordinal);
}
[Fact]
public void BusySnapshotFreezesControlsWithoutQueueingInput()
{