"""구조물집계표조합 — 서버가 집계표 [저장] 때 맞추는 장부(키 SS · 열마다 한 줄). 견본 표(`write_sample_table`)로만 봄 — 실제 마스터 양식은 안 읽음. """ import json from pathlib import Path import pytest from fastapi import FastAPI from fastapi.testclient import TestClient from M02_MasterTemplete import M02_MasterTemplete_Store as store from M02_MasterTemplete import M02_Template_Layers as layers from M02_MasterTemplete.M02_MasterTemplete_Router import router from resources.tester.test_m02_structure import write_sample_table ROOT = Path(__file__).resolve().parents[2] TABLE = "/api/m02/templates/table/구조물집계표" @pytest.fixture() def client(tmp_path, monkeypatch): write_sample_table(tmp_path) monkeypatch.setattr(store, "FOLDER", tmp_path) app = FastAPI() app.include_router(router) return TestClient(app) def _save_table(client, change) -> None: got = client.get(TABLE).json() change(got["문서"]) assert client.put(TABLE, json={"판": got["판"], "문서": got["문서"]}).status_code == 200 def _keys(client) -> dict[str, str]: return {row["열"]: row["키"] for row in client.get("/api/m02/structure-combos").json()["줄"]} def test_처음_한_번_만듦_글_열_빼고_칸은_집계표_그대로(client, tmp_path): assert not (tmp_path / store.COMBO_FILE).exists() doc = client.get("/api/m02/structure-combos").json() assert (doc["그룹"], doc["다음"]) == ("구조물집계표조합", 14) rows = doc["줄"] assert [r["열"] for r in rows][:3] == ["rv", "c1", "pv_b"] assert {"no", "sta", "memo"}.isdisjoint(r["열"] for r in rows) # 글 열 빼기 assert (rows[0]["키"], rows[-1]["키"]) == ("SS000001", "SS000013") assert rows[-1]["열"] == "h_rip_m" fb = next(r for r in rows if r["열"] == "fb") assert (fb["종류"], fb["분류1"], fb["분류2"], fb["분류3"]) == ( "세월교", "(관경 × 련수마다)", None, None, ) assert (tmp_path / store.COMBO_FILE).is_file() def test_집계표_저장_때_새_열_지운_열_고친_열을_맞춤_키_유지(client): before = _keys(client) def change(table): cols = table["열"] cols[:] = [c for c in cols if c["id"] != "h_rip_m"] # 맨 끝 키 SS000013 열 지움 rv = next(c for c in cols if c["id"] == "rv") rv["머리"] = ["돌기슭막이", "메쌓기", "1.2m", "기초유"] rv.update(단위="m", 일위대가="UA000004", 펼침=True, 바인딩={"종류": "revetment"}) cols.insert(3, {"id": "new1", "머리": ["옹벽", None, None, None], "단위": "m", "꼴": "수"}) _save_table(client, change) doc = client.get("/api/m02/structure-combos").json() rows = {r["열"]: r for r in doc["줄"]} assert "h_rip_m" not in rows assert rows["new1"]["키"] == "SS000014" # 지운 열의 SS000013 은 다시 안 씀 assert [r["열"] for r in doc["줄"]][:4] == ["rv", "new1", "c1", "pv_b"] # 집계표 차례 assert all(rows[c]["키"] == k for c, k in before.items() if c != "h_rip_m") rv = rows["rv"] assert (rv["분류1"], rv["분류3"], rv["단위"], rv["일위대가"]) == ( "메쌓기", "기초유", "m", "UA000004", ) assert (rv["바인딩"], rv["펼침"]) == ({"종류": "revetment"}, True) # 번호는 세 파일과 같음 — 새 열도 [저장] 한 번에 SD · SA · SM 이 섬 numbers = client.get("/api/m02/structures/numbers").json() assert {k: rows["new1"][k] for k in ("도번", "면적번", "재료번")} == numbers["new1"] assert rows["new1"]["도번"].startswith("SD") # 또 저장해도(바뀐 것 없음) 키 · 다음 그대로 _save_table(client, lambda table: None) again = client.get("/api/m02/structure-combos").json() assert (again["다음"], again["줄"]) == (15, doc["줄"]) def test_SS_머리글은_키대장과_안_겹침(): ledger = json.loads((ROOT / "resources/master_data/_키대장.json").read_text("utf-8")) assert "SS" not in ledger["다음"] assert store._COMBO_HEAD == "SS" def test_새_프로젝트_복사에_실림(client, tmp_path, monkeypatch): client.get("/api/m02/structure-combos") monkeypatch.setattr(layers, "SYSTEM_ROOT", tmp_path) copied = layers.seed_project(tmp_path / "project") assert "구조물집계표조합" in copied["작업본"] and "구조물집계표조합" in copied["초기"] target = layers.project_dir(tmp_path / "project") / store.COMBO_FILE assert json.loads(target.read_bytes())["줄"][0]["키"] == "SS000001"