fix(M01): 별칭 복사 gather — 사용자 변수가 별칭과 이름이 겹치면 조용히 안 바꾸고 저장 막음
- 교차 검증 어긋남 1건(사용자가 별칭과 같은 이름의 입력·중간·덧줄을 새로 만들면 되돌릴 때 그 이름이 조용히 찾기() 원문으로 바뀌던 것) — gather 에서 별칭 이름을 그 순간의 입력·중간·덧줄 이름(_taken)과 견줘 겹치면 FormulaError(400) · 자동 이름 바꾸기는 화면 개입 없이는 어느 쪽 식인지 못 가려 안전하지 않아 넣지 않음 - 재현 시험 추가 · 전체 로직 1,354줄 펴고 되돌리기 어긋남 0건 재확인 · resources/master_data/ref/_검증_별칭_복사.md 에 「고친 뒤」 덧붙임
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
# 별칭 복사(3-4) 검증 — 어긋난 것만
|
||||
|
||||
읽기 전용 검증 · 사본 폴더에서만 저장(정본·대장·키 대장 안 바뀜 → 시험 GX 지울 것 없음).
|
||||
|
||||
## 어긋남 1건
|
||||
|
||||
- **사용자가 별칭과 같은 이름의 자기 변수를 만들면 조용히 원문 부르기로 바뀜** (`master_copy.gather` → `_unmask`)
|
||||
- 재현: `GF000219` 를 펴서 별칭 `메쌓기인력` 을 얻은 뒤, 중간에 `메쌓기인력 = 1` 을 더하고 수량 칸을 `메쌓기인력 * 2` 로 씀 → 되돌리면 수량 칸이 `찾기(QF000421, …) * 2` 로 바뀌고 사용자 변수는 안 쓰인 채 남음. 오류 없이 저장됨.
|
||||
- 원인: `gather` 가 별칭 이름을 입력·중간·덧줄 이름과 견주지 않음(펴기 쪽 `_taken` 은 견줌).
|
||||
- 고칠 곳: `gather` 에서 별칭 이름이 입력·중간·덧줄 이름과 같으면 `FormulaError`.
|
||||
|
||||
## 어긋남 없음 (확인한 것)
|
||||
|
||||
- ① 정본 40개(산림 9 · 건설 31 · 찾기 많은 8 · 호표 요소에 로직() 14 · 덧줄 13 포함): 펴고 그대로 되돌려 저장 → 40/40 원본과 글자 같음 · 같은 입력 계 40/40 같음.
|
||||
- ② 별칭 이름 겹침 0건 · 기존 이름과 겹침 0건. 낱말 일부(`별칭량` · `별칭_2`) · 작은따옴표 안 · 이미 원문인 부르기 안 · 점 뒤(값 칸 이름) 는 안 바뀜. 같은 별칭에 두 원문은 막음.
|
||||
- ③ 호표 요소 안 `로직()` 14개 별칭으로 보였다가 원문으로 돌아옴.
|
||||
- ④ 저장한 GX 40개 틀·로직 검사 통과(대장 넣은 검사 · 정본 전체 검사 0건).
|
||||
|
||||
## 고친 뒤
|
||||
|
||||
- `gather` 에 이름 겹침 검사 더함 — 별칭 이름이 그 순간 입력·중간·덧줄 이름(`_taken`)과 같으면 `FormulaError`(400) · 조용히 안 바꾸고 막음(가림 자동화는 화면 개입 없이는 안전하게 못함). 재현 사례 다시 돌리면 막힘 확인 · 전체 로직 1,354줄(40개 표본의 상위 집합) 펴고 되돌리기 어긋남 0건 그대로 · 시험 `test_m01_copy.py` 추가.
|
||||
@@ -243,8 +243,15 @@ def _line(where: str, kind: str, at: int, one: dict, text) -> dict:
|
||||
|
||||
|
||||
def gather(shown: dict, marks: list[dict]) -> dict:
|
||||
"""별칭 꼴 식 묶음 → 저장할 로직 한 줄. 별칭 원문은 부르기 하나여야 함(아무 글이나 못 넣음)."""
|
||||
"""별칭 꼴 식 묶음 → 저장할 로직 한 줄. 별칭 원문은 부르기 하나여야 함(아무 글이나 못 넣음).
|
||||
|
||||
사용자가 편집 중 별칭과 같은 이름의 입력·중간·덧줄을 새로 만들면(예: `메쌓기인력 = 1`)
|
||||
그 뒤로는 식 글 속 그 이름이 사용자 것인지 별칭인지 가릴 수 없음(둘 다 같은 토큰) —
|
||||
자동으로 별칭 쪽만 다시 이름 지어 피하려 하면 사용자가 실제로 쓴 자리를 건드릴 위험이
|
||||
있어(화면 개입 없이는 안전하게 못 가림) 조용히 바꾸지 않고 저장을 막음.
|
||||
"""
|
||||
raw_of: dict[str, str] = {}
|
||||
taken = _taken(shown) # 지금 입력·중간·덧줄에 실제로 있는 이름(사용자 것이 우선)
|
||||
for one in marks or []:
|
||||
name, raw = str(one.get("이름") or ""), str(one.get("원문") or "")
|
||||
if not name or not raw:
|
||||
@@ -253,6 +260,11 @@ def gather(shown: dict, marks: list[dict]) -> dict:
|
||||
raise mf.FormulaError(f"별칭 「{name}」 의 원문이 찾기·로직 부르기가 아님 — 「{raw}」")
|
||||
if name in raw_of and raw_of[name] != raw:
|
||||
raise mf.FormulaError(f"별칭 「{name}」 이 두 가지 원문을 가리킴")
|
||||
if name in taken:
|
||||
raise mf.FormulaError(
|
||||
f"「{name}」 을 입력·중간·덧줄 이름으로도 쓰고 별칭으로도 씀 — "
|
||||
"어느 쪽 식이 그 이름을 가리키는지 가릴 수 없음 · 이름을 바꿀 것"
|
||||
)
|
||||
raw_of[name] = raw
|
||||
out = copy.deepcopy(shown)
|
||||
for _, _, _, col, box in slots(out):
|
||||
|
||||
@@ -169,6 +169,28 @@ def test_식을_고쳐_저장하면_그_값으로_계산된다(client: TestClien
|
||||
assert Decimal(str(now["재료비"])) == Decimal(str(was["재료비"]))
|
||||
|
||||
|
||||
def test_별칭과_같은_이름의_사용자_변수를_만들면_저장을_막는다(client: TestClient) -> None:
|
||||
"""일감 15 — 교차 검증 어긋남 1건. 가림 못 하는 이름 겹침은 조용히 안 바꾸고 막음."""
|
||||
view = _get(client, "/api/m01/logic/formula", key=STACK)
|
||||
row = view["로직"]
|
||||
row["중간"].insert(0, {"이름": "메쌓기인력", "식": "1"}) # 별칭과 같은 이름
|
||||
for h in row["호표"]:
|
||||
if h["이름"] == "석공":
|
||||
h["수량"] = "메쌓기인력 * 2"
|
||||
got = _post(
|
||||
client,
|
||||
"/api/m01/logic/formula/save",
|
||||
{"로직": row, "별칭": view["별칭"], "본뜬키": STACK},
|
||||
want=400,
|
||||
)
|
||||
assert "메쌓기인력" in str(got["detail"])
|
||||
# 미리보기 길도 같이 막힘
|
||||
got2 = _post(
|
||||
client, "/api/m01/logic/formula/preview", {"로직": row, "별칭": view["별칭"]}, want=400
|
||||
)
|
||||
assert "메쌓기인력" in str(got2["detail"])
|
||||
|
||||
|
||||
def test_별칭_원문이_부르기가_아니면_막는다(client: TestClient) -> None:
|
||||
view = _get(client, "/api/m01/logic/formula", key=STACK)
|
||||
marks = [{**one} for one in view["별칭"]]
|
||||
|
||||
Reference in New Issue
Block a user