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>
173 lines
6.9 KiB
Python
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"]
|