fix(M02): 자재 조합 이름 오류 글 — 파일명 · 키 없이 쉬운 말 한 줄 (PLAN 21-10 흠 7 서버)

- 같은 이름 · 60자 넘는 이름을 틀 검사 앞에서 「이름」 한 줄로 막음
- 오류 글에 이름은 30자에서 자름(…)
- 시험: 겹침 글 · 긴 이름 자름 · 60자 넘음

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EUypcnp5d1gU2aeKh9F2H7
This commit is contained in:
2026-09-29 13:31:59 +09:00
co-authored by Claude Sonnet 5.5
parent 5cb65dff16
commit 90e478fe0b
2 changed files with 25 additions and 1 deletions
@@ -91,6 +91,20 @@ def _clean(row: Any, key: str, keep: dict | None = None) -> dict[str, Any]:
}
def _short(name: str, at: int = 30) -> str:
return name if len(name) <= at else name[:at] + "…"
def _check_name(rows: list, key: str, name: str) -> None:
"""이름 겹침 · 긴 이름 — 틀 검사(파일명 · 키가 섞인 글) 앞에서 쉬운 말 한 줄로 막음."""
if len(name) > mmc.MAX_이름:
raise store.StoreError(422, {"errors": [f"이름이 너무 김 — {mmc.MAX_이름}자까지"]})
if any(r.get("키") != key and str(r.get("이름", "")).strip() == name for r in rows):
raise store.StoreError(
422, {"errors": [f"「{_short(name)}」 이름의 자재 조합이 이미 있음 — 다른 이름으로"]}
)
def _save(doc: dict, version: str) -> str:
"""틀 검사 → 판이 같을 때만 씀 → 새 판."""
if errors := mmc.check_form(FILE, doc):
@@ -111,6 +125,7 @@ def create(row: Any, version: str | None = None) -> dict[str, Any]:
fixed = _clean(row, key)
if fixed["본뜬키"] and fixed["본뜬키"] not in {r.get("키") for r in doc["줄"]}:
raise store.StoreError(400, f"본뜬키 「{fixed['본뜬키']}」 — 있는 다른 자재 조합 키여야 함")
_check_name(doc["줄"], key, fixed["이름"])
doc = {**doc, "다음": int(doc.get("다음") or 1) + 1, "줄": [*doc["줄"], fixed]}
return {"키": key, "판": _save(doc, have if version is None else version), "줄": fixed}
@@ -118,6 +133,7 @@ def create(row: Any, version: str | None = None) -> dict[str, Any]:
def edit(key: str, row: Any, version: str) -> dict[str, Any]:
doc, _ = _load()
fixed = _clean(row, key, keep=_find(doc, key))
_check_name(doc["줄"], key, fixed["이름"])
doc = {**doc, "줄": [fixed if r.get("키") == key else r for r in doc["줄"]]}
return {"키": key, "판": _save(doc, version), "줄": fixed}
+9 -1
View File
@@ -83,7 +83,15 @@ def test_없는_자재_두_번_이름_본뜬키는_막는다(client) -> None:
ok = _new(client, "원본", [M1]).json()["키"]
copy = _new(client, "본뜬 것", [M1, M2], 본뜬키=ok).json()
assert copy["줄"]["본뜬키"] == ok and copy["키"] != ok
assert _new(client, "원본", [M2]).status_code == 422 # 이름 겹침
dup = _new(client, "원본", [M2]) # 이름 겹침 — 파일명 · 키 없이 쉬운 말 한 줄
assert dup.status_code == 422 and dup.json()["detail"]["errors"] == [
"「원본」 이름의 자재 조합이 이미 있음 — 다른 이름으로"
]
long = "긴" * 40
assert _new(client, long, [M1]).status_code == 200
cut = _new(client, long, [M2]).json()["detail"]["errors"][0]
assert cut.startswith("「" + "긴" * 30 + "…」") and matset.FILE not in cut and "UM0" not in cut
assert _new(client, "긴" * 61, [M1]).json()["detail"]["errors"] == ["이름이 너무 김 — 60자까지"]
assert _new(client, "낡은 판", [M2], 판="0000").status_code == 409