Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016VBGFXB9AbJBwXP19z75Qq
103 lines
3.8 KiB
Python
103 lines
3.8 KiB
Python
"""횡단도(B06)가 그리는 돌쌓기 벽 폭과 수량(B08)이 세는 벽 두께가 **같은 값인가** — 대조 시험.
|
||
|
||
왜 (2026-09-14 브레인 차례 「두께 차이」) — 같은 다단 벽을 두 곳이 다른 두께로 봄:
|
||
B06 `REVET_THICKNESS_M` 0.45(울진 뒷길이 관측 · 표시용) → 윗폭 1.5×0.45 · 밑폭 윗폭 + 기울기×H
|
||
B08 `wall_thickness` 실무 구조물도 식 → 상부 뒷길이 + 0.30 · 하부 상부 + 0.30×(H−1)
|
||
어느 쪽으로 맞출지는 아직 안 정함 — 그래서 `xfail(strict)`: 지금은 빨강이 **나야** 하고,
|
||
두 값이 맞춰지면 통과로 뒤집혀 이 표시를 걷으라고 알림.
|
||
실제 화면 코드를 컴파일해 Node 로 돌림(`test_b06_extra_shift_floor` 와 같은 방식).
|
||
"""
|
||
|
||
import json
|
||
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 B08_Quantity.B08_Quantity_Engine_UnitQuantity_StoneSpec import ( # noqa: E402
|
||
_back_length,
|
||
wall_thickness,
|
||
)
|
||
|
||
TSC = PROJECT_ROOT / "config" / "node_modules" / "typescript" / "bin" / "tsc"
|
||
MODULE = PROJECT_ROOT / "B06_Section" / "B06_Section_UI_Cross_Culvert_Extra.ts"
|
||
FORM = "돌쌓기(메)"
|
||
LEAN = 0.35 # 품셈 13-4-4 [주]⑪ 메쌓기 성토 ~3m
|
||
|
||
_RUNNER = """
|
||
const { buildOutletExtras } = require(process.argv[3]);
|
||
const { writeFileSync } = require("node:fs");
|
||
const groundAt = (offset) => 95 - offset / 2;
|
||
const result = buildOutletExtras({
|
||
start: { offset: 0, elevation: 100 },
|
||
startBottomElevation: 100,
|
||
outward: 1,
|
||
groundAt,
|
||
limitOffset: 60,
|
||
adjusts: [{ x: 0, d: null, h: 2.0, m: "%(form)s" }],
|
||
leanFor: () => %(lean)s,
|
||
});
|
||
const wall = result.walls[0];
|
||
const [bottomBack, topBack, topFront, bottomFront] = wall.points;
|
||
writeFileSync(process.argv[2], JSON.stringify({
|
||
pureHeight: wall.height + 0.5,
|
||
top: Math.abs(topFront.offset - topBack.offset),
|
||
bottom: Math.abs(bottomFront.offset - bottomBack.offset),
|
||
}));
|
||
"""
|
||
|
||
|
||
def _drawn(tmp_path: Path) -> dict:
|
||
out = tmp_path / "out"
|
||
subprocess.run( # noqa: S603 — 고정 실행 파일
|
||
[
|
||
"node",
|
||
str(TSC),
|
||
"--ignoreConfig",
|
||
"--target",
|
||
"es2022",
|
||
"--module",
|
||
"commonjs",
|
||
"--skipLibCheck",
|
||
"--outDir",
|
||
str(out),
|
||
str(MODULE),
|
||
],
|
||
cwd=str(PROJECT_ROOT),
|
||
check=False,
|
||
capture_output=True,
|
||
)
|
||
compiled = next(out.rglob("B06_Section_UI_Cross_Culvert_Extra.js"), None)
|
||
assert compiled is not None, "화면 코드가 JS 로 안 나옴 — tsc 실패"
|
||
(out / "runner.cjs").write_text(_RUNNER % {"form": FORM, "lean": LEAN}, encoding="utf-8")
|
||
result = tmp_path / "result.json"
|
||
subprocess.run( # noqa: S603 — 고정 실행 파일
|
||
["node", str(out / "runner.cjs"), str(result), str(compiled)],
|
||
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="프론트엔드 의존성 미설치")
|
||
@pytest.mark.xfail(strict=True, reason="B06 0.45 표시값 ↔ B08 구조물도 식 — 맞출 쪽 판정 대기")
|
||
def test_횡단도_벽_폭과_수량_벽_두께가_같다(tmp_path: Path) -> None:
|
||
drawn = _drawn(tmp_path)
|
||
height = drawn["pureHeight"]
|
||
options = {"form": FORM}
|
||
back_cm = _back_length(options, wet=False, height_m=height)
|
||
top, bottom, basis = wall_thickness(options, back_cm=back_cm, height_m=height)
|
||
assert (round(drawn["top"], 2), round(drawn["bottom"], 2)) == (
|
||
round(top, 2),
|
||
round(bottom, 2),
|
||
), (
|
||
f"H {height:.2f} — 횡단도 윗폭 {drawn['top']:.3f} · 밑폭 {drawn['bottom']:.3f}"
|
||
f" ↔ 수량 {basis}"
|
||
)
|