- M02 종류별 탭 [종류 더하기] = 본보기(구조물 정본 원본) 복사 — 키 자동 · 이름 「본보기 2」 · 옵션 · 표시 · 그룹 · 배치 · 규칙 · 종류 덧칸 · 반영 그대로 · 종류.{새}.본보기 · 관 지점 종류 · 복사본은 본보기 밖
- 구조물 표 = 본보기 처음값 이어받음(resolveCatalog · resolve_catalog 짝 · 줄 칸 base) · baseType/base_type · specFor(명세) · 덧단 · 길이 전후 풀기 · 옹벽 추가 단 콘크리트
- 검사(화면 = 서버 짝) — 본보기 = 원본 · 같은 군 · 자리꼴 · 정본 · 만든 뒤 고정 · 복사본은 본보기가 숨긴 칸 이어받음 · 서버 덧칸 본보기 받음
- 특수 동작 = 동작 이름 — 바로 안 놓기 · 포장 나누기 = 포장_물넘이_겹침 · 월류 높이 기다림 = 월류단면
- 정본 목록 = 표 — 파이썬 관 지점 종류 = catalog owner · B05 시설 서브폼 = 표 형상 · 안 쓰는 손 목록 걷음
- drawing_views — 스키마에서 빼고 읽을 때 버림 · M02 [저장] 때 빠짐(코드는 마스터 안 고침)
- 이관 = 지난 판 이름 대신 측점 계획 자리(planted)로 가름 · all_type_names 걷음
- 허용 거리 = common_util_structure_station.py 한 곳(SAME_STATION_M · EDGE_TOLERANCE_M · PIPE_MATCH_M) · TS 표는 station.ts 머리
- 시험: test_323_7_new_type(템플릿 · 일곱 갈래 · 그림 · 검사 짝 · 서버 읽기) · 322-0 유령 시험 = 자리 · 옛 도움 파일 동작 · 표 길 · 기대 글 고침
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ji4nuyLVHcxrXWHTdb5nRD
129 lines
5.0 KiB
Python
129 lines
5.0 KiB
Python
"""B05 시설 [저장]이 폼이 모르는 칸을 안 지우는가 (2026-09-14 브레인 판정).
|
|
|
|
⚠ 앞서 저장이 옵션을 통째로 갈아 끼워, 구조물 집계표·구조물도로 적은 칸(관 기슭막이 기초 등)이
|
|
B05 에서 그 시설을 한 번 더 저장하면 조용히 지워졌음 — 되돌릴 길이 없는 사고.
|
|
짝: `B05_Profile/B05_Profile_UI_Drainage_Facility_Merge.ts` — 실제 TS 를 옮겨 돌림.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import re
|
|
import subprocess
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
ROOT = Path(__file__).resolve().parents[2]
|
|
TSC = ROOT / "config" / "node_modules" / "typescript" / "bin" / "tsc"
|
|
MERGE_TS = ROOT / "B05_Profile" / "B05_Profile_UI_Drainage_Facility_Merge.ts"
|
|
FORM_TS = ROOT / "B05_Profile" / "B05_Profile_UI_Drainage_Facility.ts"
|
|
|
|
RUNNER = """
|
|
import { readFileSync, writeFileSync } from "node:fs";
|
|
import { FORM_OPTION_KEYS, mergeFacilityOptions } from "./B05_Profile_UI_Drainage_Facility_Merge.js";
|
|
const cases = JSON.parse(readFileSync(process.argv[2], "utf8"));
|
|
const out = cases.map(([previous, next]) => mergeFacilityOptions(previous, next));
|
|
writeFileSync(process.argv[3], JSON.stringify({ out, keys: FORM_OPTION_KEYS }));
|
|
"""
|
|
|
|
PIPE_BEFORE = {
|
|
"facility": "pipe",
|
|
"options": {
|
|
"pipe_diameter_mm": 1000,
|
|
"inlet_type": "집수정",
|
|
"inlet_basin_form": "□형(기본형)",
|
|
"revet_foundation": "기초유", # 집계표로 적은 칸 — 폼에 없음
|
|
"inlet_basin_before_m": 1.5, # 집수정 전 — 24-4a 부터 폼 칸(옛 횡단 조정창 몫)
|
|
},
|
|
}
|
|
# 폼이 다시 저장 — 유입구를 기슭막이로 바꿔 집수정 칸은 안 보냄
|
|
PIPE_FORM = {
|
|
"facility": "pipe",
|
|
"options": {
|
|
"pipe_diameter_mm": 800,
|
|
"inlet_type": "기슭막이",
|
|
"inlet_revet_form": "돌쌓기(찰)",
|
|
},
|
|
}
|
|
REVET_BEFORE = {"facility": "revetment", "options": {"side": "양쪽", "back_len_cm": "35"}}
|
|
TYPE_CHANGE = {"facility": "ford_pavement", "options": {"ford_width_m": 5}}
|
|
|
|
|
|
@pytest.fixture(scope="module")
|
|
def merged(tmp_path_factory) -> dict:
|
|
if not TSC.is_file():
|
|
pytest.skip("프론트엔드 의존성 미설치")
|
|
out = tmp_path_factory.mktemp("merge_js")
|
|
subprocess.run( # noqa: S603 — 고정 실행 파일
|
|
[
|
|
"node",
|
|
str(TSC),
|
|
str(MERGE_TS),
|
|
"--outDir",
|
|
str(out),
|
|
"--module",
|
|
"esnext",
|
|
"--target",
|
|
"es2022",
|
|
"--moduleResolution",
|
|
"bundler",
|
|
"--ignoreConfig",
|
|
],
|
|
cwd=str(ROOT),
|
|
check=True,
|
|
capture_output=True,
|
|
)
|
|
(out / "runner.mjs").write_text(RUNNER, encoding="utf-8")
|
|
cases = [
|
|
[PIPE_BEFORE, PIPE_FORM],
|
|
[REVET_BEFORE, {"facility": "revetment", "options": {"side": "좌"}}],
|
|
[PIPE_BEFORE, TYPE_CHANGE],
|
|
[None, PIPE_FORM],
|
|
]
|
|
(out / "cases.json").write_text(json.dumps(cases, ensure_ascii=False), encoding="utf-8")
|
|
subprocess.run( # noqa: S603
|
|
["node", str(out / "runner.mjs"), str(out / "cases.json"), str(out / "result.json")],
|
|
cwd=str(ROOT),
|
|
check=True,
|
|
capture_output=True,
|
|
)
|
|
return json.loads((out / "result.json").read_text(encoding="utf-8"))
|
|
|
|
|
|
def test_폼이_모르는_칸은_남고_폼이_아는_칸은_폼대로(merged) -> None:
|
|
options = merged["out"][0]["options"]
|
|
assert options["revet_foundation"] == "기초유" # 집계표 값이 살아남음
|
|
assert options["inlet_basin_before_m"] == 1.5 # 집수정 전 — 폼에서 빠짐(54-1) · 옛 값 남음
|
|
assert options["pipe_diameter_mm"] == 800 # 폼 값이 이김
|
|
# 표 01 명세(PLAN 52-1)가 뺀 옛 칸 = 폼이 모르는 칸 → 남음(지난 정의 값)
|
|
assert options["inlet_basin_form"] == "□형(기본형)"
|
|
assert merged["out"][1]["options"] == {"side": "좌", "back_len_cm": "35"}
|
|
|
|
|
|
def test_시설_종류를_바꾸면_통째로_갈아_끼움(merged) -> None:
|
|
assert merged["out"][2] == TYPE_CHANGE
|
|
assert merged["out"][3] == PIPE_FORM
|
|
|
|
|
|
def test_폼이_적는_칸이_모두_목록에_있다(merged) -> None:
|
|
"""폼에 칸을 늘리고 목록에 안 넣으면 여기서 잡힘."""
|
|
source = FORM_TS.read_text(encoding="utf-8")
|
|
body = source[source.index("readOptions() {") :]
|
|
# 폼 갈래 = 구조물 표 형상(323-7 · 종류 키 대신) → 관 지점 종류
|
|
branches = re.split(r'shapeOf\(current\) === "(\w+)"', body)[1:]
|
|
kinds = {
|
|
"pipe_set": "pipe",
|
|
"box": "box_culvert",
|
|
"ford_pavement": "ford_pavement",
|
|
"ford": "ford_bridge",
|
|
"hidden_revet": "revetment",
|
|
}
|
|
assert branches[::2] == list(kinds)
|
|
for shape, block in zip(branches[::2], branches[1::2]):
|
|
facility = kinds[shape]
|
|
written = set(re.findall(r'putNumber\(options, "(\w+)"', block))
|
|
written |= set(re.findall(r"options\.(\w+) =", block))
|
|
missing = written - set(merged["keys"][facility])
|
|
assert not missing, f"{facility}: 폼이 적는데 목록에 없음 {missing}"
|