Files
Aislo/resources/tester/test_structure_group_items.py
T
eomsangdonandClaude Opus 5.5 b7beeb292a feat(M02): 구조물 조합 사본 구멍 메움 — 관 옮김 이름만 · 실패 알림 · 알약 · 그룹 · 가져온이(142-2)
- sync_snapshots: 관 지점을 옮겨도 같은 항목 · 같은 그룹 꼬리면 폴더 이름만 바꿈(가까운 측점 먼저)
- safe_sync 못만듦을 구조물 · 관 지점 저장 응답(snapshot_failed)에 실음 · B05/B06 [저장] 알림 한 줄
- 사전조합 참조사본에 안 로직 알약(배 · 단위) 굳힘 · 사본 셈이 지금 M01 을 안 읽음
- 옵션 그룹 이름을 사본 manifest 에 굳힘 · 집계 채움이 사본 이름으로(정의가 바뀌거나 사라져도)
- manifest 가져온이(세션) · 사본 목록 부분 · 가져온이 · [개인에 저장]이 일위대가 문서도 가져감
- 시험: 관 옮김 → 같은 사본 · 못만듦 · 가져온이 · 그룹 사본 값 · 알약 굳힘 · 개인 저장 일위대가 · 저장 응답

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015sDZSjFvoiDjdGpgj3muc3
2026-10-09 15:05:52 +09:00

125 lines
6.0 KiB
Python

