Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016VBGFXB9AbJBwXP19z75Qq
112 lines
4.7 KiB
Python
112 lines
4.7 KiB
Python
"""Z01 마스터 데이터 표 칸 — 숨김 열 · 열 제목(한글 label + 단위) · 칸 글자 · 쪽 수 (2026-09-15 브레인 Z01).
|
|
|
|
① 한글 이름은 API 가 `label` 로 줌 — 화면은 사전을 안 가짐(받은 대로 보임)
|
|
② `hidden` 열(내부 id · sha · 생성시각)은 기본 숨김 · 「숨긴 열 보기」면 보임
|
|
③ 단위가 있는 열의 수는 천 단위 쉼표(소수 자리는 자르지 않음) · 단위 없는 수(연도 등)는 그대로
|
|
④ 쪽 수는 줄이 없어도 1
|
|
⑤ columns 차례가 열 차례 — rows 에 columns 에 없는 열이 섞여도 안 그림 · columns 에 있는데
|
|
줄에 그 열이 없으면 「—」(값이 null 이면 빈칸) (2026-09-15 브레인)
|
|
⑥ 큰 표(40,000줄 = 800쪽) — 쪽 번호 칸에 적은 값은 1~끝 쪽으로 조임(끝 쪽으로 바로 감)
|
|
⑦ 검색·쪽 요청이 겹치면 최신 응답만 — 늦게 온 옛 응답·옛 오류는 버림 (2026-09-15 브레인)
|
|
TS 를 실제로 돌린다(`test_b06_berm_review_info` 와 같은 방식).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import subprocess
|
|
from pathlib import Path
|
|
|
|
PROJECT_ROOT = Path(__file__).resolve().parents[2]
|
|
TSC = PROJECT_ROOT / "config" / "node_modules" / "typescript" / "bin" / "tsc"
|
|
SOURCE = PROJECT_ROOT / "Z01_MasterData" / "Z01_MasterData_UI_Cells.ts"
|
|
|
|
_RUNNER = """
|
|
import { writeFileSync } from "node:fs";
|
|
import {
|
|
cellText, clampPage, columnTitle, latestOnly, pageCount, rowCells, shownColumns,
|
|
} from "./Z01_MasterData_UI_Cells.js";
|
|
|
|
const wait = (ms, value, fail) =>
|
|
new Promise((ok, no) => setTimeout(() => (fail ? no(new Error(value)) : ok(value)), ms));
|
|
const fetchLatest = latestOnly(wait);
|
|
// 옛 검색(느림) → 새 검색(빠름) · 옛 오류(느림) → 새 성공(빠름)
|
|
const race = await Promise.all([fetchLatest(60, "old"), fetchLatest(10, "new")]);
|
|
const raceError = await Promise.all([
|
|
fetchLatest(60, "old error", true).catch((e) => "thrown " + e.message),
|
|
fetchLatest(10, "new"),
|
|
]);
|
|
const latestError = await fetchLatest(5, "boom", true).catch((e) => "thrown " + e.message);
|
|
|
|
const columns = [
|
|
{ key: "id", label: "id", unit: null, hidden: true },
|
|
{ key: "occupation_name", label: "직종명", unit: null, hidden: false },
|
|
{ key: "daily_wage_krw", label: "일 노임", unit: "원", hidden: false },
|
|
];
|
|
writeFileSync(process.argv[2], JSON.stringify({
|
|
shown: shownColumns(columns, false).map((c) => c.key),
|
|
shownAll: shownColumns(columns, true).map((c) => c.key),
|
|
titles: columns.map(columnTitle),
|
|
cells: [
|
|
cellText(null, null),
|
|
cellText(250000, "원"),
|
|
cellText(1.23456, "㎥"),
|
|
cellText(2026, null),
|
|
cellText({ a: 1 }, null),
|
|
cellText(true, null),
|
|
cellText("보통인부", null),
|
|
],
|
|
pages: [pageCount(0, 50), pageCount(6999, 50), pageCount(50, 50)],
|
|
row: rowCells({ extra: "x", daily_wage_krw: 250000, occupation_name: null }, columns),
|
|
clamp: [clampPage(801, 800), clampPage(0, 800), clampPage(NaN, 800), clampPage(12.6, 800)],
|
|
race: race.map((v) => v ?? null),
|
|
raceError: raceError.map((v) => v ?? null),
|
|
latestError,
|
|
}));
|
|
"""
|
|
|
|
|
|
def test_칸_글자와_숨김_열(tmp_path: Path) -> None:
|
|
out = tmp_path / "js"
|
|
subprocess.run( # noqa: S603 — 고정 실행 파일
|
|
[
|
|
"node",
|
|
str(TSC),
|
|
str(SOURCE),
|
|
"--outDir",
|
|
str(out),
|
|
"--module",
|
|
"esnext",
|
|
"--target",
|
|
"es2022",
|
|
"--ignoreConfig",
|
|
"--noCheck",
|
|
"--noResolve",
|
|
],
|
|
cwd=str(PROJECT_ROOT),
|
|
check=True,
|
|
capture_output=True,
|
|
)
|
|
(out / "runner.mjs").write_text(_RUNNER, encoding="utf-8")
|
|
result = tmp_path / "output.json"
|
|
subprocess.run( # noqa: S603
|
|
["node", str(out / "runner.mjs"), str(result)],
|
|
cwd=str(PROJECT_ROOT),
|
|
check=True,
|
|
capture_output=True,
|
|
)
|
|
got = json.loads(result.read_text(encoding="utf-8"))
|
|
|
|
assert got["shown"] == ["occupation_name", "daily_wage_krw"]
|
|
assert got["shownAll"] == ["id", "occupation_name", "daily_wage_krw"]
|
|
assert got["titles"] == ["id", "직종명", "일 노임 (원)"]
|
|
assert got["cells"] == ["", "250,000", "1.23456", "2026", '{"a":1}', "예", "보통인부"]
|
|
assert got["pages"] == [1, 140, 1]
|
|
# columns 차례 · id 는 줄에 없어 「—」 · null 은 빈칸 · extra 는 안 그림
|
|
assert got["row"] == ["—", "", "250,000"]
|
|
assert got["clamp"] == [800, 1, 1, 13]
|
|
# 옛 응답은 늦게 와도 버림(null) · 옛 오류도 안 던짐 · 최신 오류는 던짐
|
|
assert got["race"] == [None, "new"]
|
|
assert got["raceError"] == [None, "new"]
|
|
assert got["latestError"] == "thrown boom"
|