"use strict"; const fs = require("node:fs"); const path = require("node:path"); const test = require("node:test"); const assert = require("node:assert/strict"); const root = path.resolve(__dirname, "..", ".."); const native = fs.readFileSync(path.join(root, "MainWindow.ManualLists.cs"), "utf8"); const dispatch = fs.readFileSync(path.join(root, "MainWindow.xaml.cs"), "utf8"); const importer = fs.readFileSync(path.join( root, "src", "MBN_STOCK_WEBVIEW.Infrastructure", "Playout", "LegacyManualOperatorDataImporter.cs"), "utf8"); const playout = fs.readFileSync(path.join(root, "MainWindow.Playout.cs"), "utf8"); const factory = fs.readFileSync(path.join(root, "LegacySceneRuntimeFactory.cs"), "utf8"); const app = fs.readFileSync(path.join(root, "Web/app.js"), "utf8"); test("generic catalog cannot add the s5025 sample outside the trusted manual-list workflow", () => { assert.match(app, /operatorInputBuilderKeys[\s\S]*"s5025"/u); assert.match(app, /item\.builderKey === "s5025"[\s\S]*투자자별 수동 목록 화면에서 추가/u); }); test("native VI responses expose the canonical version and no-op persistence receipt", () => { assert.match(native, /ReadSnapshotAsync\(_lifetimeCancellation\.Token\)/u); assert.match(native, /version = snapshot\.RowVersion/u); assert.match(native, /persisted = result\.Persisted/u); assert.match(native, /version = result\.Snapshot\.RowVersion/u); }); test("VI writes require a closed expected version and compare it under the data gate before mutation", () => { assert.match(native, /HasOnlyProperties\(payload, "requestId", "expectedVersion", "items"\)/u); assert.match(native, /TrustedViManualReference\.IsRowVersion\(expectedVersion\)/u); const methodStart = native.indexOf("private async Task HandleViManualListWriteAsync"); const methodEnd = native.indexOf("private async Task TryEnterManualMutationGatesAsync", methodStart); const method = native.slice(methodStart, methodEnd); const gate = method.indexOf("TryEnterManualMutationGatesAsync"); const reread = method.indexOf("var currentSnapshot = await store.ReadSnapshotAsync"); const compare = method.indexOf("currentSnapshot.RowVersion"); const stale = method.indexOf('"STALE_DATA"'); const mutation = method.indexOf("var result = await store.WriteAsync"); assert.ok(methodStart >= 0 && methodEnd > methodStart); assert.ok(gate >= 0 && gate < reread && reread < compare && compare < stale && stale < mutation); assert.match(method.slice(stale, mutation), /retryable: false,[\s\S]*outcomeUnknown: false/u); }); test("different post-error readback always enters OutcomeUnknown quarantine", () => { assert.equal(native.includes('"SAVE_FAILED"'), false); assert.match(native, /수동 순매도 저장 중 파일 내용이 달라져 결과를 확정할 수 없습니다/u); assert.match(native, /VI 수동 목록 저장 중 파일 내용이 달라져 결과를 확정할 수 없습니다/u); assert.ok((native.match(/PostManualOperatorQuarantine\(requestId, "save-/gu) || []).length >= 4); }); test("configured external operator paths are disabled before any directory creation", () => { const configuredGuard = native.indexOf("if (!string.IsNullOrWhiteSpace(configuredDirectory))"); const privatePrepare = native.indexOf("TrustedManualDirectory.PreparePrivateWritableDirectory"); assert.ok(configuredGuard >= 0); assert.ok(privatePrepare > configuredGuard); assert.equal(native.includes("Directory.CreateDirectory(directory)"), false); }); test("interrupted import guard runs before either manual store can be constructed", () => { const inspection = native.indexOf("LegacyManualOperatorDataRuntimeGuard.Inspect(directory)"); const readyGuard = native.indexOf("if (!inspection.IsReady)", inspection); const failClosedReturn = native.indexOf("return;", readyGuard); const netStore = native.indexOf("new S5025TrustedManualFileStore(directory)", inspection); const viStore = native.indexOf("new ViTrustedManualFileStore(directory)", inspection); assert.ok(inspection >= 0 && readyGuard > inspection && failClosedReturn > readyGuard); assert.ok(netStore > failClosedReturn && viStore > failClosedReturn); assert.match(importer, /if \(EntryExists\(intentPath\)\)[\s\S]*Do not read any production file/u); }); test("legacy import commit has a durable intent before moves and marker remains last", () => { const commitStart = importer.indexOf("private void CommitAndVerify"); const commitEnd = importer.indexOf("private void WriteIntentDurably", commitStart); const commit = importer.slice(commitStart, commitEnd); const intent = commit.indexOf("WriteIntentDurably(intentPath, intentBytes)"); const dataMove = commit.indexOf("File.Move(file.TemporaryPath, file.FinalPath"); const readback = commit.indexOf("VerifyImportedData(snapshot)"); const markerMove = commit.indexOf("File.Move(marker.TemporaryPath, marker.FinalPath"); const intentRemoval = commit.indexOf("DeleteOwnedFile(", markerMove); assert.ok(intent >= 0 && intent < dataMove && dataMove < readback); assert.ok(readback < markerMove && markerMove < intentRemoval); assert.match(importer, /FileOptions\.WriteThrough[\s\S]*Flush\(flushToDisk: true\)/u); }); test("trusted VI source is injected and checked for both page plan and page load", () => { assert.match(playout, /trustedViSource: _viManualListStore/u); assert.match(factory, /ITrustedViStockNameSnapshotSource\? trustedViSource/u); assert.ok((factory.match(/TrustedViManualReference\.ResolveAsync/gu) || []).length >= 2); }); test("legacy manual import bridge is closed, fixed under UserProfile, and never accepts a path", () => { assert.match(dispatch, /case "import-legacy-manual-operator-data"/u); const methodStart = native.indexOf("private async Task HandleLegacyManualOperatorImportAsync"); const methodEnd = native.indexOf("private async Task HandleManualNetSellReadAsync", methodStart); const method = native.slice(methodStart, methodEnd); assert.ok(methodStart >= 0 && methodEnd > methodStart); assert.match(method, /HasOnlyProperties\(payload, "requestId"\)/u); assert.equal(/GetProperty\([^)]*(?:path|directory|source)/iu.test(method), false); assert.match(importer, /Environment\.SpecialFolder\.UserProfile/u); assert.match(importer, /FixedRelativeDirectory/u); assert.equal(importer.includes("MBN_STOCK_OPERATOR_DATA_DIRECTORY"), false); assert.equal(importer.includes("C:\\Users\\MD"), false); }); test("legacy manual import returns only a versioned receipt and terminal no-retry errors", () => { assert.match(native, /PostMessage\("legacy-manual-operator-import-result"/u); assert.match(native, /markerVersion = result\.MarkerVersion/u); assert.match(native, /sourceSha256 = result\.SourceSha256/u); assert.match(native, /retryable: false,[\s\S]*outcomeUnknown: false/u); assert.match(native, /PostManualOperatorQuarantine\(requestId, operation\)/u); assert.equal(native.includes("result.SourcePath"), false); });