181 lines
6.5 KiB
JavaScript
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);
|
|
});
|