Files
Aislo/resources/tester/test_826_1_layer_index.py
T
eomsangdonandClaude Opus 5.5 bb9d8a8101 perf(826-1): M02 층 목록 요약 색인 — 자국 같으면 항목 파일 안 엶 · 판 · 요약 · 쓰는곳수 기억
- 새 M02_Layer_Index — 항목마다 자국(고친 때 · 크기) · 판 · 서고 요약 기억(메모리 + storage _derived/m02_layer_index) · 바뀐 항목만 다시 읽어 셈
- 기준금액 기댐 = 셈 코드 내용 · M01 폴더 · 층 곁 파일 자국 + 셈 때 읽은 참조 파일 자국(refs.SEEN)
- 쓰는곳수 = 요청마다 쓰는 곳 파일 한 번 훑어 거꾸로 셈 · 참조 이름표 기억도 파생 저장본(ids.json)
- 판 확인(저장 · 지움 409)은 그대로 파일 내용 · 목록 답 내용 · 차례 그대로(8004 바이트 대조)
- 개인 층(102 줄) 두 번째 3.0 → 0.14 초 · 다시 뜬 뒤 첫 9.9 → 0.24 초 · 마스터 2.9 → 0.10 초

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

209 lines
7.3 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)
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