Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016VBGFXB9AbJBwXP19z75Qq
109 lines
3.9 KiB
Python
109 lines
3.9 KiB
Python
"""사면길이 3m 초과 — 소단 **검토 대상 정보 한 줄**(경고 아님 · 2026-09-15 브레인 ③).
|
|
|
|
① 법령 별표2 Ⅰ.2.차.(5) 「붕괴 또는 밀려 내려갈 **우려가 있는 지역**에는 사면길이 2~3m 마다」 —
|
|
「우려 있음」을 우리가 판정 못 함 ⇒ 경고가 아니라 정보
|
|
② 3m 를 **넘는** 쪽만(3.00 은 아님) · 절토·성토 둘 다 · 원지반을 못 만난 하한값(≥)도 넘으면 들어감
|
|
③ 벽이 선 쪽은 뺌(성토사면 5m 경고와 같은 규칙 · `wallSides`)
|
|
④ 문구는 브레인 문구 그대로
|
|
TS 를 실제로 돌린다(파이썬 짝이 없는 화면 판정 — `test_b06_fill_slope_warn` 과 같은 방식).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import re
|
|
import subprocess
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
PROJECT_ROOT = Path(__file__).resolve().parents[2]
|
|
TSC = PROJECT_ROOT / "config" / "node_modules" / "typescript" / "bin" / "tsc"
|
|
FOLDER = PROJECT_ROOT / "B06_Section"
|
|
SOURCES = [
|
|
FOLDER / "B06_Section_UI_Cross_Berm_Info.ts",
|
|
FOLDER / "B06_Section_UI_Cross_FillSlope_Warn.ts",
|
|
FOLDER / "B06_Section_UI_Cross_Culvert_Const.ts",
|
|
]
|
|
|
|
_RUNNER = """
|
|
import { readFileSync, writeFileSync } from "node:fs";
|
|
import { BERM_INFO_TEXT, bermReviewStations } from "./B06_Section_UI_Cross_Berm_Info.js";
|
|
|
|
const [inputPath, outputPath] = process.argv.slice(2);
|
|
const cases = JSON.parse(readFileSync(inputPath, "utf8"));
|
|
const rows = bermReviewStations(cases.map((c) => c.section), (section) =>
|
|
cases.find((c) => c.section.station_id === section.station_id).lengths,
|
|
);
|
|
writeFileSync(outputPath, JSON.stringify({
|
|
text: BERM_INFO_TEXT,
|
|
rows: rows.map((w) => [w.section.station_id, w.sides.map((s) => s.side)]),
|
|
}));
|
|
"""
|
|
|
|
LONG = {"lengthM": 4.0, "open": False}
|
|
|
|
CASES = [
|
|
{"section": {"station_id": "both"}, "lengths": {"left": LONG, "right": LONG}},
|
|
{
|
|
"section": {"station_id": "edge"},
|
|
"lengths": {
|
|
"left": {"lengthM": 3.0, "open": False},
|
|
"right": {"lengthM": 2.5, "open": True},
|
|
},
|
|
},
|
|
{
|
|
"section": {"station_id": "open_long"},
|
|
"lengths": {"left": None, "right": {"lengthM": 3.4, "open": True}},
|
|
},
|
|
{"section": {"station_id": "ford", "ford": {}}, "lengths": {"left": LONG, "right": LONG}},
|
|
]
|
|
|
|
|
|
def _run(tmp_path: Path) -> dict:
|
|
out = tmp_path / "js"
|
|
subprocess.run( # noqa: S603 — 고정 실행 파일
|
|
[
|
|
"node",
|
|
str(TSC),
|
|
*map(str, SOURCES),
|
|
"--outDir",
|
|
str(out),
|
|
"--module",
|
|
"esnext",
|
|
"--target",
|
|
"es2022",
|
|
"--moduleResolution",
|
|
"bundler",
|
|
"--ignoreConfig",
|
|
"--noCheck",
|
|
"--noResolve",
|
|
],
|
|
cwd=str(PROJECT_ROOT),
|
|
check=True,
|
|
capture_output=True,
|
|
)
|
|
for emitted in out.glob("*.js"):
|
|
text = emitted.read_text(encoding="utf-8")
|
|
emitted.write_text(
|
|
re.sub(r'(from "\./[^"]+?)(")', lambda m: m.group(1) + ".js" + m.group(2), text),
|
|
encoding="utf-8",
|
|
)
|
|
(out / "runner.mjs").write_text(_RUNNER, encoding="utf-8")
|
|
payload, result = tmp_path / "input.json", tmp_path / "output.json"
|
|
payload.write_text(json.dumps(CASES, ensure_ascii=False), encoding="utf-8")
|
|
subprocess.run( # noqa: S603
|
|
["node", str(out / "runner.mjs"), str(payload), str(result)],
|
|
cwd=str(PROJECT_ROOT),
|
|
check=True,
|
|
capture_output=True,
|
|
)
|
|
return json.loads(result.read_text(encoding="utf-8"))
|
|
|
|
|
|
@pytest.mark.skipif(not TSC.is_file(), reason="프론트엔드 의존성 미설치")
|
|
def test_3m_넘는_사면만_벽_선_쪽은_뺀다(tmp_path: Path) -> None:
|
|
got = _run(tmp_path)
|
|
assert dict(got["rows"]) == {"both": ["left", "right"], "open_long": ["right"]}
|
|
assert got["text"] == "사면길이 3m 초과 — 소단 검토 대상(별표2 차.(5) · 붕괴 우려 지역 조건)"
|