124 lines
6.3 KiB
JavaScript
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/);
|
|
});
|