# -*- coding: utf-8 -*- """178-3 사면안정 흙막이 · 옹벽 「추가 단」 상자 → 여러 단 횡단 형상. 못박는 것 - 상자 = 유출 흙막이 상자 양식(공용 [추가] · [빼기]) · 흙막이 = 형태 · 높이 · 옹벽 = 높이만 · 키 `tier_{n}_*`. - 명세 저장 왕복 그대로 · 빈 상자도 한 단으로 남음 · 정의 그룹 종류 그대로(`STRUCTURE_SPECS` 에 안 넣음). - 서버 저장(structures.json) → 벽 제원(파이썬) 단 수 = 1 + 상자 수 · 단마다 형태 · 높이 · 옹벽 단 = 콘크리트. - 횡단 형상(진짜 `computeRevetmentLayout`) — 단 수 · 단마다 형태 · 높이(빈 값 = 본 벽 형태 · 1.5 m) · 옛 `tiers` 그대로. Node 헬퍼 `helper_178_3_slope_tiers.cjs`. 화면은 사용자 확인. """ import json import os import subprocess import pytest from B05_Profile.B05_Profile_Structures_Schema import StructureInstance HERE = os.path.dirname(os.path.abspath(__file__)) ROOT = os.path.abspath(os.path.join(HERE, "..", "..")) SOIL = { "form": "돌쌓기(찰)", "length_m": 20, "height_m": 2, "before_m": 10, "after_m": 10, "tier_1_form": "콘크리트", "tier_1_height": 2, "tier_2_form": "돌망태", "tier_2_height": 1.2, } def _read(*parts: str) -> str: with open(os.path.join(ROOT, *parts), encoding="utf-8") as handle: return handle.read() @pytest.fixture(scope="module") def out(): proc = subprocess.run( ["node", os.path.join(HERE, "helper_178_3_slope_tiers.cjs")], capture_output=True, text=True, encoding="utf-8", timeout=60, ) assert proc.returncode == 0, proc.stderr return json.loads(proc.stdout) def test_상자_칸_흙막이_형태_높이_옹벽_높이(out): assert out["roundTrip"]["items"] == { "soil_guard": ["form", "height"], "retaining_wall": ["height"], } assert out["roundTrip"]["notMainSpecs"] == [] def test_명세_저장_왕복(out): trip = out["roundTrip"] assert trip["soilState"] == [ {"form": "콘크리트", "height": "2"}, {"form": "돌망태", "height": "1.2"}, ] assert trip["soilBack"] == { "tier_1_form": "콘크리트", "tier_1_height": 2, "tier_2_form": "돌망태", "tier_2_height": 1.2, } assert trip["wallBack"] == {"tier_1_height": 1.8} assert trip["emptyBox"] == {"tier_1_form": ""} # 값 없이 추가만 해도 단 하나 def test_서버_저장_뒤_벽_제원(tmp_path): from B05_Profile.B05_Profile_Structures_Repository import save_structures from B06_Section import B06_Section_Engine_Structures_Wall as wall items = [ StructureInstance( type_id="soil_guard", placement="interval", chainage_m=180, start_m=170, end_m=190, options=SOIL, ), StructureInstance( type_id="retaining_wall", placement="interval", chainage_m=300, start_m=290, end_m=310, options={"height_m": 2.5, "tier_1_height": 1.8}, ), ] save_structures(str(tmp_path), items, base_revision=0) got = {spec["type_id"]: spec for spec in wall.load_wall_structures(tmp_path)} assert got["soil_guard"]["tiers"] == 3 assert got["soil_guard"]["extras"] == [ {"form": "콘크리트", "height_m": 2}, {"form": "돌망태", "height_m": 1.2}, ] assert got["retaining_wall"]["tiers"] == 2 assert got["retaining_wall"]["extras"] == [{"form": "콘크리트", "height_m": 1.8}] def test_횡단_형상_단_수_단마다_형태_높이(out): assert out["soil2"]["requested"] == 3 assert out["soil2"]["walls"] == [ {"form": "돌쌓기(찰)", "pure": 2}, {"form": "콘크리트", "pure": 2}, {"form": "돌망태", "pure": 1.2}, ] assert out["wall1"]["walls"] == [ {"form": "콘크리트", "pure": 2.5}, {"form": "콘크리트", "pure": 1.8}, ] # 빈 상자 = 본 벽 형태 · 기본 1.5 m · 상자 없으면 옛 tiers 그대로 · 둘 다 없으면 1단 assert out["emptyBox"]["walls"][1] == {"form": "돌망태", "pure": 1.5} assert out["legacy"]["requested"] == 2 and out["legacy"]["extras"] == [] assert out["none"]["requested"] == 1 def test_배선(): spec = _read("B05_Profile", "B05_Profile_UI_Structures_Spec.ts") # 명세 · 덧붙이 명세 = 본보기 키로(323-7 · 복사한 종류도 같은 상자) assert "specFor(type.type_id) ?? ADDON_SPECS[baseType(type.type_id)]" in spec walls = _read("common_util", "common_util_structure_walls.ts") assert "파이썬 `tier_extras` 짝" in walls engine = _read("B06_Section", "B06_Section_Engine_Structures_Wall.py") assert "TS `tierExtras` 짝" in engine