Migrate remaining legacy operator workflows
This commit is contained in:
102
tests/Web/native-operator-log-integration.test.cjs
Normal file
102
tests/Web/native-operator-log-integration.test.cjs
Normal file
@@ -0,0 +1,102 @@
|
||||
"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);
|
||||
});
|
||||
Reference in New Issue
Block a user