⚠ **뿌리** — `tmp/` 는 창 사이에 안 건너감(실측 확인: 상대 창이 놓은 `tmp/_sync_probe.txt` 가 시간을 두고 두 번 봐도 안 보임). 그래서 **정본(등록부 스키마)만 건너가고 그것을 읽는 시험은 안 건너가** 오늘 두 번, 같은 시험이 **연 창은 통과·받은 창은 실패**가 됐음. - `tmp/tests/*` 를 `resources/tester/` 로 **복사**(127 파일). 내용은 **한 줄도 안 고침** - `tmp/tests` 는 **남겨 둠** — 되돌릴 자리(사용자 지시) - 실행: `./venv/Scripts/python.exe -m pytest resources/tester/ -q` 옮기기 전과 **같은 수**: 617 통과 / 22 건너뜀 / 실패 0 ⇒ 이제 시험·예외·까닭이 **정본과 함께** 움직임. 오늘 세운 「예외는 정본 스키마에」와 짝임. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
89 lines
3.1 KiB
Python
89 lines
3.1 KiB
Python
"""사토장이 서면 사토 운반거리가 **측점에서 나온다** (2026-09-09 사용자 확정 ③).
|
|
|
|
「기존 구조물 측점이나 규칙 측점에만 들어가는 게 맞아」 ⇒ 사토장 자리가 측점이라
|
|
거리는 **발생점 → 사토장 측점** 누가거리로 그냥 나온다. 가정할 것이 없다.
|
|
|
|
⚠ 사토장이 없으면 설계 입력값으로 되돌아간다 — **임의 거리를 넣지 않는다**.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
PROJECT_ROOT = Path(__file__).resolve().parents[2]
|
|
if str(PROJECT_ROOT) not in sys.path:
|
|
sys.path.insert(0, str(PROJECT_ROOT))
|
|
|
|
from B08_Quantity.B08_Quantity_Router_Earthwork import ( # noqa: E402
|
|
_spoil_of,
|
|
_spoil_sites,
|
|
)
|
|
|
|
_PLAN = {
|
|
"spoil_m3": 300.0,
|
|
"natural_spoil_m3": 0.0,
|
|
"residuals": [
|
|
{"kind": "spoil", "from_m": 0.0, "to_m": 40.0, "volume_m3": 100.0, "natural_m3": 0.0},
|
|
{"kind": "spoil", "from_m": 200.0, "to_m": 240.0, "volume_m3": 200.0, "natural_m3": 0.0},
|
|
],
|
|
}
|
|
|
|
|
|
def _designs(*, extra: float | None = None) -> list[dict]:
|
|
rows = []
|
|
for chainage in (100.0, 120.0, 140.0):
|
|
rows.append(
|
|
{
|
|
"chainage_m": chainage,
|
|
"design": {
|
|
"spoil_fill_area_m2": 12.0,
|
|
"spoil_fill_structure_id": "sp-1",
|
|
"spoil_fill_capacity_m3": 500.0,
|
|
"spoil_fill_placed_m3": 480.0,
|
|
"spoil_fill_unplaced_m3": 20.0,
|
|
"spoil_fill_extra_distance_m": extra,
|
|
},
|
|
}
|
|
)
|
|
return rows
|
|
|
|
|
|
def test_사토장_구간을_한_덩어리로_접는다() -> None:
|
|
sites = _spoil_sites(_designs())
|
|
assert len(sites) == 1
|
|
assert sites[0]["from_m"] == 100.0
|
|
assert sites[0]["to_m"] == 140.0
|
|
assert sites[0]["center_m"] == 120.0
|
|
assert sites[0]["placed_m3"] == 480.0
|
|
|
|
|
|
def test_거리는_발생점에서_사토장_측점까지다() -> None:
|
|
spoil = _spoil_of(_PLAN, {}, _spoil_sites(_designs()))
|
|
# 발생점 20m(100㎥) → 100m · 발생점 220m(200㎥) → 100m ⇒ 가중평균 100m
|
|
assert spoil["distance_m"] == pytest.approx(100.0)
|
|
assert spoil["distance_basis"] == "사토장(측점) 기준"
|
|
assert "사토장 측점" in spoil["note"]
|
|
|
|
|
|
def test_추가_운반거리는_항상_더한다() -> None:
|
|
spoil = _spoil_of(_PLAN, {}, _spoil_sites(_designs(extra=50.0)))
|
|
assert spoil["distance_m"] == pytest.approx(150.0)
|
|
|
|
|
|
def test_못_담는_몫을_사유로_드러낸다() -> None:
|
|
spoil = _spoil_of(_PLAN, {}, _spoil_sites(_designs()))
|
|
assert "못 담는" in spoil["note"]
|
|
assert spoil["sites"][0]["unplaced_m3"] == 20.0
|
|
|
|
|
|
def test_사토장이_없으면_설계_입력값으로_돌아간다() -> None:
|
|
"""임의 거리를 넣지 않는다 — 값이 없으면 막히는 것이 맞다."""
|
|
spoil = _spoil_of(_PLAN, {"spoil_site_distance_m": 900.0}, [])
|
|
assert spoil["distance_m"] == 900.0
|
|
assert spoil["distance_basis"] == "설계 입력값"
|
|
blocked = _spoil_of(_PLAN, {}, [])
|
|
assert blocked["distance_m"] is None
|