Files
Aislo/resources/tester/test_cover_template.py
eomsangdonandClaude Opus 5 0ef32b5279 chore(tester): 시험을 resources/tester/ 로 옮김 — 창끼리 건너가게
⚠ **뿌리** — `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>
2026-09-09 17:12:30 +09:00

90 lines
3.7 KiB
Python

"""표지 템플릿·엔진 검증 (2026-08-31 신설).
표지는 A1 실치수 고정이라 좌표가 어긋나면 곧바로 종이 밖으로 나간다. 그래서
① 종이 크기 ② 플레이스홀더 치환 ③ 캐시 오염 세 가지를 못 박아 둔다.
"""
import json
from pathlib import Path
import pytest
from B07_DesignDetail.B07_DesignDetail_Engine_Cad import DRAWING_FORMAT, FRAME_LAYER_ID
from B07_DesignDetail.B07_DesignDetail_Engine_Cad_Cover import build_cover_drawing
TEMPLATE = Path("resources/template_2dDrawing/00_template_cover.json")
# A1 도각 템플릿과 같은 재단 표식 원점 — 두 도면이 같은 종이 좌표계를 써야 한다.
PAPER = (-5.05, -5.04, 834.95, 588.96)
def _bbox(entities):
xs, ys = [], []
for entity in entities:
shape = entity.get("shapeData") or {}
for key in ("basePoint", "point", "startPoint", "endPoint"):
if key in shape:
xs.append(shape[key]["x"])
ys.append(shape[key]["y"])
for p in shape.get("points", []):
xs.append(p["x"])
ys.append(p["y"])
return min(xs), min(ys), max(xs), max(ys)
def test_템플릿이_A1_종이에_맞는다():
document = json.loads(TEMPLATE.read_text(encoding="utf-8"))
bbox = _bbox(document["entities"])
assert [round(v, 2) for v in bbox] == list(PAPER)
assert round(bbox[2] - bbox[0], 1) == 840.0
assert round(bbox[3] - bbox[1], 1) == 594.0
def test_굵은_띠는_실치수_두께를_갖는다():
"""lineWidth는 화면 픽셀이라 두께가 안 나온다 — solid Hatch로 냈는지 확인."""
document = json.loads(TEMPLATE.read_text(encoding="utf-8"))
hatches = [e for e in document["entities"] if e["type"] == "Hatch"]
assert len(hatches) == 3 # 상단선·하단선·제목 강조선
for hatch in hatches:
assert hatch["shapeData"]["options"]["style"] == "solid"
ys = [p["y"] for p in hatch["shapeData"]["points"]]
assert 0.5 <= max(ys) - min(ys) <= 3.0 # mm
def test_도면은_잠금_도각_레이어로_나온다():
"""도각은 잠기고, 덧그릴 비잠금 층이 최소 하나 있어야 한다.
도각만 있으면 CAD에서 새 선을 놓을 자리가 없다 — 표지에 비잠금 「주기」 층을
함께 싣는다(2026-09-01 `64d944eb`). 층 순서는 규약이 아니므로 id로 찾는다.
"""
drawing = build_cover_drawing("cover")
assert drawing["format"] == DRAWING_FORMAT
assert {e["layerId"] for e in drawing["entities"]} == {FRAME_LAYER_ID}
layers = {layer["id"]: layer for layer in drawing["layers"]}
assert layers[FRAME_LAYER_ID]["isLocked"] is True
assert any(not layer["isLocked"] for layer in drawing["layers"]), (
"덧그릴 비잠금 도면층이 없다"
)
@pytest.mark.parametrize(
("fields", "expected"),
[
({}, ""), # 값이 없으면 빈칸 — 남의 값이 남지 않는다
({"시행청": "울진국유림관리소"}, "울진국유림관리소"),
],
)
def test_플레이스홀더가_치환된다(fields, expected):
drawing = build_cover_drawing("cover", fields)
labels = [e["shapeData"]["label"] for e in drawing["entities"] if e["type"] == "Text"]
assert expected in labels
def test_치환이_캐시된_템플릿을_더럽히지_않는다():
"""_load_template은 lru_cache다 — 한 번 채운 값이 다음 도면에 새면 안 된다."""
build_cover_drawing("a", {"공사명": "남의 사업", "시행청": "남의 청"})
fresh = build_cover_drawing("b")["entities"]
labels = [e["shapeData"]["label"] for e in fresh if e["type"] == "Text"]
assert "남의 사업 설계도" not in labels
assert "남의 청" not in labels