Files
MBN_STOCK_WEBVIEW/tests/MBN_STOCK_WEBVIEW.LegacyWeb.Tests/LegacyDatabaseHealthMonitorNativeContractTests.cs

167 lines
6.6 KiB
C#

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.");
}
}