"""M02 항목 목록(PLAN 39-5) — 칸 · 찾기 · 쪽(TS · `helper_m02_item_list.cjs`) · 서버 목록 칸(임시 storage). ① 종류마다 칸(도면 · 면적산출 · 일위대가 · 항목 통째) · 빈 번호 「—」 · 합계 없으면 「—」 · 고친 날 = 날짜만 · 찾기 = 보이는 칸 AND · 쪽 30 줄 · 넘친 쪽은 끝 쪽. ② 서버 `items` — `수정일`(도면 · 면적산출 · 일위대가 파일) · `labor`(번호 · 담은 로직 수 · 합계 = 계산값.반영합.계) · 일위대가 파일이 없으면 null · 깨진 파일 · 수 아닌 합계도 목록은 뜸. """ from __future__ import annotations import json import os import subprocess import pytest from resources.tester import test_m02_tree as base from resources.tester.test_m02_tree import Tree, _as world = base.world # 같은 임시 storage 판 HERE = os.path.dirname(os.path.abspath(__file__)) @pytest.fixture(scope="module") def ts() -> dict: proc = subprocess.run( ["node", os.path.join(HERE, "helper_m02_item_list.cjs")], capture_output=True, text=True, encoding="utf-8", timeout=60, ) assert proc.returncode == 0, proc.stderr return json.loads(proc.stdout) def test_종류마다_칸(ts): assert ts["heads"]["structure"] == ["도번", "이름", "폴더", "단위", "고친 날"] assert ts["heads"]["area"] == ["면적번", "이름", "폴더", "단위", "고친 날"] assert ts["heads"]["labor"] == [ "일위대가번", "이름", "폴더", "단위", "담은 로직 수", "합계", "고친 날", ] assert "규격" not in sum(ts["heads"].values(), []) and "층" not in sum(ts["heads"].values(), []) def test_칸_값_빈_번호_합계_날짜(ts): s, labor = ts["cells"]["structure"], ts["cells"]["labor"] assert s[0] == ["SD-c1", "1.2m", "기슭막이 / 메쌓기", "m", "2026-10-01"] assert s[2][0] == "—" and s[2][2] == "" # 빈 도번 · 맨 위 항목(폴더 없음) assert labor[0][3:] == ["m", "3", "1,234,567", "2026-10-04"] # 합계 원 단위 반올림 assert labor[1][5] == "—" and labor[2][0] == labor[2][4] == "—" # 합계 없음 · 일위대가 없음 assert ts["cells"]["item"][0][-1] == "2026-10-04" # 항목 통째 = 가장 늦게 고친 날 def test_찾기_쪽(ts): assert ts["find"] == ["c2"] # 낱말 둘 AND(폴더 · 이름 칸) assert ts["findLabor"] == ["c1"] # 번호 칸도 찾음 assert ts["findNone"] == {"shown": [], "page": 1, "total": 0} assert ts["page3"] == {"page": 3, "total": 65, "n": 5} assert ts["pageOver"] == 3 and ts["pageEmpty"] == 1 def test_서버_목록_칸_수정일_labor(world): t = Tree(_as(world), "personal") t.do(op="add-item", 경로=["배수"], 이름="측구", 단위="m") t.do(op="add-item", 경로=[], 이름="옹벽", 단위="m") root = world["storage"] / "7" / "42" / "templates" ditch, wall = t.item(["배수", "측구"]), t.item(["옹벽"]) assert set(ditch["수정일"]) >= {"structure", "area"} assert len(ditch["수정일"]["structure"]) == 19 # 초 단위 ISO assert ( ditch["labor"]["로직수"] == 0 and ditch["labor"]["합계"] is None ) # 새 열 = 빈 일위대가(39-1b) (root / "labor").mkdir(exist_ok=True) doc = { "일위대가번": "SU-000001", "담은로직": [{"로직": "GF000219"}, {"로직": "GF000137"}], "계산값": {"반영합": {"계": 12345.6}}, } (root / "labor" / f"{ditch['열']}.json").write_text(json.dumps(doc), encoding="utf-8") (root / "labor" / f"{wall['열']}.json").write_text("{깨짐", encoding="utf-8") rows = {r["열"]: r for r in t.client.get(t.url).json()["items"]} assert rows[ditch["열"]]["labor"] == {"번호": "SU-000001", "로직수": 2, "합계": 12345.6} assert "labor" in rows[ditch["열"]]["수정일"] assert rows[wall["열"]]["labor"] == {"번호": None, "로직수": 0, "합계": None} doc["계산값"] = {"반영합": {"계": "글"}} (root / "labor" / f"{ditch['열']}.json").write_text(json.dumps(doc), encoding="utf-8") again = {r["열"]: r for r in t.client.get(t.url).json()["items"]} assert again[ditch["열"]]["labor"]["합계"] is None