Files
MBN_STOCK_WEBVIEW/tests/Web/named-comparison-restore-workflow.test.cjs

181 lines
6.5 KiB
JavaScript

"use strict";
const test = require("node:test");
const assert = require("node:assert/strict");
const workflow = require("../../Web/named-comparison-restore-workflow.js");
function raw(overrides = {}) {
return {
itemIndex: 0,
enabled: true,
groupCode: "코스피,코스닥",
subject: "삼성전자,에이비엘바이오",
graphicType: "종목별 비교분석_캔들 그래프",
subtype: "",
pageText: "1/1",
dataCode: "",
...overrides
};
}
function pending(value = raw(), id = "db-comparison-0") {
return workflow.classify(value, { id, fadeDuration: 6 });
}
function response(rows, requestId = "named-load-1") {
return {
requestId,
retrievedAt: "2026-07-12T12:00:00+09:00",
totalRowCount: rows.length,
rows
};
}
test("the exact original action grammar classifies all nine comparison leaves", () => {
const cases = [
["2열판", "", "two-column-current"],
["2열판", "예상체결", "two-column-expected"],
["종목별 비교분석_캔들 그래프", "", "comparison-candle"],
["종목별 비교분석_라인 그래프", "", "comparison-line"],
["종목별 수익률 비교", "5일", "return-5d"],
["종목별 수익률 비교", "1개월", "return-1m"],
["종목별 수익률 비교", "3개월", "return-3m"],
["종목별 수익률 비교", "6개월", "return-6m"],
["종목별 수익률 비교", "12개월", "return-12m"]
];
for (let index = 0; index < cases.length; index += 1) {
const [graphicType, subtype, actionId] = cases[index];
const value = pending(raw({
itemIndex: index,
groupCode: graphicType === "2열판" ? "지수" : "코스피,코스닥",
subject: graphicType === "2열판"
? "코스피 지수,코스닥 지수"
: "삼성전자,에이비엘바이오",
graphicType,
subtype
}), `comparison-${index}`);
assert.ok(value);
assert.equal(value.actionId, actionId);
}
});
test("market-lost stock rows are submitted but can only normalize as a failed outcome", () => {
const value = pending(raw({
groupCode: "종목",
subject: "삼성전자,엔비디아",
graphicType: "2열판"
}));
assert.ok(value);
const normalized = workflow.normalizeResponse(response([{
itemIndex: 0,
status: "failed",
failure: "MISSING_MARKET_IDENTITY"
}]), "named-load-1", [value]);
assert.equal(normalized.rows[0].failure, "MISSING_MARKET_IDENTITY");
assert.equal(workflow.materialize(value, normalized.rows[0]), null);
});
test("a correlated fixed-pair result materializes the explicit comparison selection", () => {
const value = pending(raw({
groupCode: "지수",
subject: "코스피 지수,코스닥 지수",
graphicType: "2열판"
}));
const normalized = workflow.normalizeResponse(response([{
itemIndex: 0,
status: "resolved",
actionId: "two-column-current",
first: { kind: "market-target", target: "Kospi" },
second: { kind: "market-target", target: "Kosdaq" }
}]), "named-load-1", [value]);
const entry = workflow.materialize(value, normalized.rows[0]);
assert.equal(entry.operator.source, "legacy-comparison-workflow");
assert.equal(entry.operator.actionId, "two-column-current");
assert.equal(entry.selection.subject, "Kospi,Kosdaq");
assert.equal(entry.builderKey, "s8018");
assert.equal(entry.enabled, true);
});
test("a correlated exact KRX result materializes candle and preserves disabled state", () => {
const value = pending(raw({ enabled: false }), "db-comparison-disabled");
const normalized = workflow.normalizeResponse(response([{
itemIndex: 0,
status: "resolved",
actionId: "comparison-candle",
first: {
kind: "krx-stock", market: "kospi", stockName: "삼성전자", stockCode: "005930"
},
second: {
kind: "krx-stock", market: "kosdaq", stockName: "에이비엘바이오", stockCode: "298380"
}
}]), "named-load-1", [value]);
const entry = workflow.materialize(value, normalized.rows[0]);
assert.equal(entry.builderKey, "s5026");
assert.equal(entry.operator.pair[0].stockCode, "005930");
assert.equal(entry.selection.dataCode, "KOSPI:005930|KOSDAQ:298380");
assert.equal(entry.enabled, false);
});
test("tampered correlation, ordering, action, target, and failure codes are rejected", () => {
const first = pending(raw(), "first");
const second = pending(raw({
itemIndex: 1,
graphicType: "종목별 비교분석_라인 그래프"
}), "second");
const validRows = [
{
itemIndex: 0,
status: "failed",
failure: "IDENTITY_NOT_FOUND"
},
{
itemIndex: 1,
status: "failed",
failure: "IDENTITY_AMBIGUOUS"
}
];
assert.ok(workflow.normalizeResponse(response(validRows), "named-load-1", [first, second]));
assert.equal(workflow.normalizeResponse(response(validRows, "stale"), "named-load-1", [first, second]), null);
assert.equal(workflow.normalizeResponse(response([...validRows].reverse()), "named-load-1", [first, second]), null);
assert.equal(workflow.normalizeResponse(response([
{ ...validRows[0], failure: "GUESS_MARKET" }, validRows[1]
]), "named-load-1", [first, second]), null);
const wrongAction = response([{
itemIndex: 0,
status: "resolved",
actionId: "comparison-line",
first: { kind: "krx-stock", market: "kospi", stockName: "삼성전자", stockCode: "005930" },
second: { kind: "krx-stock", market: "kosdaq", stockName: "에이비엘바이오", stockCode: "298380" }
}]);
assert.equal(workflow.normalizeResponse(wrongAction, "named-load-1", [first]), null);
});
test("malformed raw fields and retryable native errors are rejected", () => {
assert.equal(pending(raw({ pageText: "1/2" })), null);
assert.equal(pending(raw({ dataCode: "005930" })), null);
assert.equal(pending(raw({ subject: "삼성전자" })), null);
assert.equal(pending(raw({ subject: " 삼성전자,기아" })), null);
assert.equal(pending(raw({ subtype: "20일", graphicType: "종목별 수익률 비교" })), null);
assert.deepEqual(workflow.normalizeError({
requestId: "named-load-1",
code: "DATABASE_UNAVAILABLE",
message: "현재 DB를 사용할 수 없습니다.",
retryable: false
}, "named-load-1"), {
requestId: "named-load-1",
code: "DATABASE_UNAVAILABLE",
message: "현재 DB를 사용할 수 없습니다.",
retryable: false
});
assert.equal(workflow.normalizeError({
requestId: "named-load-1",
code: "DATABASE_UNAVAILABLE",
message: "현재 DB를 사용할 수 없습니다.",
retryable: true
}, "named-load-1"), null);
});