Complete legacy operator UI and playout migration

This commit is contained in:
2026-07-12 02:05:49 +09:00
parent d2e16bf0c2
commit ffb8f43c19
131 changed files with 48473 additions and 228 deletions

View File

@@ -0,0 +1,56 @@
"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 playout = fs.readFileSync(path.join(root, "MainWindow.Playout.cs"), "utf8");
const factory = fs.readFileSync(path.join(root, "LegacySceneRuntimeFactory.cs"), "utf8");
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<bool> 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("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);
});