Complete legacy operator UI and playout migration
This commit is contained in:
119
tests/Web/candle-options-workflow.test.cjs
Normal file
119
tests/Web/candle-options-workflow.test.cjs
Normal file
@@ -0,0 +1,119 @@
|
||||
"use strict";
|
||||
|
||||
const test = require("node:test");
|
||||
const assert = require("node:assert/strict");
|
||||
const workflow = require("../../Web/candle-options-workflow.js");
|
||||
const stock = require("../../Web/operator-workflow.js");
|
||||
const fixed = require("../../Web/legacy-fixed-workflow.js");
|
||||
const industry = require("../../Web/industry-workflow.js");
|
||||
const overseas = require("../../Web/overseas-workflow.js");
|
||||
const tradingHalt = require("../../Web/trading-halt-workflow.js");
|
||||
|
||||
const adapters = { stock, fixed, industry, overseas, tradingHalt };
|
||||
const options = id => ({ id, fadeDuration: 6, ma5: false, ma20: false });
|
||||
|
||||
const domesticStock = Object.freeze({
|
||||
market: "kospi",
|
||||
stockName: "삼성전자",
|
||||
displayName: "삼성전자",
|
||||
stockCode: "005930",
|
||||
isNxt: false
|
||||
});
|
||||
const industryItem = Object.freeze({ market: "kospi", name: "전기전자", code: "013" });
|
||||
const overseasStock = Object.freeze({
|
||||
inputName: "NVIDIA",
|
||||
symbol: "NAS@NVDA",
|
||||
nationCode: "US",
|
||||
fdtc: "1"
|
||||
});
|
||||
const haltedStock = Object.freeze({
|
||||
market: "kospi",
|
||||
stockCode: "005930",
|
||||
displayName: "삼성전자"
|
||||
});
|
||||
|
||||
function entries() {
|
||||
const fixedAction = fixed.actions.find(value => value.builderKey === "s8010" && value.available);
|
||||
const industryCut = industry.cuts.find(value => value.kind === "candle");
|
||||
return [
|
||||
stock.createStockPlaylistEntry(domesticStock, "candle-120d", options("stock-candle")),
|
||||
fixed.createFixedPlaylistEntry(fixedAction.id, options("fixed-candle")),
|
||||
industry.createIndustryPlaylistEntry("kospi", industryCut.id, {
|
||||
...options("industry-candle"),
|
||||
selected: industryItem
|
||||
}),
|
||||
overseas.createOverseasStockPlaylistEntry(
|
||||
overseasStock,
|
||||
"stock-candle-120d",
|
||||
options("overseas-candle")),
|
||||
tradingHalt.createTradingHaltPlaylistEntry(
|
||||
haltedStock,
|
||||
"candle-120d",
|
||||
options("halt-candle"))
|
||||
];
|
||||
}
|
||||
|
||||
test("global flags map to the exact closed candle graphic type", () => {
|
||||
assert.equal(workflow.expectedGraphicType(false, false), "");
|
||||
assert.equal(workflow.expectedGraphicType(true, false), "MA5");
|
||||
assert.equal(workflow.expectedGraphicType(false, true), "MA20");
|
||||
assert.equal(workflow.expectedGraphicType(true, true), "MA5,MA20");
|
||||
assert.throws(() => workflow.expectedGraphicType("yes", true));
|
||||
});
|
||||
|
||||
test("all five original candle entry sources can be recreated with global flags", () => {
|
||||
for (const entry of entries()) {
|
||||
const recreated = workflow.recreateCandleEntry(entry, true, true, adapters);
|
||||
assert.ok(recreated, entry.operator.source);
|
||||
assert.equal(recreated.selection.graphicType, "MA5,MA20");
|
||||
assert.deepEqual(recreated.operator.movingAverages, { ma5: true, ma20: true });
|
||||
assert.equal(recreated.enabled, entry.enabled);
|
||||
}
|
||||
});
|
||||
|
||||
test("playlist synchronization changes only s8010 entries and preserves order", () => {
|
||||
const candles = entries();
|
||||
const nonCandle = stock.createStockPlaylistEntry(domesticStock, "basic-current", {
|
||||
id: "non-candle",
|
||||
fadeDuration: 6,
|
||||
ma5: false,
|
||||
ma20: false
|
||||
});
|
||||
const input = [candles[0], nonCandle, ...candles.slice(1)];
|
||||
|
||||
const result = workflow.synchronizePlaylist(input, true, false, adapters);
|
||||
|
||||
assert.equal(result.valid, true);
|
||||
assert.equal(result.changed, true);
|
||||
assert.equal(result.playlist[1], nonCandle);
|
||||
assert.deepEqual(result.playlist.map(value => value.id), input.map(value => value.id));
|
||||
for (const entry of result.playlist.filter(value => value.builderKey === "s8010")) {
|
||||
assert.equal(entry.selection.graphicType, "MA5");
|
||||
}
|
||||
});
|
||||
|
||||
test("an untrusted s8010 entry blocks synchronization instead of being rewritten", () => {
|
||||
const untrusted = {
|
||||
id: "unknown-candle",
|
||||
builderKey: "s8010",
|
||||
code: "8051",
|
||||
enabled: true,
|
||||
fadeDuration: 6,
|
||||
selection: { graphicType: "" },
|
||||
operator: { source: "unknown" }
|
||||
};
|
||||
const result = workflow.synchronizePlaylist([untrusted], true, true, adapters);
|
||||
assert.equal(result.valid, false);
|
||||
assert.equal(result.changed, false);
|
||||
assert.deepEqual(result.invalidIds, ["unknown-candle"]);
|
||||
assert.equal(result.playlist[0], untrusted);
|
||||
});
|
||||
|
||||
test("a second synchronization with identical flags is stable", () => {
|
||||
const first = workflow.synchronizePlaylist(entries(), true, true, adapters);
|
||||
const second = workflow.synchronizePlaylist([...first.playlist], true, true, adapters);
|
||||
assert.equal(first.valid, true);
|
||||
assert.equal(second.valid, true);
|
||||
assert.equal(second.changed, false);
|
||||
second.playlist.forEach((entry, index) => assert.equal(entry, first.playlist[index]));
|
||||
});
|
||||
Reference in New Issue
Block a user