Files
Aislo/resources/tester/test_472_1_golden.py
T
eomsangdonandClaude Opus 5.5 3a64f9a812 feat(472-4 ② 횡단 짝 모으기): [저장] · [확정] 횡단 설계선 · 면적 · 소단 구간을 Node 입구 한 갈래로(C1 · C2 · C4 · X3 풀림)
- Router_Design enforce 셋 · 넓힘 뒤 설계 — 파이썬은 입력 조립만 · 측점 묶음을 Node 한 번(B06_Section_Engine_Design_Node · cross.design) · Node 못 부르면 파이썬 짝
- 소단 구간 셈을 화면 파일에서 common_util_berm_spans.ts 로 · 서버도 같은 TS(cross.berm_spans) · Persist 가 다시 내보냄
- 값: 고정 자료 기준값 0 차(면적 · 계획고 · 누가토량 −17,570.7) · 설계선 점 13 곳 1e-4 m(반올림 동점 · 브레인 승인) — 기준값 시험에 판정 더함
- 시험: compute_cross_design 갈아 끼우던 시험 다섯을 묶음 자리로(까닭: [저장] 다시 셈이 묶음으로 부름) · 소단 글 시험 자리 옮김
- 등록부 · 위키 계산 목록 · B06 지침 줄

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 09:21:42 +09:00

