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>
117 lines
5.4 KiB
Python
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 본문
|