Files
Aislo/resources/tester/test_m02_labor_calc_mirror.py
T
eomsangdonandClaude Opus 5.5 3028801cd8 feat(일위대가 규격 열): 문서 「규격」 칸 검사 · settle 열 끼우기 짝 · 열마다 반영합 (PLAN 704-4)
- M02_Labor_Spec 새 파일 — 규격 칸 정리 · 열 끼우기 · 사본 굳히기 · 쓰는 사본 찾기
- settle(파이썬 · TS) 에 col — 그 열 수량이 있는 줄 = 손 자리 · 규격 없음 = 그대로
- 계산값 규격합 = 열마다 반영합(고르기 창 열 단추)
- 거울 시험 — 규격 열 · 모르는 열 = 기본 · 꼴 틀림 400 · 열 없는 옛 문서

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 14:10:04 +09:00

153 lines
5.3 KiB
Python

"""일위대가 수량 · 반영 · 합 거울 시험 — `M02_Labor_Doc.settle`(파이썬) ↔
`M02_MasterTemplete_Labor_Calc.ts` `settle`(TS · Node) 가 같은 답(CLAUDE.md 5장 ①).
기준은 서버가 한 번 셈(`calc`) · 그 답에 다른 알약 값을 넣어 둘이 같은지 봄.
"""
import json
import os
import subprocess
import pytest
from M02_MasterTemplete import M02_Labor_Doc as labor
HERE = os.path.dirname(os.path.abspath(__file__))
TEN, THOUSAND, PLAIN, MAT = "GC000421", "GF000017", "GC000422", "MT000008"
def _ts(answer: dict, doc: dict, values: dict) -> dict:
proc = subprocess.run(
["node", os.path.join(HERE, "helper_m02_labor_calc.cjs")],
input=json.dumps({"답": answer, "문서": doc, "값": values}, ensure_ascii=False),
capture_output=True,
text=True,
encoding="utf-8",
check=True,
)
return json.loads(proc.stdout)
def _same(a, b) -> None:
if isinstance(a, dict):
assert set(a) == set(b)
for k in a:
_same(a[k], b[k])
elif isinstance(a, list):
assert len(a) == len(b)
for x, y in zip(a, b):
_same(x, y)
elif isinstance(a, (int, float)) and not isinstance(a, bool):
assert b == pytest.approx(a, rel=1e-9)
else:
assert a == b
@pytest.mark.parametrize(
"values",
[
{}, # 수량 없음 — 반영 null · 손만
{"사석 수상고르기|㎡": 2.5, f"자재|{MAT}": "3"},
{"사석 수상고르기|㎡": "1,200", "나무식재|본": 7, f"자재|{MAT}": "글"}, # 쉼표 · 수 아님
],
)
def test_파이썬과_TS_가_같은_답(values):
doc = labor.clean(
{
"열": "rv",
"담은로직": [
{"로직": TEN, "수량": None},
{"로직": THOUSAND, "수량": {"손": 4}},
{"로직": PLAIN, "수량": None},
],
"자재": [{"코드": MAT, "수량": None}],
}
)
answer = labor.calc(doc, {})
keys = {line["알약"] for line in answer["줄"]}
assert {"사석 수상고르기|㎡", "나무식재|본"} <= keys # 알약 키가 시험 값과 맞음
py = labor.settle(answer, doc, values)
_same(py, _ts(answer, doc, values))
assert py == labor.calc(doc, values) # settle 만 다시 = 처음부터 다시
# ── 규격 열(704-4) — 열마다 줄 수량 · 열 없는 옛 문서는 위 시험 그대로 ──
SPEC_DOC = {
"열": "rv",
"담은로직": [
{"로직": TEN, "수량": None},
{"로직": THOUSAND, "수량": {"손": 4}},
{"로직": PLAIN, "수량": None},
],
"자재": [{"코드": MAT, "수량": {"손": 1}, "별도계상": "직접계산"}],
"규격": {
"이름": "H",
"열": ["1.5", "2.0", "2.5"],
"기본": "2.0",
"수량": {
THOUSAND: {"1.5": 3, "2.0": "4.5", "2.5": 6},
MAT: {"1.5": 0.8, "2.5": 1.4}, # 2.0 빈 열 = 공통 손 1
"없는줄": {"1.5": 9}, # 버림
},
},
}
@pytest.mark.parametrize("col", [None, "1.5", "2.0", "2.5", "모름"])
def test_규격_열_파이썬과_TS_가_같은_답(col):
doc = labor.clean(SPEC_DOC)
assert doc["규격"]["수량"] == {
THOUSAND: {"1.5": 3, "2.0": 4.5, "2.5": 6},
MAT: {"1.5": 0.8, "2.5": 1.4},
}
answer = labor.calc(doc, {})
values = {"사석 수상고르기|㎡": 2.5}
py = labor.settle(answer, doc, values, col)
_same(py, _ts(answer, doc, values) if col is None else _ts_col(answer, doc, values, col))
use = col if col in ("1.5", "2.0", "2.5") else "2.0" # 모름 = 기본
line = next(x for x in py["줄"] if x["로직"] == THOUSAND)
assert line["수량"] == {"1.5": 3, "2.0": 4.5, "2.5": 6}[use] and line["수량출처"] == "손"
mat = py["자재"][0]
assert mat["수량"] == {"1.5": 0.8, "2.0": 1, "2.5": 1.4}[use]
def _ts_col(answer: dict, doc: dict, values: dict, col: str) -> dict:
proc = subprocess.run(
["node", os.path.join(HERE, "helper_m02_labor_calc.cjs")],
input=json.dumps({"답": answer, "문서": doc, "값": values, "열": col}, ensure_ascii=False),
capture_output=True,
text=True,
encoding="utf-8",
check=True,
)
return json.loads(proc.stdout)
def test_규격합_은_열마다_다시_센_반영합():
doc = labor.clean(SPEC_DOC)
out = labor.calc(doc, {"사석 수상고르기|㎡": 2.5})
sums = labor.spec_sums(doc, out)["규격합"]
assert list(sums) == ["1.5", "2.0", "2.5"]
for col, total in sums.items():
assert total == labor.settle(out, doc, {"사석 수상고르기|㎡": 2.5}, col)["반영합"]
assert sums["1.5"]["계"] < sums["2.0"]["계"] < sums["2.5"]["계"]
@pytest.mark.parametrize(
"bad",
[
{"이름": "", "열": ["1.5"]},
{"이름": "H", "열": ["1.5", "1.5"]},
{"이름": "H", "열": ["1.5"], "수량": {THOUSAND: {"1.5": "글"}}},
],
)
def test_규격_칸_꼴이_틀리면_400(bad):
with pytest.raises(labor.store.StoreError) as got:
labor.clean({**SPEC_DOC, "규격": bad})
assert got.value.status == 400
def test_열_없는_규격_은_옛_문서():
assert "규격" not in labor.clean({**SPEC_DOC, "규격": {"이름": "H", "열": []}})
assert "규격" not in labor.clean({k: v for k, v in SPEC_DOC.items() if k != "규격"})