Files
Aislo/resources/tester/test_826_1_layer_index.py
T
eomsangdonandClaude Opus 5.5 c7cbef86a0 feat(826-2): M01 기계 = 한 대 한 파일 · M01 저장 길 줄이기
- 기계.json 한 파일 → 폴더 기계/(EQ 키마다 파일 + _머리.json · 차례 = 키 차례) · 옮긴 빌더 build_기계폴더_826_2 (655 대 머리 · 줄 · 차례 대조 뒤 옛 파일 지움)
- 읽기 도우미 master_machine_files — cm.load · Store_Cache · Store.read 가 옛 「기계.json」 한 표 꼴로 지어 줌(API · 화면 · 색인 그대로) · 쓰기 = 바뀐 대 파일만
- 기계.json 직접 열던 곳 도우미로: M02_San_Basis_Index · M02_Logic_Standard 지문 · build_용도 · master_keys.adopt · _틀 · _갱신절차
- 저장 몸 M01_MasterData_Store_Save — 전 = 기억 · 후 = 고친 파일만 얕은 사본 · 틀은 고친 줄만 · 조합 · 일위대가 문서는 로직 줄이 빠질 때만 · 재료 찾기 기억 이어받음 · 저장 뒤 쓴 파일만 기억에 갈아 끼움
- 마스터 사본 시험 22 개 = 기계 폴더도 복사 · 821_2 · 815_2 글 대조 = 대 파일 하나 대조(옛 한 파일 글 대조 낡음)
- 새 시험 test_826_2 — 폴더 = 한 표 · 한 대 한 파일 · 새 · 지움 · 409 · 옛 길과 같은 오류 스무 견본

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-12 19:45:00 +09:00

210 lines
7.4 KiB
Python

