Files
Aislo/resources/tester/test_b08_option_keys.py
eomsangdonandClaude Opus 5 d7cb14f416 test(B08): tmp/tests 중 tester 에 없던 47 개를 resources/tester 로 옮김
tmp/ 가 창끼리 안 건너가는 것이 확정돼(랩탑이 시간 두고 두 번 확인) 시험·예외가
저절로 건너가도록 git 안으로 옮김. 사용자 확정.

- 내용은 하나도 안 고침 — 자리만 옮김. tmp/tests 는 남겨 둠.
- 같은 이름이 이미 있던 64 개는 랩탑 것을 그대로 두고 건너뜀.
- helper_b05_*.js 둘은 랩탑이 .cjs 로 이미 올린 것과 **줄바꿈만 다른 같은 내용**이라
  복사본을 도로 뺌(시험이 .cjs 를 부름).
- resources/tester/ 에서 전체 1176 통과 · 29 건너뜀 · 실패 0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-09 17:15:57 +09:00

117 lines
5.4 KiB
Python

"""저장 제원 키 ↔ 엔진이 읽는 키 대조 (2026-09-07 ⑳ 앞).
⚠ **오늘 잡은 사고 중 사용자 입력이 직접 무시되던 첫 사례**가 여기였다 —
레지스트리는 `back_len_cm` 인데 엔진은 `stone_back_length_cm` 을 읽고 있어
**뒷길이를 75 로 골라도 늘 45 계수**로 돌았다. 값이 나오므로 아무 시험도 안 잡았다.
⇒ 「이름으로 알아보는 코드는 정본과 대조하라」(사방공 `type_id` 건)의 **데이터 키 판**이다.
"""
from __future__ import annotations
import json
import re
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(ROOT))
#: 레지스트리에 칸이 **아직 없는** 키. 「칸이 생기면 받는다」는 뜻으로 코드에 남겨 둔 것이고,
#: 키 이름이 어긋난 것과는 다르다. 새로 늘리려면 **왜 없는지**를 여기 적을 것.
#: 레지스트리에 아직 칸이 없는 키 — **왜 없는지**를 함께 적는다.
#: ⚠ 2026-09-09 `face_slope_ratio` 는 칸이 생겨 뺐음(`ebdf2988`). 비워 두면 품셈
#: 표준경사표로 자동 판정되고, 값이 있으면 그 값이 이긴다(확정 ⑨).
#: ⓘ 2026-09-09 오후 비었다 — `ditch_spec` 은 랩탑 보조가 칸을 만들어(`92fd9092`) 여기서 뺐다.
#: 목록이 비어 있는 것이 정상이다. **칸 없이 코드에만 있는 키가 생기면** 여기 까닭과 함께
#: 적을 것 — 적지 않으면 이 시험이 「이름을 잘못 적었다」로 잡는다.
KNOWN_ABSENT: dict[str, str] = {}
def _registry_keys() -> set[str]:
payload = json.loads(
(ROOT / "B05_Profile" / "B05_Profile_Structure_Types.json").read_text(encoding="utf-8")
)
types = payload if isinstance(payload, list) else (payload.get("types") or [])
return {option["key"] for item in types for option in item.get("options") or []}
def _engine_keys() -> dict[str, set[str]]:
found: dict[str, set[str]] = {}
for path in (ROOT / "B08_Quantity").glob("*.py"):
text = path.read_text(encoding="utf-8")
for match in re.finditer(r'options\.get\(\s*"([a-z0-9_]+)"', text):
found.setdefault(match.group(1), set()).add(path.name)
for path in (ROOT / "resources").rglob("*.json"):
try:
text = path.read_text(encoding="utf-8")
except OSError:
continue
for match in re.finditer(r'"option_key"\s*:\s*"([a-z0-9_]+)"', text):
found.setdefault(match.group(1), set()).add(path.name)
return found
def test_엔진이_읽는_키가_레지스트리에_있을것() -> None:
"""⚠ 이 시험이 `back_len_cm` 사고를 다시 못 나게 한다."""
registry = _registry_keys()
missing = {
key: sorted(where)
for key, where in _engine_keys().items()
if key not in registry and key not in KNOWN_ABSENT
}
assert not missing, f"레지스트리에 없는 옵션 키를 읽고 있다: {missing}"
def test_없는_키는_사유가_적혀_있을것() -> None:
"""「없다」를 그냥 넘기지 않는다 — 왜 없는지가 적혀 있어야 한다."""
for key, why in KNOWN_ABSENT.items():
assert why, key
# 사유 목록에 적힌 키가 정말 레지스트리에 없는지도 본다(고쳐졌는데 목록만 남는 것 방지).
registry = _registry_keys()
stale = [key for key in KNOWN_ABSENT if key in registry]
assert not stale, f"레지스트리에 칸이 생겼으니 목록에서 뺄 것: {stale}"
def test_돌쌓기_뒷길이_키가_실제로_맞을것() -> None:
"""사고가 났던 그 자리를 이름으로 직접 못 박는다."""
from B08_Quantity.B08_Quantity_Engine_UnitQuantity import BACK_LENGTH_KEYS
assert "back_len_cm" in BACK_LENGTH_KEYS
assert "back_len_cm" in _registry_keys()
def test_되돌릴_수_있어야_하는_숫자_칸은_계약에_등록될것() -> None:
"""⚠ `None` 을 통째로 버리면 「안 정함」으로 되돌릴 길이 없다 (2026-09-08 ㉘).
시공법·타설 방식은 빈 문자열로 되돌리지만 **숫자 칸은 되돌릴 값이 `None` 뿐**이다.
새 숫자 칸을 넣고 여기 등록하지 않으면 **한 번 넣은 값이 영영 남는다.**
같은 병을 두 번 만났으므로 계약처럼 다룬다.
"""
from B08_Quantity.B08_Quantity_Router_Earthwork import (
NULLABLE_SETTING_KEYS,
QuantitySettingsBody,
)
# ⚠ 좁게 본다 — `dict[str, float]`(비율표)까지 걸면 정상 칸을 잡는다(넓은 규칙 사고).
# 여기서 겨냥하는 것은 **홑 숫자 칸**뿐이다.
홑숫자 = {float | None, int | None}
숫자칸 = {
name
for name, field in QuantitySettingsBody.model_fields.items()
if field.annotation in 홑숫자
}
assert 숫자칸, "홑 숫자 칸을 하나도 못 골랐다 — 판정이 틀렸다"
빠진것 = 숫자칸 - set(NULLABLE_SETTING_KEYS)
assert not 빠진것, f"숫자 칸이 NULLABLE_SETTING_KEYS 에 없다 — 되돌릴 길이 없다: {sorted(빠진것)}"
def test_되돌리기_칸은_갈아끼우기_대상이기도_할것() -> None:
"""등록만 하고 갈아끼우기(`replace_keys`)에서 빠지면 병합돼 옛 값이 남는다."""
import inspect
from B08_Quantity import B08_Quantity_Router_Earthwork as router
본문 = inspect.getsource(router.save_quantity_settings)
assert "NULLABLE_SETTING_KEYS" in 본문