169 lines
6.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""PLAN 472-1 (요구 273) — B05 · B06 기준값 시험 · 구조 다시 세움 단계마다의 문.
고정 자료 `fixtures/golden_472/`(검증_랩탑메인 노선 370 · 읽기만 뜸) 하나로 길 넷의 값을 떠
`golden_472_values.json` 과 견줌(값 꼴 · 길 = `helper_472_1_paths.py` 머리).
못박는 것
- 길마다 자기 값과 0 차 — 측점(수 · 열쇠 · 누가거리 · x · y) · 계획고 ·
면적(절 · 성 · 토사 · 암 · 측구) · 층따기 밑수 · 단면 모양 · 누가토량(측점별 · 끝값) ·
관 길이 · 종단 경고 · 기울기 위반 수.
- 두 길 사이 = 「알려진 차」(`KNOWN`)만 · 그 밖 차는 실패 · 안 나타난 알려진 차도 실패
(목록에서 지움 — 472-4 가 짝을 하나 옮길 때마다 한 줄씩 지워 마지막에 빈 목록).
- 화면 참값 — 정본 누가토량 −17,570.70 ㎥ · 측점 57(465 기준값).
이 고정 자료가 안 거치는 짝인데 값이 갈리는 자리 — X1 단면 모양 기본값(설계 없는 새 측점 없음) ·
X3 면적 두 갈래(구조물 = 관 351 하나 · 두 길 다 Node TS 로 셈) · X4 · X6(노선 안 바뀜).
값 다시 뜨기 = `helper_472_1_dump.py`(승인 때만 · 시험은 다시 안 뜸).
./venv/Scripts/python.exe -m pytest resources/tester/test_472_1_golden.py -q
"""
from __future__ import annotations
import copy
import json
from dataclasses import dataclass
import helper_472_1_paths as paths
import pytest
pytestmark = pytest.mark.skipif(
not (paths.ROOT / "config" / "node_modules" / "vite").is_dir(),
reason="프론트엔드 의존성 미설치",
)
GOLDEN = json.loads(paths.VALUES.read_text(encoding="utf-8"))
AREAS = ("cut_m2", "fill_m2", "cut_soil_m2", "cut_rock_m2", "ditch_m2")
@dataclass(frozen=True)
class Known:
"""알려진 차 한 줄 — 칸 → 허용 폭(None = 같지 않아도 됨 · 글 칸) · 까닭."""
id: str
fields: dict[str, float | None]
why: str
KNOWN: dict[tuple[str, str], list[Known]] = {
# [저장] 재계산 = 정본 그대로(사용자 원칙 — 입력 안 바뀌면 값 안 바뀜) · 경고만 지금 코드
("server_save", "stored"): [
Known(
"W0",
{"warnings": None},
"정본 종단 경고 = 10-17 저장 때 코드(법정 R · L 미달 46 줄 전) · "
"[저장] 재계산이 지금 코드로 다시 씀(472 덧 · 기하 그대로)",
),
],
("server_route", "stored"): [
Known(
"R7",
{"fill_m2": 0.00011, "cumulative_m3": 0.011},
"다시 뜬 표본 마지막 자리 — grid:37 성토 1e-4 ㎡ · 누가토량 0.01 ㎥ 넷(끝값 같음)",
),
Known(
"W0",
{"warnings": None},
"정본 종단 경고 = 10-17 저장 때 코드(법정 R · L 미달 46 줄 전) · "
"노선 저장 체인은 지금 코드",
),
],
("browser", "server_route"): [
Known(
"X2",
{
"x": 0.002,
"y": 0.002,
**dict.fromkeys(AREAS, 0.1),
"bench_m": 1.0,
"cumulative_m3": 2.0,
"final_cumulative_m3": 1.0,
},
"입력 선 — 브라우저 /route/plan planned(0.1 mm) · 서버 노선 geojson(1 mm) · "
"측점 자리 ≤ 1 mm → 표본 · 면적 ≤ 0.07 ㎡ · 층따기 밑수 ≤ 0.52 m · "
"누가토량 끝값 0.69 ㎥(−17,570.01 ↔ −17,570.70)",
),
],
}
@pytest.fixture(scope="module")
def got(tmp_path_factory) -> dict:
out = {}
for name in paths.PATHS:
out[name] = paths.run_path(name, tmp_path_factory.mktemp(name))
return out
def diffs(a: dict, b: dict) -> list[tuple[str, str, object, object]]:
"""(칸, 측점 열쇠, a, b) — 측점은 같은 차례(수가 다르면 측점 수 한 줄)."""
out = []
for field in ("station_count", "final_cumulative_m3", "pipe_lengths", "warnings", "violations"):
if a[field] != b[field]:
out.append((field, "-", a[field], b[field]))
if a["station_count"] == b["station_count"]:
for x, y in zip(a["stations"], b["stations"], strict=True):
out += [(k, x["key"], x[k], y[k]) for k in x if x[k] != y[k]]
return out
def allowed(diff: tuple, known: Known) -> bool:
field, _key, a, b = diff
if field not in known.fields:
return False
width = known.fields[field]
if width is None:
return True
return isinstance(a, (int, float)) and isinstance(b, (int, float)) and abs(a - b) <= width
@pytest.mark.parametrize("name", paths.PATHS)
def test_길마다_기준값_그대로(got, name) -> None:
found = diffs(got[name], GOLDEN[name])
assert not found, f"{name} 기준값과 다름 {len(found)}곳 — 앞 10: {found[:10]}"
@pytest.mark.parametrize("pair", list(KNOWN), ids=lambda pair: f"{pair[0]}-{pair[1]}")
def test_두_길_사이_알려진_차만(got, pair) -> None:
known = KNOWN[pair]
found = diffs(got[pair[0]], got[pair[1]])
stray = [d for d in found if not any(allowed(d, k) for k in known)]
assert not stray, f"알려진 차 밖 {len(stray)}곳 — 앞 10: {stray[:10]}"
gone = [k.id for k in known if not any(allowed(d, k) for d in found)]
assert not gone, f"안 나타난 알려진 차 {gone} — KNOWN 에서 지움"
def test_저장_설계선_점_정본과_1e4_안(tmp_path) -> None:
"""[저장] 다시 셈 설계선(472-4 ② = Node TS) — 정본과 점 수 · offset 같음 · 고도 1e-4 m 안.
알려진 차 = 반올림 동점(파이썬 round 짝수 쪽 · TS 위쪽) 13 점(브레인 10-19 승인).
"""
detail = copy.deepcopy(paths.read("detail.json"))
before = {s["chainage_m"]: s["design"]["design_line"] for s in detail["cross_sections"]}
sections, _ = paths._save_tail(detail, paths.stage_project(tmp_path / "project"))
moved = 0
for section in sections:
old, new = before[section["chainage_m"]], section["design"]["design_line"]
assert [p["offset_m"] for p in new] == [p["offset_m"] for p in old], section["chainage_m"]
for a, b in zip(old, new, strict=True):
gap = abs(a["elevation_m"] - b["elevation_m"])
assert gap <= 1e-4 + 1e-9, (section["chainage_m"], a, b)
moved += gap > 0
assert moved == 13, f"반올림 동점 점 수 {moved} — 알려진 차(13)와 다름"
def test_화면_참값() -> None:
stored = GOLDEN["stored"]
assert stored["final_cumulative_m3"] == -17570.7
assert stored["station_count"] == 57
assert stored["pipe_lengths"] == {"pipe:p35100": 7.9}
def test_고정_자료_크기() -> None:
size = sum(p.stat().st_size for p in paths.FIXTURE.rglob("*") if p.is_file())
assert size < 5e6
@pytest.mark.skip(reason="API 왕복(목록 4.2 ③ 선택) — 472-1 에서 건너뜀")
def test_API_왕복() -> None:
raise AssertionError