Files
Aislo/resources/tester/test_b08_structure_trench_rows.py
T
eomsangdonandClaude Opus 5 d7cb14f416 test(B08): tmp/tests 중 tester 에 없던 47 개를 resources/tester 로 옮김
tmp/ 가 창끼리 안 건너가는 것이 확정돼(랩탑이 시간 두고 두 번 확인) 시험·예외가
저절로 건너가도록 git 안으로 옮김. 사용자 확정.

- 내용은 하나도 안 고침 — 자리만 옮김. tmp/tests 는 남겨 둠.
- 같은 이름이 이미 있던 64 개는 랩탑 것을 그대로 두고 건너뜀.
- helper_b05_*.js 둘은 랩탑이 .cjs 로 이미 올린 것과 **줄바꿈만 다른 같은 내용**이라
  복사본을 도로 뺌(시험이 .cjs 를 부름).
- resources/tester/ 에서 전체 1176 통과 · 29 건너뜀 · 실패 0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-09 17:15:57 +09:00

246 lines
9.6 KiB
Python

"""구조물 터파기·되메우기·잔토가 인계 축에 오르나 (2026-09-08).
⚠⚠ **빠뜨렸던 자리다.** 성분으로만 있고 아무도 안 받아 **내역서에 한 줄도 안 나갔다** —
두께 식·기초 몫을 아무리 맞춰도 금액이 0원이었다(B09 매김에서 드러남).
⚠ 겨누는 것 여섯
① 터파기가 **공종 줄로 선다**
② 심도로 갈린다 — 품셈 9-13 이 18구분(토질 3 × 육상/용수 × 심도 3)이라서
③ ⚠ 토질·용수를 **지어내지 않는다** — 상위 코드 + 사유
④ 되메우기는 코드가 붙고 **금액이 선다**
⑤ ⚠ 잔토는 **사토로 갈 몫**이라 내역에 안 서되 값과 사유가 실린다
⑥ 구조물이 없으면 줄도 없다
"""
from __future__ import annotations
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(ROOT))
from B08_Quantity.B08_Quantity_Engine_Handoff import build_handoff # noqa: E402
from B08_Quantity.B08_Quantity_Engine_UnitQuantity import build_table # noqa: E402
def 구조물(**options: object) -> dict:
return {
"structure_id": "s1",
"type_id": "masonry_wet",
"start_m": 0.0,
"end_m": 10.0,
"options": {"height_m": 2.5, "length_m": 10.0, "back_len_cm": 45, **options},
}
def (**options: object) -> dict:
unit = build_table([구조물(**options)])
rows = build_handoff(unit_quantity_table=unit)["work_items"]
return {row["name"]: row for row in rows}
def test_터파기가_공종_줄로_선다() -> None:
row = (foundation="기초유")["구조물터파기"]
assert row["unit"] == "㎥"
assert row["quantity"] > 0
assert row["work_item_code"] == "FP-09-13"
def test_심도로_갈린다() -> None:
"""직고 2.5 + 기초 0.5 = 3.0m → `2~3m` 칸. 기초를 안 고르면 2.5m 라 같은 칸이다."""
assert (foundation="기초유")["구조물터파기"]["spec"] == "심도 2~3m"
깊은 = build_table(
[
{
"structure_id": "s9",
"type_id": "masonry_wet",
"start_m": 0.0,
"end_m": 10.0,
"options": {
"height_m": 3.5,
"length_m": 10.0,
"back_len_cm": 45,
"foundation": "기초유",
},
}
]
)
rows = build_handoff(unit_quantity_table=깊은)["work_items"]
assert next(r for r in rows if r["name"] == "구조물터파기")["spec"] == "심도 3m 초과"
def test_토질을_못_가르면_그렇게_적는다() -> None:
"""⚠ 지반 갈래 표가 없으면 「토질을 못 가름」이다 — 토사로 눅이지 않는다."""
row = (foundation="기초유")["구조물터파기"]
assert row["blocked_kind"] == "input_missing"
assert "토질을 못 가름" in row["blocked_reason"]
assert row["ground_class"] is None
assert row["in_bill"] is False
def test_토질은_측점_설계값에서_온다() -> None:
"""⚠ 새 칸을 만들지 않는다 — 이미 저장되는 `design.ground_type` 을 읽는다."""
unit = build_table(
[구조물(foundation="기초유")],
None,
None,
{0.0: "ripping_rock", 5.0: "ripping_rock", 10.0: "ripping_rock"},
)
row = next(
r
for r in build_handoff(unit_quantity_table=unit)["work_items"]
if r["name"] == "구조물터파기"
)
assert row["ground_class"] == "암절취"
assert row["spec"] == "암절취 · 심도 2~3m"
# 남은 축은 용수 하나다 — 그것만 사유에 남는다.
assert "용수" in row["blocked_reason"] and "토질" not in row["blocked_reason"]
def test_걸친_측점이_섞이면_안_고른다() -> None:
"""⚠ 다수결로 고르면 임의값이 금액으로 굳는다 — 암 단가가 몇 배다."""
unit = build_table(
[구조물(foundation="기초유")],
None,
None,
{0.0: "soil", 5.0: "soil", 10.0: "blasting_rock"},
)
row = next(
r
for r in build_handoff(unit_quantity_table=unit)["work_items"]
if r["name"] == "구조물터파기"
)
assert row["ground_class"] is None
assert "섞여" in row["blocked_reason"]
assert "토사 2곳" in row["blocked_reason"] and "발파암 1곳" in row["blocked_reason"]
def test_되메우기는_금액이_선다() -> None:
row = (foundation="기초유")["되메우기"]
assert row["work_item_code"] == "FP-09-14-01"
assert row["in_bill"] is True
assert abs(row["quantity"] - (0.5 + 2.5) * 0.2 * 10.0) < 1e-9
def test_잔토는_사토로_갈_몫이라_내역에_안_선다() -> None:
"""⚠ 유토곡선이 세야 겹치지 않는다 — 다만 그 통로가 아직 없다는 사실을 사유로 남긴다."""
row = (foundation="기초유")["잔토처리"]
assert row["in_bill"] is False
# ⚠ 「통로가 없어 안 실림」이던 문구가 **바뀐 자리** — 2026-09-09 유토곡선이 그 값을
# 받아 사토에 얹고 「사토 운반」 줄로 세우게 됐다. 사유도 그 사실로 옮겼다.
assert "사토" in row["in_bill_reason"] and "사토 운반" in row["in_bill_reason"]
assert row["quantity"] > 0
def test_구조물이_없으면_줄도_없다() -> None:
rows = build_handoff(unit_quantity_table={"structures": []})["work_items"]
assert all(row["name"] not in {"구조물터파기", "되메우기", "잔토처리"} for row in rows)
def test_같은_토질_같은_심도는_한_줄이다() -> None:
"""⚠ 근거 문구가 측점마다 달라 **두 줄로 갈리던 자리**(2026-09-08 실화면).
사람이 보는 문구가 다르다는 이유로 내역 줄이 쪼개지면 같은 공종이 두 번 선다.
"""
unit = build_table(
[
{
"structure_id": "a",
"type_id": "masonry_wet",
"start_m": 0.0,
"end_m": 10.0,
"options": {
"height_m": 2.5,
"length_m": 10.0,
"back_len_cm": 45,
"foundation": "기초유",
},
},
{
"structure_id": "b",
"type_id": "masonry_wet",
"start_m": 100.0,
"end_m": 110.0,
"options": {
"height_m": 2.5,
"length_m": 10.0,
"back_len_cm": 45,
"foundation": "기초유",
},
},
],
None,
None,
{0.0: "soil", 10.0: "soil", 105.0: "soil"},
)
rows = [
r
for r in build_handoff(unit_quantity_table=unit)["work_items"]
if r["name"] == "구조물터파기"
]
assert len(rows) == 1
assert rows[0]["ground_class"] == "토사"
# ── 용수 축 · 18구분 (2026-09-09 사용자 확정 3차 ④) ──────────────────
def test_18구분_차례가_원문_그대로다() -> None:
"""⚠ 한 칸 밀리면 **다른 지반·다른 심도 단가**가 붙는다."""
from B08_Quantity.B08_Quantity_Engine_Handoff_Trench import trench_child_code
assert trench_child_code("FP-09-13", "soil", "육상", "0~1m") == "FP-09-13-01"
assert trench_child_code("FP-09-13", "soil", "용수", "0~1m") == "FP-09-13-04"
assert trench_child_code("FP-09-13", "ripping_rock", "육상", "0~1m") == "FP-09-13-07"
assert trench_child_code("FP-09-13", "ripping_rock", "용수", "2~3m") == "FP-09-13-12"
assert trench_child_code("FP-09-13", "blasting_rock", "용수", "2~3m") == "FP-09-13-18"
def test_축이_없으면_상위_코드로_둔다() -> None:
"""⚠ 3m 를 넘는 칸은 **원문에 없다** — 지어내지 않는다."""
from B08_Quantity.B08_Quantity_Engine_Handoff_Trench import trench_child_code
assert trench_child_code("FP-09-13", "soil", "", "0~1m") is None
assert trench_child_code("FP-09-13", None, "육상", "0~1m") is None
assert trench_child_code("FP-09-13", "soil", "육상", "3m 초과") is None
def 물줄(water: str | None, ground: str = "ripping_rock") -> dict:
unit = build_table(
[구조물(foundation="기초유")],
None,
None,
{0.0: ground, 10.0: ground},
)
rows = build_handoff(unit_quantity_table=unit, structure_trench_water=water)["work_items"]
return next(row for row in rows if row["name"] == "구조물터파기")
def test_용수를_정하면_자식_코드로_내려가_금액이_선다() -> None:
row = 물줄("육상")
assert row["work_item_code"] == "FP-09-13-09" # 육상 암절취 2~3m
assert row["in_bill"] is True and row["blocked_kind"] is None
def test_통상값이라는_사실이_줄에_적힌다() -> None:
"""⚠ 사용자 확정이 아니라 **통상값**이다 — 안 적으면 뒤집을 근거가 사라진다."""
assert "통상값" in 물줄("육상")["spec_detail"]
def test_용수로_뒤집으면_코드가_옮겨_간다() -> None:
assert 물줄("용수")["work_item_code"] == "FP-09-13-12"
def test_안_정하면_상위_코드로_막힌다() -> None:
row = 물줄(None)
assert row["work_item_code"] == "FP-09-13"
assert row["in_bill"] is False and "용수" in row["blocked_reason"]
def test_상태_사실이_값_옆에_적힌다() -> None:
"""⚠ 코드 주석에만 두면 화면에서 안 보인다 — 되메우기·잔토 줄에 함께 실린다."""
rows = (foundation="기초유")
for name in ("되메우기", "잔토처리"):
assert "다짐부피 ÷ C" in rows[name]["spec_detail"], name