feat(M01): 로직 지우기 검사 = 모든 층 일위대가 문서 담은로직 + 실무문서 — 쓰는 곳 알림 · 조합 고치기 길(PUT) 은퇴(39-1d)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013rJwJMVL4EE31y77TB11AP
This commit is contained in:
2026-10-04 18:03:35 +09:00
co-authored by Claude Opus 5.5
parent 36c437d9a1
commit cbcfc18436
4 changed files with 78 additions and 18 deletions
+43
View File
@@ -176,3 +176,46 @@ def test_만든_자체_로직이_시험_계산에_돈다(client: TestClient) ->
assert got["ok"] is True and Decimal(str(got["sums"]["노무비"])) > 0
listed = _get(client, "/api/m01/logics", owner="공용")["logics"]
assert made["key"] in [x["키"] for x in listed]
def test_일위대가가_담은_로직은_못_지운다(client: TestClient, tmp_path: Path, monkeypatch) -> None:
"""39-1d — 로직 지우기 검사 = 모든 층(마스터 · 회사 · 개인) labor 담은로직 + 실무문서(마스터
일위대가 조합) · 쓰는 곳을 알림 · 다 빼면 지워짐(임시 storage · 임시 마스터)."""
import json
from config import config_system
from M02_MasterTemplete import M02_Template_Layers as layers
system, storage = tmp_path / "system", tmp_path / "storage"
monkeypatch.setattr(layers, "SYSTEM_ROOT", system)
monkeypatch.setattr(config_system, "STORAGE_BASE_DIR", str(storage))
made = _post(client, "/api/m01/logic/new", {"logic": _draft(), "owner": "현장"})
key = made["key"]
docs = {
"회사 3": storage / "3/templates/labor/item1.json",
"개인 3/15": storage / "3/15/templates/labor/item2.json",
"마스터": system / "labor/item3.json",
}
for at, path in enumerate(docs.values()):
path.parent.mkdir(parents=True)
doc = {"일위대가번": f"SU-00000{at}", "담은로직": [{"로직": SAMPLE}, {"로직": key}]}
path.write_text(json.dumps(doc, ensure_ascii=False), encoding="utf-8")
combos = store.FOLDER / store.mcb.FILE
raw = json.loads(combos.read_text(encoding="utf-8"))
raw["줄"].append({**raw["줄"][0], "키": "UA999999", "담은로직": [{"로직": key, "메모": None}]})
combos.write_text(json.dumps(raw, ensure_ascii=False), encoding="utf-8")
def delete(want: int) -> dict:
version = _get(client, "/api/m01/logic", key=key)["version"]
return _post(client, "/api/m01/logic/delete", {"key": key, "version": version}, want)
errors = " ".join(delete(422)["detail"]["errors"])
for where in docs:
assert f"일위대가 {where} 「SU-" in errors and key in errors # 어디서 쓰는지 알림
assert store.mcb.FILE in errors # 실무문서(마스터 일위대가 조합)
for path in docs.values(): # 일위대가에서 뺌 — 실무문서만 남음
path.write_text(json.dumps({"담은로직": [{"로직": SAMPLE}]}), encoding="utf-8")
assert "일위대가 " not in " ".join(delete(422)["detail"]["errors"])
raw["줄"].pop()
combos.write_text(json.dumps(raw, ensure_ascii=False), encoding="utf-8")
assert delete(200)["file"] == make.OWN # 쓰는 곳 0 = 지워짐
+4 -5
View File
@@ -60,7 +60,7 @@ def world(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> dict[str, Any]:
def test_조합_길은_시스템_관리자만(world):
"""39-6 — 일위대가 조합 = 실무문서 · 조합 길 전부 시스템 관리자만 · 담을 로직 읽기는 로그인만."""
"""39-6 — 일위대가 조합 = 실무문서 · 조합 길 전부 시스템 관리자만 · 담을 로직은 로그인만."""
world["client"].app.dependency_overrides.pop(combos_router._door)
for role in ("USER", "ADMIN"):
client = _as(world, role)
@@ -149,10 +149,9 @@ def test_검사_빈_조합_이름_겹침_본뜬키_판(world):
branched = _new(me, "personal", _combo("마스터 본뜸", 본뜬키=master_key))
assert branched["combo"]["본뜬키"] == master_key # 층을 넘는 계보
url = f"{URL.format('personal')}/{first['키']}"
assert me.put(url, json={"combo": _combo("원본 고침"), "판": "낡은판"}).status_code == 409
now = _list(me, "personal")["판"]
res = me.put(url, json={"combo": _combo("원본 고침", (B,)), "판": now})
assert res.status_code == 200 and res.json()["combo"]["담은로직"][0]["로직"] == B
assert (
me.put(url, json={"combo": _combo("원본 고침")}).status_code == 405
) # 조합 고치기 은퇴(39-1d)
# ── 나무 ─────────────────────────────────────────────────────────────