Files
Aislo/resources/tester/test_m02_labor_diagram.py
T
eomsangdonandClaude Opus 5.5 d847f724cf feat(M02): 일위대가 상세 부품 — 담은 로직 표 · 입력 모달 · 자재목록 표 · 다이어그램 calc 값 · 미리 보기 지움(39-3 C3 · C4 · C6)
- 새 Labor_Detail.ts(mountLaborDetail · 차례 = 정보 → 담은 로직 → 자재목록 → 다이어그램 · 고칠 때마다 calc 를 뒤에서 · 최신 답만 · 같은 로직 · 자재 두 번 막음)
- 새 Labor_Input.ts(담은 로직 줄 입력 모달 · 빈 칸 = 자동값 · 메모 · 수량 = 이은 칸 / 손 입력 / 면적산출에서 잇기 · 사라진 입력값 알림 · 자재 수량 모달)
- 새 Labor_Rows.ts(작은 글 「지역 서울 · 수량 B6 = 12.5㎡」 · 빈 값 n 붉게 · 금액 칸 넷 두 줄(위 기준 · 아래 반영) · 합계 줄 · 보기 갈래는 손잡이 없이)
- Labor_Api.ts: labor 문서 · calc 답 모양 · calcLabor · fetchLogicInputs · previewCombo 지움
- Labor_Diagram.ts: buildDiagramData → buildLaborDiagram(줄 입력값 + calc · 수량 있으면 반영 · 없으면 기준 표기 · 자재목록 카드 하나 → 재료비 · 알약 = 입력 모달)
- 미리 보기 지움: Labor_Preview.ts · M02 preview 길 · Combo_Store.preview · 옛 조합 상세의 미리 보기 · 다이어그램 칸
- 시험: test_m02_labor_diagram 새 모양 · test_m01_combo 미리 보기 시험은 같은 계산(combo_preview)을 바로 부름

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MgChuAcyBqUkhHMTXomQC7
2026-10-04 17:42:59 +09:00

133 lines
5.2 KiB
Python

