"""PLAN 306 — B06 횡단 맹암거 안 돌 무늬 · 고르기 차례(요구 200-1). - 306-1 돌 무늬 = 켜마다 반 칸 엇갈리고 이웃 · 위아래와 겹친 돌 · 기울기 번갈아 교차 · 그림 전용(누름 안 받음) - 306-2 고르기 차례 = 맹암거 안쪽 판정면 맨 아래(겹친 띠 · 관이 위) · 테두리 판정 = 면 판정 맨 위 · 보라 선 맨 위 그대로 · 다른 구조물 차례(작은 것 위) 그대로 실제 TS 를 `helper_306_underdrain_pick.cjs` 가 Node 로 돌린다. """ from __future__ import annotations import json import os import re import subprocess from collections import defaultdict import pytest HERE = os.path.dirname(__file__) ROOT = os.path.join(HERE, "..", "..") B06 = os.path.join(ROOT, "B06_Section") pytestmark = pytest.mark.skipif( not os.path.isdir(os.path.join(ROOT, "config", "node_modules")), reason="프론트엔드 의존성 미설치", ) @pytest.fixture(scope="module") def out() -> dict: proc = subprocess.run( # noqa: S603 — 고정 실행 파일 ["node", os.path.join(HERE, "helper_306_underdrain_pick.cjs")], capture_output=True, text=True, encoding="utf-8", ) assert proc.returncode == 0, proc.stderr return json.loads(proc.stdout) def _rows(out: dict) -> list[list[dict]]: """돌을 가로(따라) · 노면 아래 깊이(켜) 좌표로 — 깊이 같은 것끼리 한 켜.""" (ax, ay), (bx, by) = out["body"][0], out["body"][1] rows: dict[float, list[dict]] = defaultdict(list) for s in out["stones"]: depth = s["cy"] - (ay + (s["cx"] - ax) * (by - ay) / (bx - ax)) rows[round(depth, 1)].append({**s, "along": s["cx"]}) return [sorted(rows[k], key=lambda s: s["along"]) for k in sorted(rows)] def test_돌_무늬_엇갈려_겹침_306_1(out): assert out["groupClass"] == "b06-chart__underdrain" assert out["stones"] and all(s["cls"] == "b06-chart__culvert-stone" for s in out["stones"]) rows = _rows(out) assert len(rows) == 4 # 높이 1 m ÷ 0.25 step = rows[0][1]["along"] - rows[0][0]["along"] gap = abs(rows[1][0]["along"] - rows[0][0]["along"]) % step assert abs(gap - step / 2) < 1e-6 # 이웃 켜는 반 칸 엇갈림 for row in rows: # 돌 길이(따라) > 간격 · 돌 높이(가로질러) > 켜 간격 → 이웃 · 위아래와 겹침 assert all(s["h"] > step * 1.3 for s in row) degs = [s["deg"] for s in row] assert all(a != b for a, b in zip(degs, degs[1:])) # 기울기 번갈아 → 교차 row_gap = 20 / len(rows) # 암거 높이 1 m = 20 px assert all(s["w"] > row_gap for s in out["stones"]) assert len({s["deg"] for s in out["stones"]}) == 2 def test_돌_무늬는_누름_안_받음_306_1(): with open(os.path.join(B06, "B06_Section_UI_Style_Cross_Areas.css"), encoding="utf-8") as f: css = f.read() assert re.search(r"\.b06-chart__underdrain \{\s*pointer-events: none;", css) assert re.search(r"\.b06-chart__shape-hit--edge \{[^}]*pointer-events: stroke;", css) def test_고르기_차례_306_2(out): # 안쪽 판정면 맨 아래 · 구조물 판정면 넓이 차례(작은 관 위) 그대로 · 테두리 = 면 판정 맨 위 · 보라 선 맨 위 assert out["order"] == [ "underdrain", "band", "g", "bigPipe", "smallPipe", "underdrain+edge", "road", ] assert out["edgeCount"] == 1 def test_누른_자리_받는_것_306_2(out): old, new = out["probesOld"], out["probesNew"] # 옛 차례 — 맹암거 안 성토 띠 · 맹암거보다 넓은 관이 막힘 assert old["bandInUnderdrain"] == "underdrain" and old["pipeInUnderdrain"] == "underdrain" assert new == { "bandInUnderdrain": "band", "pipeInUnderdrain": "bigPipe", "smallPipe": "smallPipe", "underdrainOnly": "underdrain", "border": "underdrain+edge", "road": "road", } def test_테두리_누름은_맹암거_고름_306_2(out): assert out["picked"] == ["ud-1"]