From a8d7832c8b7bc38a74af37bd4727fe6ce2c20430 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Thu, 15 Oct 2026 12:52:42 +0900 Subject: [PATCH] =?UTF-8?q?test(=EC=9D=BC=EC=9C=84=EB=8C=80=EA=B0=80?= =?UTF-8?q?=EC=8B=A4=EB=AC=B4):=20329=20=C2=B7=20330=20=EC=9D=B4=EC=9D=8C?= =?UTF-8?q?=EB=A7=A4=20=E2=80=94=20=EC=8B=A4=EC=A0=9C=20=EA=B2=A9=EC=9E=90?= =?UTF-8?q?=20=C2=B7=20=EB=A7=9E=EC=B6=A4=20=EC=9D=91=EB=8B=B5=EC=9D=84=20?= =?UTF-8?q?=ED=99=94=EB=A9=B4=20=EB=8F=84=EC=9A=B0=EB=AF=B8(Hopyo=5FSheet?= =?UTF-8?q?=20=C2=B7=20Hopyo=5FView)=EC=97=90=20=EB=A8=B9=EC=9D=B4?= =?UTF-8?q?=EB=8A=94=20=EC=8B=9C=ED=97=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - helper_m02_hopyo_seam.cjs · test_m02_hopyo_seam — 행 → 호표(25 빈 줄) · 영역 B20:O24 · 단산93 행 · 엑셀 근거 줄 · 근거 꼬리표 · 근거 맞음 대상(GC000083 · GC000143) Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Ji4nuyLVHcxrXWHTdb5nRD --- resources/tester/helper_m02_hopyo_seam.cjs | 55 ++++++++++++++++++++++ resources/tester/test_m02_hopyo_seam.py | 47 ++++++++++++++++++ 2 files changed, 102 insertions(+) create mode 100644 resources/tester/helper_m02_hopyo_seam.cjs create mode 100644 resources/tester/test_m02_hopyo_seam.py diff --git a/resources/tester/helper_m02_hopyo_seam.cjs b/resources/tester/helper_m02_hopyo_seam.cjs new file mode 100644 index 000000000..faa75042a --- /dev/null +++ b/resources/tester/helper_m02_hopyo_seam.cjs @@ -0,0 +1,55 @@ +/* M02 일위대가 실무자료 이음매(329 · 330) 헬퍼 — 실제 서버 응답(격자 · 호표 맞춤)을 + * `M02_MasterTemplete_Hopyo_Sheet.ts`(행 → 호표 · 영역 A1) · `M02_MasterTemplete_Hopyo_View.ts` + * (근거 꼬리표 · 엑셀 근거 줄 · 근거 맞음 대상)에 먹임 · + * stdin = {grid, lines} · stdout = {at, span, rows, lines: [{이름, source, tag, ok, pick}]} JSON. */ +const fs = require("fs"); +const path = require("path"); +const ts = require(path.join(__dirname, "..", "..", "config", "node_modules", "typescript")); + +const ROOT = path.join(__dirname, "..", ".."); +const STUBS = { "@config/config_frontend": { API_BASE_URL: "/api" } }; +const STUB_FILES = ["M02_MasterTemplete_Labor_Pick", "M02_Material_Pick"]; +const stub = new Proxy({}, { get: (_t, k) => (k === "currentLanguageIndex" ? () => 0 : () => {}) }); +const cache = new Map(); +function loadTs(file) { + const full = path.resolve(file); + if (cache.has(full)) return cache.get(full); + const js = ts.transpileModule(fs.readFileSync(full, "utf8"), { + compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 }, + }).outputText; + const box = { exports: {} }; + cache.set(full, box.exports); + const localRequire = (request) => { + if (STUBS[request]) return STUBS[request]; + if (STUB_FILES.some((name) => request.endsWith(name))) return stub; + if (request.startsWith(".") && !request.endsWith(".css")) + return loadTs(`${path.resolve(path.dirname(full), request)}.ts`); + return stub; + }; + new Function("exports", "module", "require", js)(box.exports, box, localRequire); + return box.exports; +} + +const input = JSON.parse(fs.readFileSync(0, "utf8")); +const M02 = path.join(ROOT, "M02_MasterTemplete"); +const sheet = loadTs(path.join(M02, "M02_MasterTemplete_Hopyo_Sheet.ts")); +const view = loadTs(path.join(M02, "M02_MasterTemplete_Hopyo_View.ts")); +const { grid } = input; +const ilwi = grid.책.시트.find((s) => s.이름 === "일위대가"); +const three = grid.호표.find((h) => h.번 === 3); +const out = { + at: [19, 20, 22, 24, 25, 26].map((row) => sheet.hopyoAtRow(grid.호표, row)), + span: sheet.spanText(three.행, sheet.lastColumn(ilwi)), + rows: sheet.rowsText(grid.단산.find((b) => b.번 === 93).행), + lines: input.lines.map((line) => { + const top = (line.맞춤.후보 ?? [])[0]; + return { + 이름: line.이름, + source: view.sourceText(line.근거), + tag: view.basisTag(top?.근거), + ok: view.basisOk(top?.근거), + pick: view.basisPick(line)?.로직 ?? null, + }; + }), +}; +process.stdout.write(JSON.stringify(out)); diff --git a/resources/tester/test_m02_hopyo_seam.py b/resources/tester/test_m02_hopyo_seam.py new file mode 100644 index 000000000..0fd197766 --- /dev/null +++ b/resources/tester/test_m02_hopyo_seam.py @@ -0,0 +1,47 @@ +"""일위대가 실무자료 이음매(PLAN 329 · 330) — 실제 서버 응답(북부청 격자 · 호표 맞춤)을 +화면 도우미(`helper_m02_hopyo_seam.cjs` → Hopyo_Sheet · Hopyo_View)에 먹임. + +행 22 → 제3호표 · 25 빈 줄 · 26 → 제4호표 · 영역 B20:O24 · 단산93 행 4229:4244 · +엑셀 근거 줄 · 근거 꼬리표 · 근거 맞음 대상. +""" + +import json +import os +import subprocess + +from resources.tester import test_m02_hopyo_excel as base +from resources.tester.test_m02_hopyo_excel import URL, _post + +HERE = os.path.dirname(os.path.abspath(__file__)) +client = base.client # 공용 고정물(임시 층) + + +def _node(payload: dict) -> dict: + proc = subprocess.run( + ["node", os.path.join(HERE, "helper_m02_hopyo_seam.cjs")], + input=json.dumps(payload, ensure_ascii=False), + capture_output=True, + text=True, + encoding="utf-8", + check=False, + ) + assert proc.returncode == 0, proc.stderr + return json.loads(proc.stdout) + + +def test_실제_응답_화면_도우미(client): + assert _post(client).status_code == 200 + grid = client.get("/api/m02/layers/personal/hopyo-grid/HP-000001").json() + lines = [] + for no in (3, 12, 38, 58): + res = client.get(f"{URL.format('personal')}/HP-000001/{no}").json() + lines += [x for x in res["호표"]["줄"] if x.get("근거")] + got = _node({"grid": grid, "lines": lines}) + assert got["at"] == [None, 3, 3, 3, None, 4] # 25행 = 호표 사이 빈 줄 + assert got["span"] == "B20:O24" and got["rows"] == "4229:4244" + by = {x["이름"]: x for x in got["lines"]} + dig = by["구조물터파기(인력100%)"] + assert dig["source"] == "출처 건설품셈 3-2-1 · 직종 특별인부 · 단가산출 4229–4244행" + assert dig["tag"] == "조항 건설품셈 공통 3-2-1 · 직종 특별인부 · 단위 같음" + assert dig["ok"] is True and dig["pick"] == "GC000083" + assert by["비탈면 보호공"]["pick"] == "GC000143"