Migrate remaining legacy operator workflows
This commit is contained in:
@@ -4,6 +4,7 @@ const assert = require("node:assert/strict");
|
||||
const workflow = require("../../Web/named-manual-restore-workflow.js");
|
||||
const manualLists = require("../../Web/manual-lists-workflow.js");
|
||||
const manualFinancial = require("../../Web/manual-financial-workflow.js");
|
||||
const namedPlaylists = require("../../Web/named-playlist-workflow.js");
|
||||
|
||||
function raw(overrides = {}) {
|
||||
return {
|
||||
@@ -14,6 +15,7 @@ function raw(overrides = {}) {
|
||||
graphicType: "MANUAL_NET_SELL",
|
||||
subtype: "MANUAL_NET_SELL",
|
||||
dataCode: "",
|
||||
pageText: "1/1",
|
||||
...overrides
|
||||
};
|
||||
}
|
||||
@@ -31,7 +33,7 @@ test("s5025 raw restoration accepts only canonical or exact legacy audience sign
|
||||
|
||||
const legacy = workflow.classify(raw({
|
||||
groupCode: "외국인 순매도 상위(수동)",
|
||||
subject: "",
|
||||
subject: "지수",
|
||||
graphicType: "순매도 상위",
|
||||
subtype: "순매도 상위"
|
||||
}), options);
|
||||
@@ -39,11 +41,12 @@ test("s5025 raw restoration accepts only canonical or exact legacy audience sign
|
||||
assert.equal(legacy.historical, true);
|
||||
assert.equal(workflow.classify(raw({
|
||||
groupCode: "외국인 순매도 상위",
|
||||
subject: "",
|
||||
subject: "지수",
|
||||
graphicType: "순매도 상위",
|
||||
subtype: "순매도 상위"
|
||||
}), options), null);
|
||||
assert.equal(workflow.classify(raw({ dataCode: "path.dat" }), options), null);
|
||||
assert.equal(workflow.classify(raw({ pageText: "1/2" }), options), null);
|
||||
});
|
||||
|
||||
test("s5025 becomes playable only after one correlated trusted five-row read", () => {
|
||||
@@ -57,6 +60,7 @@ test("s5025 becomes playable only after one correlated trusted five-row read", (
|
||||
assert.ok(entry);
|
||||
assert.equal(entry.enabled, false);
|
||||
assert.equal(entry.builderKey, "s5025");
|
||||
assert.equal(manualLists.isPlaylistEntryPlayable(entry), true);
|
||||
assert.equal(manualLists.restorePlaylistEntry(entry).operator.audience, "INDIVIDUAL");
|
||||
assert.equal(workflow.materializeManualList(pending, {
|
||||
requestId: "stale-read",
|
||||
@@ -84,6 +88,17 @@ test("versioned and historical VI raw rows require the current exact native snap
|
||||
};
|
||||
assert.equal(workflow.materializeManualList(current, payload, request).builderKey, "s5074");
|
||||
assert.equal(workflow.materializeManualList(current, { ...payload, version: "b".repeat(64) }, request), null);
|
||||
assert.equal(workflow.materializeManualList(
|
||||
workflow.classify(raw({
|
||||
groupCode: "PAGED_VI",
|
||||
subject: manualLists.viSnapshotSubject,
|
||||
graphicType: "INPUT_ORDER",
|
||||
subtype: "CURRENT",
|
||||
dataCode: version,
|
||||
pageText: "1/2"
|
||||
}), options),
|
||||
payload,
|
||||
request), null);
|
||||
|
||||
const historical = workflow.classify(raw({
|
||||
groupCode: "",
|
||||
@@ -102,11 +117,28 @@ test("versioned and historical VI raw rows require the current exact native snap
|
||||
requestId: "named-vi-history",
|
||||
items: [...payload.items].reverse()
|
||||
}, historicalRequest), null);
|
||||
assert.equal(workflow.classify(raw({
|
||||
const duplicate = workflow.classify(raw({
|
||||
groupCode: "",
|
||||
subject: "삼성전자,삼성전자",
|
||||
graphicType: "5단 표그래프",
|
||||
subtype: "VI 발동(수동)"
|
||||
}), options);
|
||||
const duplicateRequest = workflow.createManualListReadRequest("named-vi-duplicate", duplicate);
|
||||
assert.ok(workflow.materializeManualList(duplicate, {
|
||||
...payload,
|
||||
requestId: "named-vi-duplicate",
|
||||
items: [payload.items[0], payload.items[0]]
|
||||
}, duplicateRequest));
|
||||
assert.equal(workflow.materializeManualList(duplicate, {
|
||||
...payload,
|
||||
requestId: "named-vi-duplicate"
|
||||
}, duplicateRequest), null);
|
||||
assert.equal(workflow.classify(raw({
|
||||
groupCode: "",
|
||||
subject: "삼성전자,SK하이닉스",
|
||||
graphicType: "5단 표그래프",
|
||||
subtype: "VI 발동(수동)",
|
||||
pageText: "1/2"
|
||||
}), options), null);
|
||||
assert.equal(workflow.classify(raw({
|
||||
groupCode: "",
|
||||
@@ -132,6 +164,20 @@ test("GraphE raw rows yield only a fresh-load descriptor with exact screen, mark
|
||||
assert.deepEqual(pending.identity, { screen: profile.screen, stockName: "삼성전자" });
|
||||
}
|
||||
const profile = manualFinancial.sourceContract.screens[0];
|
||||
assert.equal(workflow.classify(raw({
|
||||
groupCode: "KOSPI",
|
||||
subject: "삼성전자",
|
||||
graphicType: profile.label,
|
||||
subtype: "",
|
||||
dataCode: ""
|
||||
}), options), null);
|
||||
assert.equal(workflow.classify(raw({
|
||||
groupCode: "코스피",
|
||||
subject: "삼성전자",
|
||||
graphicType: profile.graphicType,
|
||||
subtype: "",
|
||||
dataCode: ""
|
||||
}), options), null);
|
||||
assert.equal(workflow.classify(raw({
|
||||
groupCode: "UNKNOWN",
|
||||
subject: "삼성전자",
|
||||
@@ -146,4 +192,168 @@ test("GraphE raw rows yield only a fresh-load descriptor with exact screen, mark
|
||||
subtype: "",
|
||||
dataCode: "005930"
|
||||
}), options), null);
|
||||
assert.equal(workflow.classify(raw({
|
||||
groupCode: "KOSPI",
|
||||
subject: "삼성전자",
|
||||
graphicType: profile.graphicType,
|
||||
subtype: "",
|
||||
dataCode: "",
|
||||
pageText: "1/2"
|
||||
}), options), null);
|
||||
});
|
||||
|
||||
function createFreshGraphEntry() {
|
||||
const screen = "sales";
|
||||
const record = {
|
||||
kind: screen,
|
||||
stockName: "삼성전자",
|
||||
quarters: ["1Q", "2Q", "3Q", "4Q", "5Q", "6Q"]
|
||||
.map((quarter, index) => ({ quarter, value: index + 1 }))
|
||||
};
|
||||
const rowVersion = "A".repeat(64);
|
||||
const loadRequest = manualFinancial.createLoadRequest("named-save-load", screen, "삼성전자");
|
||||
const load = manualFinancial.normalizeLoadResponse({
|
||||
requestId: "named-save-load",
|
||||
screen,
|
||||
retrievedAt: "2026-07-12T01:00:00+09:00",
|
||||
snapshot: { stockName: "삼성전자", rowVersion, record }
|
||||
}, loadRequest);
|
||||
const stockResponse = manualFinancial.normalizeStockSearchResponse({
|
||||
requestId: "named-save-stock",
|
||||
query: "삼성전자",
|
||||
retrievedAt: "2026-07-12T01:00:01+09:00",
|
||||
totalRowCount: 1,
|
||||
truncated: false,
|
||||
results: [{ market: "kospi", source: "oracle", name: "삼성전자", code: "005930" }]
|
||||
});
|
||||
const stock = manualFinancial.verifyStockForRecord(record, stockResponse, "kospi", "005930");
|
||||
const selectionRequest = manualFinancial.createSelectionRequest("named-save-selection", load.snapshot, stock);
|
||||
const selection = manualFinancial.normalizeSelectionResponse({
|
||||
requestId: "named-save-selection",
|
||||
screen,
|
||||
stockName: "삼성전자",
|
||||
rowVersion,
|
||||
builderKey: "s5080",
|
||||
code: "5080",
|
||||
aliases: ["5080"],
|
||||
selection: {
|
||||
groupCode: "KOSPI",
|
||||
subject: "삼성전자",
|
||||
graphicType: "SALES",
|
||||
subtype: "",
|
||||
dataCode: ""
|
||||
},
|
||||
currentPage: 1,
|
||||
totalPages: 1
|
||||
}, selectionRequest);
|
||||
return manualFinancial.createPlaylistEntry(load.snapshot, stock, selection, {
|
||||
id: "named-save-graph",
|
||||
fadeDuration: 6
|
||||
});
|
||||
}
|
||||
|
||||
test("named save accepts only live fresh-read manual identities", () => {
|
||||
const netRead = manualLists.normalizeNetSellDataResponse({
|
||||
requestId: "named-save-net-read",
|
||||
audience: "FOREIGN",
|
||||
rows
|
||||
}, { requestId: "named-save-net-read", audience: "FOREIGN" });
|
||||
const net = manualLists.createNetSellPlaylistEntry("FOREIGN", {
|
||||
id: "named-save-net",
|
||||
fadeDuration: 6
|
||||
}, netRead);
|
||||
assert.equal(workflow.isTrustedEntryForSave(net, 0), true);
|
||||
assert.equal(workflow.isTrustedEntryForSave(JSON.parse(JSON.stringify(net)), 0), false);
|
||||
|
||||
const version = "b".repeat(64);
|
||||
const viPayload = {
|
||||
requestId: "named-save-vi-read",
|
||||
itemCount: 2,
|
||||
pageCount: 1,
|
||||
items: [{ code: "P005930", name: "삼성전자" }, { code: "P000660", name: "SK하이닉스" }],
|
||||
version
|
||||
};
|
||||
const viRead = manualLists.normalizeViDataResponse(viPayload, viPayload.requestId);
|
||||
const vi = manualLists.createViPlaylistEntry(viRead.items, viRead.version, {
|
||||
id: "named-save-vi",
|
||||
fadeDuration: 6
|
||||
}, viRead);
|
||||
assert.equal(workflow.isTrustedEntryForSave(vi, 1), true);
|
||||
vi.pageCount = 2;
|
||||
assert.equal(workflow.isTrustedEntryForSave(vi, 1), false);
|
||||
|
||||
const graph = createFreshGraphEntry();
|
||||
assert.equal(workflow.isTrustedEntryForSave(graph, 2), true);
|
||||
assert.equal(workflow.isTrustedEntryForSave(JSON.parse(JSON.stringify(graph)), 2), false);
|
||||
|
||||
vi.pageCount = 1;
|
||||
const replace = namedPlaylists.createReplaceRequest(
|
||||
"named-save-replace",
|
||||
"12345678",
|
||||
[net, vi, graph],
|
||||
{
|
||||
isTrustedEntry: workflow.isTrustedEntryForSave,
|
||||
pageTextForEntry: entry => entry.operator?.kind === "vi" ? `1/${entry.pageCount}` : "1/1",
|
||||
fieldsForEntry: workflow.legacyFieldsForEntry
|
||||
});
|
||||
assert.deepEqual(replace.items.map(item => [
|
||||
item.groupCode,
|
||||
item.subject,
|
||||
item.graphicType,
|
||||
item.subtype,
|
||||
item.pageText,
|
||||
item.dataCode
|
||||
]), [
|
||||
["외국인 순매도 상위(수동)", "지수", "순매도 상위", "순매도 상위", "1/1", ""],
|
||||
["", "삼성전자,SK하이닉스", "5단 표그래프", "VI 발동(수동)", "1/1", ""],
|
||||
["코스피", "삼성전자", "매출액", "", "1/1", ""]
|
||||
]);
|
||||
});
|
||||
|
||||
test("stored local FSell and VI entries require the same fresh-read materialization", () => {
|
||||
const net = manualLists.createNetSellPlaylistEntry("INSTITUTION", {
|
||||
id: "stored-local-net",
|
||||
fadeDuration: 6,
|
||||
enabled: false
|
||||
});
|
||||
const pendingNet = workflow.createStoredManualListPending(
|
||||
JSON.parse(JSON.stringify(net)),
|
||||
0);
|
||||
assert.equal(workflow.isStoredManualListPending(pendingNet), true);
|
||||
const netRequest = workflow.createManualListReadRequest("stored-net-read", pendingNet);
|
||||
const restoredNet = workflow.materializeManualList(pendingNet, {
|
||||
requestId: "stored-net-read",
|
||||
audience: "INSTITUTION",
|
||||
rows
|
||||
}, netRequest);
|
||||
assert.ok(restoredNet);
|
||||
assert.equal(restoredNet.enabled, false);
|
||||
assert.equal(manualLists.isPlaylistEntryPlayable(restoredNet), true);
|
||||
|
||||
const version = "c".repeat(64);
|
||||
const items = [{ code: "P005930", name: "삼성전자" }];
|
||||
const vi = manualLists.createViPlaylistEntry(items, version, {
|
||||
id: "stored-local-vi",
|
||||
fadeDuration: 6
|
||||
});
|
||||
const pendingVi = workflow.createStoredManualListPending(
|
||||
JSON.parse(JSON.stringify(vi)),
|
||||
1);
|
||||
const viRequest = workflow.createManualListReadRequest("stored-vi-read", pendingVi);
|
||||
const viPayload = {
|
||||
requestId: "stored-vi-read",
|
||||
itemCount: 1,
|
||||
pageCount: 1,
|
||||
items,
|
||||
version
|
||||
};
|
||||
assert.equal(workflow.materializeManualList(pendingVi, viPayload, viRequest).builderKey, "s5074");
|
||||
assert.equal(workflow.materializeManualList(
|
||||
pendingVi,
|
||||
{ ...viPayload, version: "d".repeat(64) },
|
||||
viRequest), null);
|
||||
assert.equal(workflow.createStoredManualListPending({
|
||||
...JSON.parse(JSON.stringify(vi)),
|
||||
selection: { ...vi.selection, dataCode: "tampered" }
|
||||
}, 1), null);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user