"""826-1 층 목록 요약 색인 — 답 = 다시 셈한 것 · 자국 같으면 항목 파일 안 엶 · 고침 · 지움 · 더함 ·
참조 고침 · M01 값 바뀜 · 색인 깨짐 뒤에도 맞음 · 판 충돌(409) 그대로."""
from __future__ import annotations
import json
import shutil
from pathlib import Path
import pytest
from M01_MasterData import M01_MasterData_Store as m01
from M02_MasterTemplete import M02_Labor_Ref as refs
from M02_MasterTemplete import M02_Layer_Index as index
from M02_MasterTemplete import M02_MasterTemplete_Store as store
from M02_MasterTemplete import M02_Template_Layers as layers
from resources.tester.test_m02_labor_ref import COMPANY, world # noqa: F401 — 같은 임시 층
PCT = "GC001109" # 원/㎡ · 인력 둘 + 덧줄 %
WAGE = "LB000001"
LIB = ("sangeun", "precombo", "labor")
@pytest.fixture()
def fresh(world, monkeypatch): # noqa: F811
"""색인 기억 비움 — 시험끼리 안 섞임."""
monkeypatch.setattr(index, "_MEM", {})
monkeypatch.setattr(index, "_IDS_LOADED", [])
monkeypatch.setattr(refs, "_IDS", {})
return world
def _forget():
index._MEM.clear()
index._IDS_LOADED.clear()
refs._IDS.clear()
def _sk(folder, name, extra=None):
doc = {"이름": name, "단위": "㎡", "바탕": {"로직": PCT}, "더한줄": extra or []}
with store.at(folder):
return refs.create("sangeun", doc)["이름"]
def _wage_line(qty=1):
return {"종류": "인력", "요소": WAGE, "이름": "보통인부", "단위": "인", "수량": qty}
def _old(folder):
rows = layers.list_templates(folder)
refs.summaries(folder, rows)
return rows
def _new(folder):
rows = index.list_rows(folder)
index.summaries(folder, rows)
return rows
def _same(folder):
got = _new(folder)
assert json.dumps(got, ensure_ascii=False) == json.dumps(_old(folder), ensure_ascii=False)
return got
@pytest.fixture()
def reads(monkeypatch):
"""항목 파일(서고 · 일위대가) 읽기 수."""
seen: list[str] = []
real = Path.read_bytes
def counting(self):
if self.parent.name in LIB:
seen.append(f"{self.parent.name}/{self.stem}")
return real(self)
monkeypatch.setattr(Path, "read_bytes", counting)
return seen
def _shelf(w):
up = _sk(w["company"], "회사 단가산출")
mine = w["personal"]
a = _sk(mine, "가", [_wage_line()])
b = _sk(mine, "나", [{"참조": {**COMPANY, "키": up}, "수량": 2}])
with store.at(mine):
sc = refs.create(
"precombo",
{
"이름": "일위대가",
"단위": "㎡",
"담은로직": [{"참조": {**COMPANY, "키": up}, "수량": {"손": 3}}],
},
)["이름"]
return up, a, b, sc
def test_same_and_no_reads_when_stamps_same(fresh, reads):
up, a, b, sc = _shelf(fresh)
mine = fresh["personal"]
first = _same(mine)
assert {r["이름"] for r in first} >= {a, b, sc} and first[0]["기준금액"] is not None
reads.clear()
assert _new(mine) == first and reads == [] # 메모리 기억
_forget()
reads.clear()
assert _new(mine) == first and reads == [] # 디스크 파생 저장본(서버 다시 뜸)
# 쓰는곳수 — 회사 SK 를 개인 SK 「나」 가 씀(사전조합은 사본으로 펼쳐 담음 · 740-1)
company = _same(fresh["company"])
assert company[0]["쓰는곳수"] == 1
def _unfreeze(folder, key):
"""옛 꼴(사본 뗌) — M01 을 지금 값으로 셈(사본 있으면 만든 때 값 그대로)."""
doc = json.loads(layers.template_path(folder, "sangeun", key).read_bytes())
old = {k: v for k, v in doc.items() if k not in ("사본", "수식", "M01판", "바탕판")}
layers.write_template(folder, "sangeun", key, old)
def test_edit_delete_add_and_dep_change(fresh, reads):
up, a, b, sc = _shelf(fresh)
mine = fresh["personal"]
gone = _sk(mine, "지울것")
_same(mine)
with store.at(mine): # 고침 · 지움
got = store.read("sangeun", a)
store.write("sangeun", a, got["판"], {**got["문서"], "더한줄": [_wage_line(3)]})
store.delete("sangeun", gone)
c = _sk(mine, "다") # 더함
reads.clear()
rows = _new(mine)
assert set(reads) == {f"sangeun/{a}", f"sangeun/{c}"} # 바뀐 항목만 다시 읽음
assert rows == _old(mine)
titles = {r["이름"]: r.get("제목") for r in rows if r["종류"] in index.layers.LIBRARY_KINDS}
assert gone == c and titles == {
a: "가",
b: "나",
c: "다",
sc: "일위대가",
} # 지운 키를 새 것이 받음
reads.clear()
was = {r["이름"]: r.get("기준금액") for r in _new(mine)}
assert reads == []
with store.at(fresh["company"]): # 사전조합이 담은 위 층 SK 고침 → 그 사전조합만 다시
got = store.read("sangeun", up)
store.write("sangeun", up, got["판"], {**got["문서"], "더한줄": [_wage_line(5)]})
reads.clear()
now = {r["이름"]: r.get("기준금액") for r in _new(mine)}
assert set(reads) == {f"precombo/{sc}", f"sangeun/{up}"}
assert now[sc] != was[sc] and {k: v for k, v in now.items() if k != sc} == {
k: v for k, v in was.items() if k != sc
}
assert _new(mine) == _old(mine)
def test_m01_change_recounts_amount(fresh, tmp_path, monkeypatch):
copy = tmp_path / "m01"
copy.mkdir()
for path in Path(m01.FOLDER).glob("*.json"):
shutil.copyfile(path, copy / path.name)
shutil.copytree(Path(m01.FOLDER) / "기계", copy / "기계") # 기계 = 폴더(826-2)
monkeypatch.setattr(m01, "FOLDER", copy)
mine = fresh["personal"]
a = _sk(mine, "가", [_wage_line()])
_unfreeze(mine, a)
one = next(r for r in _same(mine) if r["이름"] == a)["기준금액"]
assert one is not None
wages = json.loads((copy / "인력.json").read_text(encoding="utf-8"))
for row in wages["줄"]:
if row.get("키") == WAGE:
row["값"] = float(row["값"]) * 2
(copy / "인력.json").write_text(json.dumps(wages, ensure_ascii=False), encoding="utf-8")
two = next(r for r in _same(mine) if r["이름"] == a)["기준금액"]
assert two != one
def test_broken_index_rebuilds(fresh):
_up, _a, _b, _sc = _shelf(fresh)
mine = fresh["personal"]
first = _same(mine)
folder = index.derived_dir(layers.storage_root(), index.KIND)
for path in folder.glob("*.json"):
path.write_text("{깨짐", encoding="utf-8")
_forget()
assert _same(mine) == first
for path in folder.glob("*.json"):
path.write_text(json.dumps({"key": path.stem, "value": {"sangeun/x": 1}}), encoding="utf-8")
_forget()
assert _same(mine) == first
shutil.rmtree(folder)
_forget()
assert _same(mine) == first
def test_version_conflict_still_409(fresh):
mine = fresh["personal"]
a = _sk(mine, "가")
listed = next(r for r in index.list_rows(mine) if r["이름"] == a)["판"]
path = layers.template_path(mine, "sangeun", a)
assert listed == layers.version_of(path)
doc = json.loads(path.read_bytes())
layers.write_template(
mine, "sangeun", a, {**doc, "단위": "m"}, version=listed, check_version=True
)
with pytest.raises(layers.StaleTemplate): # 옛 판으로 또 저장
layers.write_template(mine, "sangeun", a, doc, version=listed, check_version=True)
fresh_ver = next(r for r in index.list_rows(mine) if r["이름"] == a)["판"]
assert fresh_ver == layers.version_of(path) != listed