feat(M01): 로직 지우기 검사 — 모든 층 구조물 일위대가 문서가 담은 로직까지 (39-1b B9)
- M01 저장 전 검사(check_saved)에 labor_errors — 시스템 층 · storage 회사 · 개인 층 labor/*.json 이 담은 로직이 없어지면 막음(프로젝트 층 빼고 · 실무문서는 조합 검사 그대로) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EYJJRCfHfdCGNhmXTjQKV7
This commit is contained in:
@@ -16,12 +16,17 @@ import threading
|
||||
from decimal import Decimal
|
||||
from pathlib import Path
|
||||
|
||||
from config import config_system
|
||||
|
||||
SCRIPTS = Path(__file__).resolve().parents[1] / "resources/master_data/scripts"
|
||||
sys.path.insert(0, str(SCRIPTS))
|
||||
|
||||
import check_master as cm # noqa: E402
|
||||
import master_material as mm # noqa: E402 (자재품목 테이블ID — 재료 고르기는 그 묶음을 훑음)
|
||||
|
||||
# 구조물 일위대가 문서(M02 `labor/<열>.json` · 39-1b) 시스템 층 — storage 층은 따로 · 시험이 바꿈
|
||||
LABOR_ROOTS: tuple[Path, ...] = (config_system.PROJECT_ROOT / "resources" / "master_template",)
|
||||
|
||||
_FRESH = threading.RLock()
|
||||
_READ: dict[str, tuple[tuple, dict]] = {} # 파일 이름 → (자국, 읽은 내용)
|
||||
_INDEX: dict = {} # 폴더 · 자국 · files · whole
|
||||
@@ -217,7 +222,36 @@ def check_saved(files: dict, changed: list[str], book: dict, narrowed=None) -> l
|
||||
out += cm.check_logics(files, whole, book, narrowed and narrowed[:2])
|
||||
if narrowed and narrowed[2]: # 요소·표를 고쳤으면 연결 · 준용도 봄(로직 좁힘과 따로)
|
||||
out += cm.check_links(whole)
|
||||
return out + cm.mcb.check_all(files, whole)
|
||||
return out + cm.mcb.check_all(files, whole) + labor_errors(whole)
|
||||
|
||||
|
||||
def _labor_files() -> list[Path]:
|
||||
base = Path(config_system.STORAGE_BASE_DIR)
|
||||
roots = [*LABOR_ROOTS]
|
||||
if base.is_dir():
|
||||
roots += [*base.glob("*/templates"), *base.glob("*/*/templates")] # 프로젝트 빼고
|
||||
return [p for root in roots for p in sorted((root / "labor").glob("[!._]*.json"))]
|
||||
|
||||
|
||||
def labor_errors(whole) -> list[str]:
|
||||
"""모든 층 구조물 일위대가가 담은 로직이 마스터에 없으면 한 줄씩 — 쓰는 로직 지우기를 막음
|
||||
(실무문서 = 마스터 조합은 `check_all` 몫)."""
|
||||
out = []
|
||||
for path in _labor_files():
|
||||
try:
|
||||
doc = json.loads(path.read_bytes())
|
||||
except (OSError, ValueError):
|
||||
continue
|
||||
for one in doc.get("담은로직") or []:
|
||||
code = str(one.get("로직") or "") if isinstance(one, dict) else ""
|
||||
try:
|
||||
whole.logic(code)
|
||||
except cm.mf.FormulaError:
|
||||
where = f"{path.parent.parent.name}/{path.stem}"
|
||||
out.append(
|
||||
f"구조물 일위대가 {where} 이 담은 로직 「{code}」 이 없음 — 쓰는 로직은 못 지움"
|
||||
)
|
||||
return out
|
||||
|
||||
|
||||
def warm(folder: Path) -> threading.Thread:
|
||||
|
||||
@@ -211,3 +211,30 @@ def test_길_틀린_문서_400_없는_열_404_남의_개인_층_403(client):
|
||||
assert client.post(url.format("system", "없는열"), json={}).status_code == 404
|
||||
other = client.post(url.format("personal", "rv") + "?owner=7/99", json={})
|
||||
assert other.status_code == 403
|
||||
|
||||
|
||||
def test_로직_지우기_검사는_모든_층_일위대가를_봄(tmp_path, monkeypatch):
|
||||
from config import config_system
|
||||
from M01_MasterData import M01_MasterData_Store_Cache as cache
|
||||
|
||||
system, storage = tmp_path / "system", tmp_path / "storage"
|
||||
for folder, code in (
|
||||
(system / "labor", STONE),
|
||||
(storage / "7" / "templates" / "labor", TEN), # 회사 층
|
||||
(storage / "7" / "42" / "templates" / "labor", STONE), # 개인 층
|
||||
(storage / "7" / "42" / "p1" / "templates" / "labor", STONE), # 프로젝트 — 안 봄
|
||||
):
|
||||
folder.mkdir(parents=True)
|
||||
body = {"담은로직": [{"로직": code}]}
|
||||
(folder / "a.json").write_text(json.dumps(body), encoding="utf-8")
|
||||
monkeypatch.setattr(cache, "LABOR_ROOTS", (system,))
|
||||
monkeypatch.setattr(config_system, "STORAGE_BASE_DIR", str(storage))
|
||||
|
||||
class Gone: # STONE 을 지운 마스터
|
||||
def logic(self, code):
|
||||
if code == STONE:
|
||||
raise cache.cm.mf.FormulaError("없음")
|
||||
|
||||
got = cache.labor_errors(Gone())
|
||||
assert len(got) == 2 and all(STONE in x for x in got)
|
||||
assert any("system/a" in x for x in got) and any("templates/a" in x for x in got)
|
||||
|
||||
Reference in New Issue
Block a user