Files
Aislo/resources/tester/test_b08_pipe.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

173 lines
6.9 KiB
Python

"""배수관 물량 — 정본 셋을 잇는 자리의 짝 시험 (2026-09-08).
⚠ 여기서 겨누는 것 넷
① `facility` 가 있는 점(세월교 등)을 **관으로 세지 않는가**
② 연장이 없으면 **0 으로 때우지 않고 사유와 함께 막히는가**
③ 관종으로 공종이 갈리는가 · 기본값으로 선 것을 **알리는가**
④ ⚠ 좁게 — 측점 맞추기가 **옆 측점 길이를 물어 오지 않는가**
"""
from __future__ import annotations
import json
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(ROOT))
from B08_Quantity.B08_Quantity_Engine_Pipe import build_rows # noqa: E402
MAPPING = json.loads(
(
ROOT / "resources" / "data_work_item_mapping" / "work_item_mapping_2026-01-01.json"
).read_text(encoding="utf-8")
)["pipe"]
def (chainage: float, **options: object) -> dict:
return {"chainage_m": chainage, "facility": "pipe", "options": options}
def 길이(chainage: float, length: float) -> dict:
return {"chainage_m": chainage, "design": {"pipe_length_m": length}}
def test_관이_아닌_시설은_안_센다() -> None:
"""⚠ `pipe_points.json` 은 계곡 통과 시설 **전부**의 정본이다 — 세월교가 섞여 있다."""
points = [
(85.05, pipe_diameter_mm=1000),
{"chainage_m": 173.09, "facility": "ford_bridge", "options": {}},
{"chainage_m": 352.14, "facility": "ford_bridge", "options": {}},
(258.12, pipe_diameter_mm=800),
]
out = build_rows(points, [], MAPPING)
assert out["pipe_count"] == 2, "세월교를 관으로 셌다"
def test_관경이_없어도_관은_관이다() -> None:
"""⚠ `pipe_diameter_mm` 유무로 가르면 **관경 미지정 관을 놓친다**."""
out = build_rows([(100.0)], [], MAPPING)
assert out["pipe_count"] == 1
assert out["rows"][0]["variant_value"] is None
def test_연장이_없으면_0_으로_때우지_않는다() -> None:
out = build_rows([(85.05, pipe_diameter_mm=1000)], [], MAPPING)
row = out["rows"][0]
assert row["in_bill"] is False
assert row["blocked_kind"] == "input_missing"
assert "횡단설계에서 [저장]" in row["blocked_reason"]
def test_연장이_오면_값이_선다() -> None:
out = build_rows([(85.05, pipe_diameter_mm=1000)], [길이(85.05, 9.0)], MAPPING)
row = out["rows"][0]
assert (row["in_bill"], row["quantity"], row["unit"]) == (True, 9.0, "m")
assert out["length_total_m"] == 9.0
def test_관종으로_공종이_갈린다() -> None:
보기 = {"파형강관": "FP-12-11-03", "흄관": "FP-12-11-02", "VR관": "FP-12-11-01"}
for kind, code in 보기.items():
out = build_rows([(85.0, pipe_kind=kind, pipe_diameter_mm=800)], [길이(85.0, 9.0)], MAPPING)
assert out["rows"][0]["work_item_code"] == code, kind
def test_관종을_안_정하면_기본값으로_서되_알린다() -> None:
"""기본 「파형강관」은 2026-08-17 사용자 확정값이라 근거가 있다 — 다만 **조용히 쓰지 않는다**."""
out = build_rows([(85.0, pipe_diameter_mm=800)], [길이(85.0, 9.0)], MAPPING)
row = out["rows"][0]
assert row["work_item_code"] == "FP-12-11-03"
assert row["kind_from_default"] is True
assert any("기본값" in note for note in out["notes"])
def test_모르는_관종은_공종을_못_고른다() -> None:
out = build_rows([(85.0, pipe_kind="철근콘크리트관")], [길이(85.0, 9.0)], MAPPING)
row = out["rows"][0]
assert row["work_item_code"] is None
assert row["in_bill"] is False
assert "아는 관종이 아니라" in row["blocked_reason"]
def test_측점이_소수점에서_어긋나도_찾는다() -> None:
out = build_rows([(85.05, pipe_diameter_mm=800)], [길이(85.0, 9.0)], MAPPING)
assert out["rows"][0]["quantity"] == 9.0
def test_옆_측점_길이를_물어_오지_않는다() -> None:
"""⚠ 좁게 — 넓히면 다른 측점 값이 조용히 실린다."""
out = build_rows([(85.0, pipe_diameter_mm=800)], [길이(120.0, 9.0)], MAPPING)
row = out["rows"][0]
assert row["quantity"] == 0.0 and row["in_bill"] is False
def test_실제_자료로_세월교가_걸러진다() -> None:
"""정본 대조 — `5601e828` 은 11점 중 관 9 · 세월교 2 임(2026-09-08 랩탑 창 확인)."""
path = (
ROOT
/ "storage"
/ "1"
/ "3"
/ "5601e828-feea-487a-9b25-415e5199f2f5"
/ "B04_PreProcess"
/ "drainage"
/ "edits"
/ "pipe_points.json"
)
if not path.is_file():
return
points = json.loads(path.read_text(encoding="utf-8"))["points"]
assert build_rows(points, [], MAPPING)["pipe_count"] == 9
# ── 「[저장]하면 풀림」과 「눌러도 안 풀림」을 갈라 말하기 (2026-09-08 실측) ────
# 실측: `5601e828` 관 9개 중 3개(439.55·620.43·720.37)가 **횡단 행 자체가 없는** 자리였다.
# 거기에 「[저장]을 누르면 섭니다」를 띄우면 눌러 보고 안 되어 헤맨다.
def test_횡단이_있는데_길이만_없으면_저장하라고_말한다() -> None:
out = build_rows(
[(85.0, pipe_diameter_mm=800)],
[],
MAPPING,
section_chainages=[85.0],
)
assert "[저장]을 한 번 누르면" in out["rows"][0]["blocked_reason"]
def test_횡단_자체가_없으면_다르게_말한다() -> None:
out = build_rows(
[(620.43, pipe_diameter_mm=1500)],
[],
MAPPING,
section_chainages=[85.0, 258.12],
)
사유 = out["rows"][0]["blocked_reason"]
assert "횡단 자체가 없습니다" in 사유
assert "[저장]으로는 안 풀립니다" in 사유
def test_측점_목록을_안_주면_종전대로_말한다() -> None:
"""⚠ 좁게 — 목록이 없다고 「횡단 없음」으로 단정하면 거짓말이 된다."""
out = build_rows([(620.43, pipe_diameter_mm=1500)], [], MAPPING)
assert "[저장]을 한 번 누르면" in out["rows"][0]["blocked_reason"]
def test_0_5m_안의_측점은_그_관의_횡단이다() -> None:
"""⚠ **뒤집힌 시험이다** — 종전에는 「옆 측점이 있다고 횡단이 있는 것이 아니다」였다.
2026-09-09 랩탑 메인 실측이 그 전제를 깼다 — 측점은 **정수 미터 격자로 스냅**되므로
관 439.55 의 횡단은 **측점 440.0** 이고, 그 측점은 구조물 이름표까지 달고 있다.
좁게 보면 「그 측점의 횡단 자체가 없습니다」라는 **거짓 사유**가 뜬다. 스냅 최대
어긋남이 0.5m 라 길이 찾기와 허용오차가 같아졌다(`e1fed5f6` — 배수관 넷 복구).
"""
out = build_rows(
[(439.55, pipe_diameter_mm=800)],
[],
MAPPING,
section_chainages=[440.0],
)
assert "[저장]을 한 번 누르면" in out["rows"][0]["blocked_reason"]