"use strict"; const test = require("node:test"); const assert = require("node:assert/strict"); const fixed = require("../../Web/legacy-fixed-workflow.js"); const industry = require("../../Web/industry-workflow.js"); const stock = require("../../Web/operator-workflow.js"); const comparison = require("../../Web/comparison-workflow.js"); const now = new Date("2026-07-11T12:00:00+09:00"); const kospiStock = Object.freeze({ market: "kospi", stockName: "삼성전자", displayName: "삼성전자", stockCode: "005930", isNxt: false }); const primaryIndustry = Object.freeze({ market: "kospi", code: "001", name: "전기전자" }); const secondaryIndustry = Object.freeze({ market: "kospi", code: "002", name: "금융업" }); const kosdaqPrimaryIndustry = Object.freeze({ market: "kosdaq", code: "101", name: "IT" }); const kosdaqSecondaryIndustry = Object.freeze({ market: "kosdaq", code: "102", name: "제약" }); const comparisonPair = Object.freeze([ Object.freeze({ kind: "krx-stock", market: "kospi", stockName: "삼성전자", stockCode: "005930" }), Object.freeze({ kind: "krx-stock", market: "kospi", stockName: "SK하이닉스", stockCode: "000660" }) ]); test("the original operator command matrix has exactly 412 visible slots", () => { assert.equal(fixed.actions.length, 328); assert.equal(industry.cuts.length * industry.markets.length, 44); assert.equal(stock.stockCuts.length, 31); assert.equal(comparison.actions.length, 9); assert.equal( fixed.actions.length + industry.cuts.length * industry.markets.length + stock.stockCuts.length + comparison.actions.length, 412); assert.equal(new Set(fixed.actions.map(value => value.id)).size, 328); assert.equal(new Set(industry.cuts.map(value => value.id)).size, 22); assert.equal(new Set(stock.stockCuts.map(value => value.id)).size, 31); assert.equal(new Set(comparison.actions.map(value => value.id)).size, 9); }); test("all 322 non-manual fixed slots create and strictly round-trip", () => { const available = fixed.actions.filter(value => value.available); assert.equal(available.length, 322); for (const [index, action] of available.entries()) { const created = fixed.createFixedPlaylistEntry(action.id, { id: `fixed-matrix-${index + 1}`, fadeDuration: 6, now, ma5: true, ma20: true }); const restored = fixed.restoreFixedPlaylistEntry(created, { now }); assert.ok(restored, `${action.id} ${action.label}`); assert.equal(restored.builderKey, action.builderKey); assert.equal(restored.code, action.code); } }); test("the six fixed manual placeholders resolve only through their dedicated editors", () => { const manual = fixed.actions.filter(value => !value.available); assert.deepEqual( manual.map(value => [value.id, value.label, value.builderKey, value.code]), [ ["fixed-245", "개인 순매도 상위(수동)", "s5025", "5025"], ["fixed-246", "외국인 순매도 상위(수동)", "s5025", "5025"], ["fixed-247", "기관 순매도 상위(수동)", "s5025", "5025"], ["fixed-262", "VI 발동(수동)", "s5074", "5074"], ["fixed-293", "VI 발동(수동)", "s5074", "5074"], ["fixed-320", "VI 발동(수동)", "s5074", "5074"] ]); for (const action of manual) { assert.throws(() => fixed.createFixedPlaylistEntry(action.id, { id: `blocked-${action.id}`, fadeDuration: 6, now }), /manual|수동|입력/i); } }); test("all 44 industry slots create and strictly round-trip with explicit selections", () => { let ordinal = 0; for (const market of industry.markets) { const selected = market === "kospi" ? primaryIndustry : kosdaqPrimaryIndustry; const pair = market === "kospi" ? [primaryIndustry, secondaryIndustry] : [kosdaqPrimaryIndustry, kosdaqSecondaryIndustry]; for (const cut of industry.cuts) { ordinal += 1; const created = industry.createIndustryPlaylistEntry(market, cut.id, { id: `industry-matrix-${ordinal}`, fadeDuration: 6, selected, pair, now, ma5: true, ma20: true }); const restored = industry.restoreIndustryPlaylistEntry(created, { now }); assert.ok(restored, `${market} ${cut.id}`); assert.equal(restored.builderKey, created.builderKey); assert.equal(restored.code, created.code); } } assert.equal(ordinal, 44); }); test("all 31 stock and nine comparison slots create and strictly round-trip", () => { for (const [index, cut] of stock.stockCuts.entries()) { const created = stock.createStockPlaylistEntry(kospiStock, cut.id, { id: `stock-matrix-${index + 1}`, fadeDuration: 6, ma5: true, ma20: true }); assert.ok(stock.restoreStockPlaylistEntry(created), cut.id); } for (const [index, action] of comparison.actions.entries()) { const created = comparison.createComparisonPlaylistEntry( action.id, comparisonPair, { id: `comparison-matrix-${index + 1}`, fadeDuration: 6 }); assert.ok(comparison.restoreComparisonPlaylistEntry(created), action.id); } }); test("the four GraphE buttons remain outside the 412 slots and map to exact INPUT tables", () => { assert.deepEqual( stock.manualStockActions.map(value => [value.id, value.builderKey, value.prerequisiteTable]), [ ["manual-revenue-mix", "s5076", "INPUT_PIE"], ["manual-growth-metrics", "s5079", "INPUT_GROW"], ["manual-sales", "s5080", "INPUT_SELL"], ["manual-operating-profit", "s5081", "INPUT_PROFIT"] ]); });