feat: restore legacy named playlist workflows
This commit is contained in:
136
tests/Web/named-foreign-stock-restore-app-integration.test.cjs
Normal file
136
tests/Web/named-foreign-stock-restore-app-integration.test.cjs
Normal file
@@ -0,0 +1,136 @@
|
||||
"use strict";
|
||||
|
||||
const test = require("node:test");
|
||||
const assert = require("node:assert/strict");
|
||||
const fs = require("node:fs");
|
||||
const path = require("node:path");
|
||||
const foreignStock = require("../../Web/named-foreign-stock-restore-workflow.js");
|
||||
const legacyRows = require("../../Web/legacy-named-row-workflow.js");
|
||||
const namedPlaylists = require("../../Web/named-playlist-workflow.js");
|
||||
|
||||
const root = path.resolve(__dirname, "../..");
|
||||
const app = fs.readFileSync(path.join(root, "Web/app.js"), "utf8");
|
||||
const index = fs.readFileSync(path.join(root, "Web/index.html"), "utf8");
|
||||
const bridge = fs.readFileSync(
|
||||
path.join(root, "MainWindow.NamedForeignStockRestore.cs"), "utf8");
|
||||
const namedBridge = fs.readFileSync(path.join(root, "MainWindow.NamedPlaylists.cs"), "utf8");
|
||||
const core = fs.readFileSync(
|
||||
path.join(root, "src/MBN_STOCK_WEBVIEW.Core/Data/LegacyNamedForeignStockRestore.cs"),
|
||||
"utf8");
|
||||
|
||||
function rawRow(enabled = false) {
|
||||
return {
|
||||
itemIndex: 0,
|
||||
enabled,
|
||||
groupCode: "해외종목",
|
||||
subject: "NVIDIA",
|
||||
graphicType: "캔들그래프",
|
||||
subtype: "240일",
|
||||
pageText: "1/1",
|
||||
dataCode: ""
|
||||
};
|
||||
}
|
||||
|
||||
function materializedEntry() {
|
||||
const raw = rawRow();
|
||||
const pending = foreignStock.classify(raw, {
|
||||
id: "db-foreign-stock-240",
|
||||
fadeDuration: 6,
|
||||
ma5: true,
|
||||
ma20: false
|
||||
});
|
||||
const response = foreignStock.normalizeResponse({
|
||||
requestId: "foreign-stock-load",
|
||||
retrievedAt: "2026-07-12T12:34:56+09:00",
|
||||
totalRowCount: 1,
|
||||
rows: [{
|
||||
itemIndex: 0,
|
||||
status: "resolved",
|
||||
actionId: "stock-candle-240d",
|
||||
inputName: "NVIDIA",
|
||||
symbol: "NAS@NVDA",
|
||||
nationCode: "US",
|
||||
foreignDomesticCode: "1",
|
||||
builderKey: "s8010",
|
||||
cutCode: "8056",
|
||||
valueType: "PRICE",
|
||||
periodDays: 240
|
||||
}]
|
||||
}, "foreign-stock-load", [pending]);
|
||||
return foreignStock.materialize(pending, response.rows[0]);
|
||||
}
|
||||
|
||||
test("named load connects one correlated native foreign-stock restore batch end to end", () => {
|
||||
assert.match(index, /<script src="named-foreign-stock-restore-workflow\.js"><\/script>[\s\S]*<script src="legacy-named-row-workflow\.js"><\/script>/u);
|
||||
assert.match(app, /MbnNamedForeignStockRestoreWorkflow/u);
|
||||
assert.match(app, /namedForeignStockRestoreWorkflow\.classify/u);
|
||||
assert.match(app, /namedForeignStockRestoreWorkflow\.normalizeResponse/u);
|
||||
assert.match(app, /namedForeignStockRestoreWorkflow\.materialize/u);
|
||||
assert.match(app, /case "named-foreign-stock-restore-results"/u);
|
||||
assert.match(app, /case "named-foreign-stock-restore-error"/u);
|
||||
assert.match(namedBridge, /await PostNamedForeignStockRestoreAsync\(/u);
|
||||
assert.match(bridge, /PostMessage\("named-foreign-stock-restore-results"/u);
|
||||
assert.match(bridge, /PostMessage\("named-foreign-stock-restore-error"/u);
|
||||
});
|
||||
|
||||
test("Core uses one exact bound SELECT and native bridge never writes or calls playout", () => {
|
||||
assert.match(core, /F_INPUT_NAME = :input_name/u);
|
||||
assert.match(core, /F_FDTC = '1'/u);
|
||||
assert.match(core, /F_NATC IN \('US', 'TW'\)/u);
|
||||
assert.match(core, /WHERE ROWNUM <= 2/u);
|
||||
assert.match(core, /new DataQueryParameter\("input_name"/u);
|
||||
assert.doesNotMatch(core, /\bLIKE\b/iu);
|
||||
assert.doesNotMatch(core, /\b(?:INSERT\s+INTO|UPDATE\s+[A-Z_]|DELETE\s+FROM|MERGE\s+INTO)\b/iu);
|
||||
assert.doesNotMatch(bridge, /Tornado|TakeIn|PrepareAsync|ExecuteNonQuery/iu);
|
||||
assert.match(bridge, /retryable = false/u);
|
||||
assert.match(bridge, /자동 재시도하지 않습니다/u);
|
||||
});
|
||||
|
||||
test("pending state blocks edits and timeout is terminal without an automatic retry", () => {
|
||||
assert.match(app, /state\.namedPlaylist\.foreignStockRestorePending/u);
|
||||
assert.match(app, /armNamedForeignStockRestoreTimeout/u);
|
||||
assert.match(app, /setTimeout\(\(\) => \{[\s\S]*자동 재시도하지 않습니다\.[\s\S]*\}, 30000\)/u);
|
||||
assert.match(app, /comparisonRestorePending \|\|[\s\S]*foreignStockRestorePending \|\|[\s\S]*foreignIndexCandleRestorePending/u);
|
||||
assert.match(app, /pendingForeignStocks\.length === 0/u);
|
||||
assert.match(app, /clearNamedForeignStockRestoreTimeout\(record\)/u);
|
||||
assert.doesNotMatch(app, /retryNamedForeignStock|requestNamedForeignStockRestore/u);
|
||||
});
|
||||
|
||||
test("verified identity and moving averages preserve the exact original seven fields for save", () => {
|
||||
const entry = materializedEntry();
|
||||
const raw = rawRow();
|
||||
assert.ok(entry);
|
||||
assert.equal(legacyRows.matchesCanonicalEntry(entry, raw), true);
|
||||
const selection = legacyRows.legacySelectionForEntry(entry);
|
||||
assert.deepEqual(selection, {
|
||||
groupCode: "해외종목",
|
||||
subject: "NVIDIA",
|
||||
graphicType: "캔들그래프",
|
||||
subtype: "240일",
|
||||
dataCode: ""
|
||||
});
|
||||
assert.deepEqual(entry.operator.movingAverages, { ma5: true, ma20: false });
|
||||
assert.deepEqual(namedPlaylists.toLegacySevenFields({ ...entry, selection }, "1/1"), [
|
||||
"0", "해외종목", "NVIDIA", "캔들그래프", "240일", "1/1", ""
|
||||
]);
|
||||
assert.match(app, /해외종목 항목의 원본 7필드 서명을 안전하게 왕복할 수 없습니다/u);
|
||||
assert.match(app, /namedForeignStockRestoreWorkflow\.matchesRaw\(restoredForeignStock, rawRow\)/u);
|
||||
assert.match(app, /item\.operator\?\.source === namedForeignStockRestoreWorkflow\.source\) return item/u);
|
||||
});
|
||||
|
||||
test("canonical FOREIGN_STOCK remains on the existing workflow and cannot collide", () => {
|
||||
assert.equal(foreignStock.classify({
|
||||
...rawRow(true),
|
||||
groupCode: "FOREIGN_STOCK",
|
||||
graphicType: "MA5",
|
||||
subtype: "PRICE",
|
||||
dataCode: "NAS@NVDA|US|1"
|
||||
}, {
|
||||
id: "canonical-does-not-route",
|
||||
fadeDuration: 6,
|
||||
ma5: true,
|
||||
ma20: false
|
||||
}), null);
|
||||
assert.match(app, /addOverseasNamedCandidates/u);
|
||||
assert.match(app, /rawRow\.groupCode !== "FOREIGN_STOCK"/u);
|
||||
});
|
||||
Reference in New Issue
Block a user