"""일위대가 다이어그램 값 만들기(`Labor_Diagram.ts` 의 `buildLaborDiagram` · 39-3) — calc 줄 → 카드 출력.
화면 TS 를 그 자리에서 트랜스파일해 node 로 돌림 — 상대 경로 TS 는 이어 트랜스파일 · 화면 부품(@ui)은 빈 흉내 ·
금액 모양(Logic_Money)도 빈 흉내(이 시험은 값 만들기만 봄). 견본 줄은 이 시험 안에 둠.
"""
import json
import shutil
import subprocess
from pathlib import Path
import pytest
ROOT = Path(__file__).resolve().parents[2]
SOURCE = ROOT / "M02_MasterTemplete" / "M02_MasterTemplete_Labor_Diagram.ts"
RUNNER = r"""
const fs = require("fs");
const path = require("path");
const [root, file] = process.argv.slice(1);
const ts = require(path.join(root, "config", "node_modules", "typescript"));
const stub = new Proxy({}, { get: (_t, k) => (k === "currentLanguageIndex" ? 0 : () => undefined) });
const cache = {};
function load(f) {
if (cache[f]) return cache[f].exports;
const js = ts.transpileModule(fs.readFileSync(f, "utf8"), {
compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 },
}).outputText;
const box = (cache[f] = { exports: {} });
const req = (id) => {
if (id.startsWith(".") && !id.includes("M01_MasterData")) {
const p = path.resolve(path.dirname(f), id);
return fs.existsSync(p + ".ts") ? load(p + ".ts") : stub;
}
return stub;
};
new Function("exports", "module", "require", js)(box.exports, box, req);
return box.exports;
}
const m = load(file);
const jobs = JSON.parse(fs.readFileSync(0, "utf8"));
process.stdout.write(JSON.stringify(jobs.map((j) => m.buildLaborDiagram(j))));
"""
def run(jobs: list[dict]) -> list:
if shutil.which("node") is None:
pytest.skip("node 가 없음")
done = subprocess.run(
["node", "-e", RUNNER, str(ROOT), str(SOURCE)],
input=json.dumps(jobs, ensure_ascii=False),
capture_output=True,
text=True,
encoding="utf-8",
check=True,
)
return json.loads(done.stdout)
LOGICS = [
{
"로직": "GC000001",
"이름": "돌쌓기",
"결과단위": "원/㎡",
"입력": [{"이름": "높이", "단위": "m"}, {"이름": "지역", "자동값": "서울"}],
},
{"로직": "GF000002", "이름": "작업량", "결과단위": "㎥/시간", "입력": [{"이름": "지역"}]},
{"로직": "GC000003", "이름": "막힌 로직", "결과단위": "원/m", "입력": []},
]
ROWS = [
{"로직": "GC000001", "입력값": {"높이": 4}},
{"로직": "GF000002", "입력값": {"지역": "부산"}},
{"로직": "GC000003", "입력값": {}},
]
MONEY = {"노무비": 100, "재료비": 50, "경비": 0, "계": 150}
APPLIED = {"노무비": 300, "재료비": 150, "경비": 0, "계": 450}
def _line(key: str, **more) -> dict:
return {"로직": key, "기준": None, "반영": None, "까닭": None, **more}
def test_돈_값_멈춤_세_갈래_반영이_있으면_반영():
calc = {
"줄": [
_line("GC000001", 기준=MONEY, 반영=APPLIED),
_line("GF000002", 결과=8.946),
_line("GC000003", 까닭="표에 맞는 줄이 없음"),
],
"자재": [],
}
(got,) = run([{"rows": ROWS, "logics": LOGICS, "calc": calc, "mats": 0}])
outs = [x["out"] for x in got["logics"]]
assert outs[0] == APPLIED and got["logics"][0]["basis"] == "반영"
assert outs[1] == {"값": 8.946}
assert outs[2] == {"멈춤": "표에 맞는 줄이 없음"}
def test_수량_없으면_기준():
calc = {"줄": [_line("GC000001", 기준=MONEY)], "자재": []}
(got,) = run([{"rows": ROWS[:1], "logics": LOGICS, "calc": calc, "mats": 0}])
assert got["logics"][0]["out"] == MONEY and got["logics"][0]["basis"] == "기준"
def test_넣은_값은_로직별_빈_값은_자동값_또는_빈_글():
(got,) = run([{"rows": ROWS, "logics": LOGICS, "calc": None, "mats": 0}])
first, second, third = got["logics"]
assert [(i["name"], i["value"]) for i in first["inputs"]] == [("높이", "4"), ("지역", "서울")]
assert first["inputs"][1].get("auto") is True and "auto" not in first["inputs"][0]
assert first["inputs"][0]["unit"] == "m"
assert [(i["name"], i["value"]) for i in second["inputs"]] == [("지역", "부산")]
assert third["inputs"] == []
def test_아직_안_셌으면_출력_없음():
(got,) = run([{"rows": ROWS, "logics": LOGICS, "calc": None, "mats": 0}])
assert [x["out"] for x in got["logics"]] == [None, None, None]
assert [x["key"] for x in got["logics"]] == ["GC000001", "GF000002", "GC000003"]
def test_자재목록은_카드_하나_재료비로():
calc = {
"줄": [_line("GC000001", 기준=MONEY)],
"자재": [{"기준단가": 1000, "반영": 5000}, {"기준단가": 200, "반영": None}],
}
(got,) = run([{"rows": ROWS[:1], "logics": LOGICS, "calc": calc, "mats": 2}])
card = got["logics"][-1]
assert card["material"] is True and card["inputs"] == []
assert card["out"] == {"노무비": 0, "재료비": 5200, "경비": 0, "계": 5200}
assert card["basis"] == "기준" # 반영 없는 자재가 있음
(none,) = run([{"rows": ROWS[:1], "logics": LOGICS, "calc": calc, "mats": 0}])
assert not any(x.get("material") for x in none["logics"])