Files
MBN_STOCK_WEBVIEW/tests/Web/manual-financial-bridge-integration.test.cjs

124 lines
6.3 KiB
JavaScript

"use strict";
const test = require("node:test");
const assert = require("node:assert/strict");
const fs = require("node:fs");
const path = require("node:path");
const workflow = require("../../Web/manual-financial-workflow.js");
const repositoryRoot = path.resolve(__dirname, "..", "..");
const bridge = fs.readFileSync(
path.join(repositoryRoot, "MainWindow.ManualFinancial.cs"),
"utf8");
const playoutBridge = fs.readFileSync(
path.join(repositoryRoot, "MainWindow.Playout.cs"),
"utf8");
const operatorGate = fs.readFileSync(
path.join(repositoryRoot, "src", "MBN_STOCK_WEBVIEW.Core", "Playout", "OperatorMutationGate.cs"),
"utf8");
test("native partial exposes every strict workflow request and response", () => {
for (const [name, value] of Object.entries(workflow.bridgeContract)) {
if (name.endsWith("RequestType") || name.endsWith("ResultType") || name.endsWith("ErrorType")) {
assert.match(bridge, new RegExp(`"${value}"`));
}
}
assert.match(bridge, /private bool TryHandleManualFinancialRequest\(/);
assert.match(bridge, /HasOnlyProperties\(payload, "requestId", "screen", "stock", "record"\)/);
assert.match(bridge, /element\.GetArrayLength\(\) != 5/);
assert.match(bridge, /element\.GetArrayLength\(\) != 4/);
assert.match(bridge, /element\.GetArrayLength\(\) != 6/);
});
test("runtime helper composes the typed service with the non-retrying Oracle executor", () => {
assert.match(bridge,
/new LegacyManualFinancialScreenService\(\s*runtime\.Executor,\s*new OracleManualFinancialMutationExecutor\(/s);
assert.match(bridge, /runtime\.ConnectionFactory/);
assert.match(bridge, /runtime\.Options\.Resilience/);
});
test("list, load and selection reads own independent correlation lanes", () => {
assert.match(bridge,
/Dictionary<string, ManualFinancialReadRequest> _manualFinancialReadLanes/);
assert.match(bridge,
/new ManualFinancialReadRequest\(\s*operation,\s*requestId,/s);
assert.match(bridge, /RegisterManualFinancialRead\(request\)/);
assert.match(bridge, /_manualFinancialReadLanes\[request\.Lane\] = request/);
assert.doesNotMatch(bridge, /_manualFinancialReadCancellation/);
assert.match(bridge, /await _databaseActivityGate\.WaitAsync\(requestToken\)/);
assert.ok((bridge.match(/Volatile\.Read\(ref _playoutCommandInFlight\) != 0/g) || []).length >= 2);
assert.match(bridge, /browserGeneration != Volatile\.Read\(ref _manualFinancialBrowserGeneration\)/);
assert.match(bridge, /!TryCompleteManualFinancialReadIfCurrent\(request\)/);
});
test("same-lane supersede emits one correlated terminal cancellation", () => {
assert.match(bridge, /CancelRequest\(superseded\.Cancellation\)/);
assert.match(bridge, /superseded\?\.TryComplete\(\)/);
assert.match(bridge, /"CANCELLED"/);
assert.match(bridge, /newer request replaced it/);
assert.match(bridge,
/Interlocked\.CompareExchange\(ref _terminalState, 1, 0\) == 0/);
assert.match(bridge,
/TryCompleteManualFinancialReadIfCurrent\(request\)[\s\S]*PostMessage\(reply\.MessageType/s);
assert.match(bridge,
/ReferenceEquals\(current, request\) &&[\s\S]*request\.TryComplete\(\)/s);
});
test("writes are single-flight and impossible during command, PREPARED, PROGRAM or refresh", () => {
assert.match(bridge, /_manualFinancialWriteGate\.WaitAsync\(0\)/);
assert.match(bridge, /_playoutCommandGate\.WaitAsync\(0, requestToken\)/);
assert.match(bridge, /_databaseActivityGate\.WaitAsync\(0, requestToken\)/);
assert.match(bridge, /CanStartManualFinancialWrite\(\)/);
assert.match(bridge,
/CanStartOperatorMutation\(IsManualFinancialWriteQuarantined\(\)\)/);
assert.match(playoutBridge, /private bool CanStartOperatorMutation\(/);
assert.match(playoutBridge, /new OperatorMutationGateSnapshot\(/);
assert.match(playoutBridge, /IsEngineAvailable: engine is not null/);
assert.match(playoutBridge, /IsWorkflowAvailable: workflow is not null/);
assert.match(playoutBridge, /IsRefreshTaskRunning: _legacyRefreshTask is \{ IsCompleted: false \}/);
for (const condition of [
"IsCommandAvailable", "IsPlayCompletionPending", "PreparedSceneName", "OnAirSceneName",
"PreparedCutCode", "OnAirCutCode", "IsRefreshActive", "IsRefreshTaskRunning"
]) {
assert.match(operatorGate, new RegExp(`snapshot\\.${condition}`));
}
assert.match(bridge, /"PLAYOUT_ACTIVE"/);
});
test("OutcomeUnknown and browser correlation loss latch a process-lifetime no-retry quarantine", () => {
assert.match(bridge, /catch \(ManualFinancialMutationException exception\)/);
assert.match(bridge, /if \(exception\.OutcomeUnknown\)/);
assert.match(bridge,
/Interlocked\.CompareExchange\(ref _manualFinancialWriteQuarantined, 1, 0\)/);
assert.match(bridge, /retryable = false/);
assert.match(bridge, /rolled back and was not retried/i);
assert.doesNotMatch(bridge, /for\s*\([^)]*retry|while\s*\([^)]*retry/i);
});
test("browser, playout and shutdown hooks cancel correlation without clearing quarantine", () => {
assert.match(bridge, /private void InvalidateManualFinancialBrowserRequests\(\)/);
assert.match(bridge, /Interlocked\.Increment\(ref _manualFinancialBrowserGeneration\)/);
assert.match(bridge, /Volatile\.Read\(ref _manualFinancialWriteInFlight\) != 0/);
assert.match(bridge,
/Interlocked\.Exchange\(ref _manualFinancialWriteCancellation, null\)/);
assert.match(bridge, /private void CancelManualFinancialReadsForPlayout\(\)/);
assert.match(bridge, /private void ShutdownManualFinancialRuntime\(\)/);
assert.ok((bridge.match(/SnapshotManualFinancialReads\(clear: true\)/g) || []).length >= 2);
assert.match(bridge,
/CancelManualFinancialReadsForPlayout\(\)[\s\S]*SnapshotManualFinancialReads\(clear: false\)/s);
});
test("selection and restore boundaries re-read row version before exposing a cut", () => {
assert.match(bridge, /var snapshot = await service\.GetAsync\(identity, cancellationToken\)/);
assert.match(bridge, /EnsureManualFinancialRowVersion\(snapshot, rowVersion\)/);
assert.match(bridge, /ResolveManualFinancialStockAsync\(/);
assert.match(bridge, /ManualFinancialCutContracts\.CreateSelection\(snapshot, verified\)/);
assert.match(bridge, /"manual-financial-selection-results"/);
});
test("successful writes return the real transaction receipt fields", () => {
assert.match(bridge, /operationId = receipt\.OperationId\.ToString\("N"/);
assert.match(bridge, /receipt\.CommittedAt/);
assert.match(bridge, /receipt\.AffectedRows/);
});