Migrate remaining legacy operator workflows

This commit is contained in:
2026-07-12 05:39:27 +09:00
parent ffb8f43c19
commit a01836a2d7
132 changed files with 20566 additions and 720 deletions

View File

@@ -31,6 +31,27 @@ test("bridge requests expose only closed operations and keys", () => {
type: "request-vi-manual-list",
payload: { requestId }
});
const legacyImport = workflow.createLegacyImportRequest(requestId);
assert.deepEqual(legacyImport, {
type: "import-legacy-manual-operator-data",
payload: { requestId }
});
assert.equal(/path|directory|sourceRoot/iu.test(JSON.stringify(legacyImport)), false);
});
test("legacy import receipt is exact, correlated, versioned, and path-free", () => {
const response = {
requestId,
markerVersion: 1,
sourceSha256: "b".repeat(64),
importedAt: "2026-07-12T01:02:03+00:00",
netSellRowCount: 15,
viItemCount: 9
};
assert.deepEqual(workflow.normalizeLegacyImportResponse(response, requestId), response);
assert.equal(workflow.normalizeLegacyImportResponse({ ...response, markerVersion: 2 }), null);
assert.equal(workflow.normalizeLegacyImportResponse({ ...response, path: "C:\\private" }), null);
assert.equal(workflow.normalizeLegacyImportResponse(response, "other_request"), null);
});
test("net-sell save request requires exactly five strict rows", () => {
@@ -83,21 +104,38 @@ test("status response preserves process-lifetime write quarantine", () => {
assert.deepEqual(workflow.normalizeStatusResponse({
requestId,
available: true,
code: "READY",
writeQuarantined: true,
message: "저장 결과를 확인할 수 없습니다."
}, requestId), {
requestId,
available: true,
code: "READY",
writeQuarantined: true,
message: "저장 결과를 확인할 수 없습니다."
});
assert.equal(workflow.normalizeStatusResponse({
requestId,
available: true,
code: "READY",
writeQuarantined: true,
message: "저장 결과를 확인할 수 없습니다.",
extra: true
}), null);
assert.equal(workflow.normalizeStatusResponse({
requestId,
available: true,
code: "IMPORT_INTERRUPTED",
writeQuarantined: false,
message: null
}), null);
assert.equal(workflow.normalizeStatusResponse({
requestId,
available: false,
code: "UNRECOGNIZED_STATE",
writeQuarantined: false,
message: "unavailable"
}), null);
});
test("net-sell data response is correlated and exact", () => {
@@ -280,6 +318,49 @@ test("trusted restore recreates manual entries and rejects tampering", () => {
}), null);
});
test("only entries bound to a correlated fresh read are playable", () => {
const unverifiedNet = workflow.createNetSellPlaylistEntry("FOREIGN", {
id: "entry_net_unverified",
fadeDuration: 6
});
assert.equal(workflow.isPlaylistEntryPlayable(unverifiedNet), false);
const netRead = workflow.normalizeNetSellDataResponse({
requestId,
audience: "FOREIGN",
rows: rows()
}, { requestId, audience: "FOREIGN" });
const verifiedNet = workflow.createNetSellPlaylistEntry("FOREIGN", {
id: "entry_net_verified",
fadeDuration: 6
}, netRead);
assert.equal(workflow.isPlaylistEntryPlayable(verifiedNet), true);
assert.equal(workflow.isPlaylistEntryPlayable(
workflow.restorePlaylistEntry(JSON.parse(JSON.stringify(verifiedNet)))), false);
assert.equal(workflow.isPlaylistEntryPlayable(workflow.createNetSellPlaylistEntry(
"INDIVIDUAL",
{ id: "entry_net_wrong_audience", fadeDuration: 6 },
netRead)), false);
const viRead = workflow.normalizeViDataResponse({
requestId,
itemCount: viItems().length,
pageCount: 1,
items: viItems(),
version: rowVersion
}, requestId);
const verifiedVi = workflow.createViPlaylistEntry(viItems(), rowVersion, {
id: "entry_vi_verified",
fadeDuration: 6
}, viRead);
assert.equal(workflow.isPlaylistEntryPlayable(verifiedVi), true);
assert.equal(workflow.isPlaylistEntryPlayable(workflow.createViPlaylistEntry(
viItems().slice(0, 2),
rowVersion,
{ id: "entry_vi_stale", fadeDuration: 6 },
viRead)), false);
});
test("request identifiers and option bounds are strict", () => {
assert.throws(() => workflow.createStatusRequest("bad id"));
assert.throws(() => workflow.createNetSellPlaylistEntry("FOREIGN", {