⚠ **뿌리** — `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>
142 lines
4.9 KiB
Python
142 lines
4.9 KiB
Python
"""터파기 단면 — 파이썬·TS 짝이 같은 값을 내는지 (2026-09-09).
|
|
|
|
화면(B06 횡단도)이 그리고 서버(B08 수량)가 세는 값이라 두 쪽이 갈리면
|
|
「그림은 이런데 수량은 저렇다」가 된다.
|
|
|
|
⚠ 근거의 급이 다른 두 갈래를 함께 잠근다 —
|
|
관은 **법정 표**(KCS 44 40 10 그림 3.2-1), 벽은 **실무 정본 식**(구조도 기슭막이 xls).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import re
|
|
import subprocess
|
|
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 common_util.common_util_excavation import ( # noqa: E402
|
|
PIPE_TRENCH_WIDTH_MM,
|
|
WALL_FOUNDATION_DEPTH_M,
|
|
WALL_FOUNDATION_WIDTH_M,
|
|
WALL_TRENCH_CLEARANCE_M,
|
|
pipe_trench_width_m,
|
|
wall_trench_area_m2,
|
|
wall_trench_width_m,
|
|
)
|
|
|
|
TSC = PROJECT_ROOT / "config" / "node_modules" / "typescript" / "bin" / "tsc"
|
|
|
|
_RUNNER = """
|
|
import { writeFileSync } from "node:fs";
|
|
import {
|
|
PIPE_TRENCH_WIDTH_MM,
|
|
pipeTrenchWidthM,
|
|
wallTrenchAreaM2,
|
|
wallTrenchWidthM,
|
|
} from "./common_util_excavation.js";
|
|
|
|
const out = {
|
|
table: PIPE_TRENCH_WIDTH_MM,
|
|
pipe: [300, 800, 1000, 1200, 1500, 250, 1600].map((d) => pipeTrenchWidthM(d)),
|
|
wallWithFoundation: [1.0, 2.0, 3.0].map((h) => wallTrenchAreaM2(h, 0.7, true)),
|
|
wallBlindingOnly: [1.0, 2.0, 3.0].map((h) => wallTrenchAreaM2(h, 0.7, false)),
|
|
wallWidth: [0.7, 1.05, 0].map((t) => wallTrenchWidthM(t)),
|
|
nulls: [pipeTrenchWidthM(null), wallTrenchAreaM2(null, 0.7, true), wallTrenchAreaM2(1, null, true)],
|
|
};
|
|
writeFileSync(process.argv[2], JSON.stringify(out));
|
|
"""
|
|
|
|
|
|
def _ts_values(tmp_path: Path) -> dict:
|
|
out = tmp_path / "js"
|
|
subprocess.run( # noqa: S603 — 고정 실행 파일
|
|
[
|
|
"node",
|
|
str(TSC),
|
|
str(PROJECT_ROOT / "common_util" / "common_util_excavation.ts"),
|
|
"--outDir",
|
|
str(out),
|
|
"--module",
|
|
"esnext",
|
|
"--target",
|
|
"es2022",
|
|
"--moduleResolution",
|
|
"bundler",
|
|
"--ignoreConfig",
|
|
],
|
|
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")
|
|
result = tmp_path / "out.json"
|
|
subprocess.run( # noqa: S603
|
|
["node", str(out / "runner.mjs"), 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_두_쪽이_같은_값을_낸다(tmp_path: Path) -> None:
|
|
ts = _ts_values(tmp_path)
|
|
|
|
assert {int(k): v for k, v in ts["table"].items()} == PIPE_TRENCH_WIDTH_MM
|
|
assert ts["pipe"] == [pipe_trench_width_m(d) for d in (300, 800, 1000, 1200, 1500, 250, 1600)]
|
|
assert ts["wallWithFoundation"] == [
|
|
wall_trench_area_m2(h, 0.7, has_foundation=True) for h in (1.0, 2.0, 3.0)
|
|
]
|
|
assert ts["wallBlindingOnly"] == [
|
|
wall_trench_area_m2(h, 0.7, has_foundation=False) for h in (1.0, 2.0, 3.0)
|
|
]
|
|
assert ts["wallWidth"] == [wall_trench_width_m(t) for t in (0.7, 1.05, 0)]
|
|
assert ts["nulls"] == [None, None, None]
|
|
|
|
|
|
def test_관은_표를_그대로_쓴다() -> None:
|
|
"""⚠ 관경 + 2b 로 계산하지 않는다 — 표가 관 두께·작업여유를 담고 있다."""
|
|
assert pipe_trench_width_m(800) == 1.6
|
|
assert pipe_trench_width_m(800) != 0.8 + 2 * 0.3
|
|
for diameter in (800, 1000, 1200, 1500):
|
|
assert pipe_trench_width_m(diameter) is not None, diameter
|
|
|
|
|
|
def test_표에_없는_관경은_지어내지_않는다() -> None:
|
|
assert pipe_trench_width_m(250) is None
|
|
assert pipe_trench_width_m(1600) is None
|
|
|
|
|
|
def test_실무_정본_수치와_맞는다() -> None:
|
|
"""xls 7탭: H=1.0 → 1.03+0.45 = 1.48 · H=2.0 → 2.51 · H=3.0 → 3.54 (평균두께 0.83)."""
|
|
thickness = 0.83
|
|
for height, expected in ((1.0, 1.48), (2.0, 2.51), (3.0, 3.54)):
|
|
area = wall_trench_area_m2(height, thickness, has_foundation=True)
|
|
assert area == pytest.approx(expected, abs=0.01), height
|
|
|
|
|
|
def test_기초_유무가_기초분을_가른다() -> None:
|
|
with_base = wall_trench_area_m2(2.0, 0.7, has_foundation=True)
|
|
without = wall_trench_area_m2(2.0, 0.7, has_foundation=False)
|
|
assert with_base - without == pytest.approx(
|
|
WALL_FOUNDATION_DEPTH_M * WALL_FOUNDATION_WIDTH_M - 0.1 * 0.7
|
|
)
|
|
|
|
|
|
def test_여유폭은_평균두께에_더한다() -> None:
|
|
assert wall_trench_width_m(0.7) == pytest.approx(0.7 + WALL_TRENCH_CLEARANCE_M)
|