Files
MBN_STOCK_WEBVIEW/tests/Web/native-operator-log-integration.test.cjs

103 lines
5.7 KiB
JavaScript

"use strict";
const assert = require("node:assert/strict");
const fs = require("node:fs");
const path = require("node:path");
const test = require("node:test");
const root = path.resolve(__dirname, "../..");
const read = relativePath => fs.readFileSync(path.join(root, relativePath), "utf8");
const host = read("MainWindow.xaml.cs");
const playout = read("MainWindow.Playout.cs");
const integration = read("MainWindow.NativeOperatorLog.cs");
const writer = read("src/MBN_STOCK_WEBVIEW.Infrastructure/Diagnostics/NativeOperatorLogWriter.cs");
const transitions = read("src/MBN_STOCK_WEBVIEW.Infrastructure/Diagnostics/NativeOperatorLogTransitions.cs");
function sliceBetween(source, startMarker, endMarker) {
const start = source.indexOf(startMarker);
const end = source.indexOf(endMarker, start + startMarker.length);
assert.ok(start >= 0, `${startMarker} must exist`);
assert.ok(end > start, `${endMarker} must follow ${startMarker}`);
return source.slice(start, end);
}
test("lifecycle and provider transitions are wired with closed scalars", () => {
const constructor = sliceBetween(host, "public MainWindow()", "private void InitializeDatabaseRuntime(");
assert.match(constructor,
/InitializeComponent\(\);\s*LogNativeOperatorStartup\(\);\s*EnsureCloseConfirmationAttached\(\);/u);
const closed = sliceBetween(host, "private void OnClosed(", "private sealed record WebRequest(");
assert.match(closed, /LogNativeOperatorShutdown\(\);/u);
assert.match(integration,
/Interlocked\.Exchange\(ref _nativeStartupRecorded, 1\) == 0/u);
assert.match(integration,
/Interlocked\.Exchange\(ref _nativeShutdownRecorded, 1\) == 0/u);
const database = sliceBetween(
host,
"private async Task QueryAndPostDatabaseStatusAsync(",
"private void PostDatabaseStatus(");
assert.match(database,
/ObserveNativeDatabaseState\(DataSourceKind\.Oracle, DatabaseHealthState\.NotConfigured\)/u);
assert.match(database,
/ObserveNativeDatabaseState\(DataSourceKind\.MariaDb, DatabaseHealthState\.NotConfigured\)/u);
assert.match(database,
/foreach \(var status in statuses\)[\s\S]*ObserveNativeDatabaseState\(status\.Source, status\.State\)/u);
assert.match(database,
/ObserveNativeDatabaseState\(DataSourceKind\.Oracle, DatabaseHealthState\.Unhealthy\)/u);
assert.doesNotMatch(database, /ObserveNativeDatabaseState\([^\n]*(?:Message|Sql|Path|RequestId)/u);
assert.match(playout,
/_playoutEngine\.StatusChanged \+= OnPlayoutStatusChanged;\s*ObserveNativeTornadoState\(_playoutEngine\.Status\.State\);/u);
assert.match(playout,
/catch \(Exception exception\)\s*\{\s*ObserveNativeTornadoState\(PlayoutConnectionState\.Faulted\);/u);
assert.match(playout,
/private void OnPlayoutStatusChanged[\s\S]*?ObserveNativeTornadoState\(args\.Current\.State\);/u);
assert.match(transitions, /_oracleState != mapped/u);
assert.match(transitions, /_mariaDbState != mapped/u);
assert.match(transitions, /if \(_tornadoState == mapped\)/u);
});
test("only gate-entered commands receive one request and a closed terminal outcome", () => {
const commandHandler = sliceBetween(
playout,
"private async Task HandlePlayoutCommandAsync(",
"private async Task HandlePlayoutTimeoutQuarantineAsync(");
const gate = commandHandler.indexOf("_playoutCommandGate.WaitAsync(0)");
const requested = commandHandler.indexOf("LogNativePlayoutCommandRequested(request.Command)");
assert.ok(gate >= 0 && requested > gate, "request logging must happen only after gate entry");
assert.equal(commandHandler.indexOf("LogNativePlayoutCommandRequested(request.Command)", requested + 1), -1);
assert.match(commandHandler,
/var result = await ExecutePlayoutCommandAsync[\s\S]*LogNativePlayoutCommandCompleted\(request\.Command, result\.Code\);/u);
assert.match(commandHandler,
/catch \(OperationCanceledException\)[\s\S]*NativeOperatorPlayoutCompletion\.Cancelled/u);
assert.match(commandHandler,
/catch \(DatabaseOperationException exception\)[\s\S]*NativeOperatorPlayoutCompletion\.Unavailable/u);
assert.match(commandHandler,
/LegacySceneDataException or ArgumentException\)[\s\S]*NativeOperatorPlayoutCompletion\.Rejected/u);
assert.match(commandHandler, /catch\s*\{\s*LogNativePlayoutOutcomeUnknown\(request\.Command\);/u);
assert.match(playout,
/QuarantineForBrowserCorrelationLossAsync[\s\S]*LogCurrentNativePlayoutOutcomeUnknown\(\);/u);
assert.match(integration, /"prepare" => NativeOperatorPlayoutCommand\.Prepare/u);
assert.match(integration, /"take-in" => NativeOperatorPlayoutCommand\.TakeIn/u);
assert.match(integration, /"next" => NativeOperatorPlayoutCommand\.Next/u);
assert.match(integration, /"take-out" => NativeOperatorPlayoutCommand\.TakeOut/u);
for (const result of ["Success", "Rejected", "Cancelled", "Unavailable", "Failed"]) {
assert.match(integration, new RegExp(`PlayoutResultCode\\.${result} => NativeOperatorPlayoutCompletion\\.`));
}
assert.match(integration,
/result is PlayoutResultCode\.OutcomeUnknown or PlayoutResultCode\.TimedOut[\s\S]*TryMarkOutcomeUnknown/u);
assert.match(transitions, /if \(_currentCommand != command \|\| _terminalRecorded\)/u);
});
test("native boundary cannot receive or persist operational free-form data", () => {
assert.match(writer, /public readonly record struct NativeOperatorLogRecord/u);
assert.doesNotMatch(writer,
/public\s+(?:string|Exception|JsonElement|object|long|int)\??\s+\w+\s*\{\s*get;/u);
assert.doesNotMatch(integration, /(?:RequestId|Scene|Title|Path|Sql|Message)/u);
assert.match(integration, /_nativeOperatorLogWriter\.TryWrite\(record\)/u);
assert.match(integration,
/try[\s\S]*_nativeOperatorLogWriter\.TryWrite\(record\);[\s\S]*catch/u);
assert.doesNotMatch(integration, /TryWrite\((?:command|result|source|state|exception|payload)/u);
});