"""옵션 그룹별 집계 항목 `group_items` (PLAN 46-3 · 46-4).
못박는 것
- 칸 모양: `{그룹 id: summary_item}` · null 항목 뺌 · 빈 묶음 = None · 그룹 id · 항목이 틀리면 거절.
- 구조물 기록 · 관 지점 기록 둘 다 칸을 읽고 씀(옛 파일 = 칸 없음).
- [저장] 뒤 복사본: 그룹 연결마다 `{구조물 id}__{그룹 id}` 폴더(본체와 따로).
- 집계표 채움: 정의된 그룹마다 한 건 더(그 그룹이 고른 열) · 미지정 그룹은 「미지정」 알림에 셈 ·
정의가 사라진 그룹 연결은 표에 안 들어감(기록 · 복사본은 남음).
"""
from __future__ import annotations
import json
from pathlib import Path
import pytest
from B05_Profile.B05_Profile_Structures_Schema import StructureInstance
from common_util.common_util_drainage_pipes import parse_pipe_points
from common_util.common_util_summary_item import parse_group_items
from M02_MasterTemplete import M02_Item_Snapshot as snap
from M02_MasterTemplete import M02_MasterTemplete_Store as store
from M02_MasterTemplete import M02_Table_Fill as fill
from M02_MasterTemplete.StructOptions import M02_SO_Groups as groups
from resources.tester.test_structures_summary_snapshot import ( # noqa: F401 — world 고정물
MS,
PIPE,
RV,
_dry,
_items,
_pipe,
_save,
world,
)
def _define(system: Path, monkeypatch, doc: dict) -> None:
monkeypatch.setattr(store, "FOLDER", system)
monkeypatch.setattr(groups, "load_definition", lambda: {"그룹": doc, "판": 1})
def test_칸_모양():
assert parse_group_items(None) is None
assert parse_group_items({}) is None
assert parse_group_items({"g1": None}) is None
assert parse_group_items({"g1": MS, "g2": None}) == {"g1": MS}
for bad in ({"G1": MS}, {"g1": {"layer": "x"}}, ["g1"], {"g_1": MS}):
with pytest.raises(ValueError):
parse_group_items(bad)
def test_구조물_관_기록이_칸을_읽고_씀():
item = StructureInstance(
type_id="x", placement="point", chainage_m=1, group_items={"g1": MS, "g2": None}
)
assert item.group_items == {"g1": MS}
assert StructureInstance(type_id="x", placement="point", chainage_m=1).group_items is None
point = parse_pipe_points([{"chainage_m": 10.0, "source": "user", "group_items": {"g9": RV}}])
assert point[0].group_items == {"g9": RV}
assert point[0].as_dict()["group_items"] == {"g9": RV}
plain = parse_pipe_points([{"chainage_m": 10.0, "source": "user"}])[0]
assert "group_items" not in plain.as_dict()
def test_그룹_연결마다_복사본_폴더가_따로(world):
root = world["root"]
wall = {**_dry(MS), "group_items": {"g1": RV}}
pipe = {**_pipe(PIPE), "group_items": {"gin": MS}}
result = _save(root, [wall], [pipe])
assert result["만듦"] == ["pipe@40.0", "pipe@40.0__gin", "st0", "st0__g1"]
assert snap.FOLDER_PATTERN.match("pipe@40.0__gin")
manifest = json.loads((_items(root) / "st0__g1/manifest.json").read_text("utf-8"))
assert manifest["원본"]["key"] == RV["key"] and manifest["구조물"] == ["st0__g1"]
# 그룹 연결을 풀면 그 복사본만 지움
again = _save(root, [_dry(MS)], [pipe])
assert again["지움"] == ["st0__g1"] and again["만듦"] == []
def test_채움은_정의된_그룹마다_한_건_더(world, monkeypatch):
"""메쌓기 벽 = 본체(돌쌓기 ms) + 그룹 「기슭막이」(rv) — 한 구조물이 두 열에 · 길이 10."""
root, system = world["root"], world["system"]
_define(system, monkeypatch, {"masonry_dry": [{"id": "g1", "이름": "기슭막이", "옵션": []}]})
linked = {**_dry(MS), "group_items": {"g1": RV, "gold": PIPE}} # gold = 정의 없음
bare = {**_dry(None), "chainage_m": 125, "start_m": 120, "end_m": 130}
_save(root, [linked, bare], [])
table = json.loads((system / "table/구조물집계표.json").read_text("utf-8"))
doc = fill.fill_table(root, table)
values = {row["id"]: row["값"] for row in doc["줄"] if not row.get("고정")}
row = {key.split("|")[0]: value for key, value in values["s80.00~90.00"].items()}
assert row["ms"] == 10 # 본체 = 돌쌓기
assert row["rv"] == 10 # 그룹 = 기슭막이(같은 벽 길이)
assert set(values) == {"s80.00~90.00"} # 미지정 벽은 줄도 없음
# 둘째 벽: 본체 미지정 + 그룹 미지정 = 2건 · 정의 없는 gold 는 셈 밖
assert "집계 항목 미지정 2건 — 표에 안 들어감" in doc["알림"]
items = fill.collect_items(root)
assert [i["복사본"] for i in items] == ["st0", "st0__g1", "st1", "st1__g1"]
assert items[1]["그룹"] == "기슭막이" and items[1]["summary_item"] == RV
def test_그룹_정의도_사본_값_정의가_바뀌거나_사라져도_그대로(world, monkeypatch):
"""142-2 — 그룹 연결 사본 manifest `그룹` = 가져온 때 정의(이름) · 이름을 바꾸거나 그룹을
지워도 프로젝트 집계 줄은 사본 이름 그대로 · 연결 없는 구조물은 지금 정의만."""
root, system = world["root"], world["system"]
_define(system, monkeypatch, {"masonry_dry": [{"id": "g1", "이름": "기슭막이", "옵션": []}]})
linked = {**_dry(MS), "group_items": {"g1": RV}}
bare = {**_dry(None), "chainage_m": 125, "start_m": 120, "end_m": 130}
_save(root, [linked, bare], [])
manifest = json.loads((_items(root) / "st0__g1/manifest.json").read_text("utf-8"))
assert manifest["그룹"] == {"id": "g1", "name": "기슭막이"}
_define(system, monkeypatch, {"masonry_dry": [{"id": "g1", "이름": "바뀐 이름", "옵션": []}]})
items = fill.collect_items(root)
assert [(i["복사본"], i.get("그룹")) for i in items] == [
("st0", None),
("st0__g1", "기슭막이"),
("st1", None),
("st1__g1", "바뀐 이름"),
]
_define(system, monkeypatch, {})
items = fill.collect_items(root)
assert [(i["복사본"], i.get("그룹")) for i in items] == [
("st0", None),
("st0__g1", "기슭막이"),
("st1", None),
]