- GET /api/m02/structure-definition/history · /history/{판}(로그인 누구나 · 없는 판 404)
- 화면이 만드는 고침 한 벌을 서버가 받음 · 판 +1 · 403 · 422 시험(사본 정의)
- 옛 옵션 보이기 · 그룹 시험의 화면 글 검사를 정의 화면 자리로
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016PSXsvyDxjNmufteN5bqmt
112 lines
5.0 KiB
Python
112 lines
5.0 KiB
Python
"""M02 구조물 옵션 그룹(`/api/m02/structure-option-groups` · 정의 「그룹」 칸 · PLAN 46-2).
|
|
|
|
못박는 것
|
|
- 「그룹」 이 `{}` 면 그룹 없음 · `/structure-types` 종류마다 `groups: []`.
|
|
- 저장한 그룹이 `/structure-types` `groups` 에 그대로(차례 · 이름 · 옵션) · 빈 그룹도 나감.
|
|
- 옵션 없는 종류도 그룹을 둘 수 있음(관리 목록 `종류` 는 레지스트리 전부).
|
|
- 없는 종류 · 키 · id 틀림 · 겹침 · 이름 빔 · 겹침 · 옵션 두 그룹은 422 · 낡은 판 409 ·
|
|
거절되면 안 씀.
|
|
- 좌측 「구조물 옵션 보이기」 는 시스템 관리자만(화면 코드).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
from fastapi import FastAPI
|
|
from fastapi.testclient import TestClient
|
|
|
|
from B05_Profile import B05_Profile_Structures_Schema as schema
|
|
from B05_Profile.B05_Profile_Structures_Router import router as b05_router
|
|
from B05_Profile.B05_Profile_Structures_Schema import load_structure_types
|
|
from M02_MasterTemplete import M02_MasterTemplete_Store as store
|
|
from M02_MasterTemplete.StructOptions.M02_SO_Router import router as m02_router
|
|
|
|
ROOT = Path(__file__).resolve().parents[2]
|
|
URL = "/api/m02/structure-option-groups"
|
|
TYPES = "/api/projects/structure-types"
|
|
|
|
|
|
@pytest.fixture
|
|
def client(tmp_path: Path, monkeypatch) -> TestClient:
|
|
"""정의 사본(표시 · 그룹 빈 칸 · 판 1)으로 — 마스터 정본은 안 씀(PLAN 48-1)."""
|
|
monkeypatch.setattr(store, "FOLDER", tmp_path)
|
|
doc = json.loads(Path(schema.DEFINITION_PATH).read_text("utf-8"))
|
|
copy = tmp_path / "구조물정의.json"
|
|
copy.write_text(json.dumps({**doc, "판": 1, "표시": {}, "그룹": {}}), "utf-8")
|
|
monkeypatch.setattr(schema, "DEFINITION_PATH", str(copy))
|
|
app = FastAPI()
|
|
app.include_router(m02_router)
|
|
app.include_router(b05_router)
|
|
return TestClient(app)
|
|
|
|
|
|
def _two_options() -> tuple[str, str, str]:
|
|
t = next(t for t in load_structure_types() if len(t.options) >= 2)
|
|
return t.type_id, t.options[0].key, t.options[1].key
|
|
|
|
|
|
def _groups(client: TestClient) -> dict[str, list[dict]]:
|
|
return {t["type_id"]: t["groups"] for t in client.get(TYPES).json()["types"]}
|
|
|
|
|
|
def test_빈_그룹_칸은_그룹_없음(client) -> None:
|
|
got = client.get(URL).json()
|
|
assert got["판"] == "1" and got["그룹"] == {}
|
|
assert len(got["종류"]) == len(load_structure_types()) # 옵션 없는 종류도
|
|
assert all(v == [] for v in _groups(client).values())
|
|
|
|
|
|
def test_저장한_그룹이_폼_응답에_차례대로_빈_그룹도(client) -> None:
|
|
tid, a, b = _two_options()
|
|
empty = next(t.type_id for t in load_structure_types() if not t.options)
|
|
body = {
|
|
tid: [
|
|
{"id": "g1", "이름": "유입구", "옵션": [b]},
|
|
{"id": "g2", "이름": " 빈 그룹 ", "옵션": []},
|
|
],
|
|
empty: [{"id": "gx", "이름": "부속", "옵션": []}],
|
|
}
|
|
res = client.put(URL, json={"판": "1", "그룹": body})
|
|
assert res.status_code == 200, res.text
|
|
assert res.json()["그룹"][tid][1]["이름"] == "빈 그룹" # 앞뒤 빈칸 지움
|
|
seen = _groups(client)
|
|
assert seen[tid] == [
|
|
{"id": "g1", "name": "유입구", "options": [b]},
|
|
{"id": "g2", "name": "빈 그룹", "options": []},
|
|
]
|
|
assert seen[empty] == [{"id": "gx", "name": "부속", "options": []}]
|
|
assert a not in seen[tid][0]["options"] # 안 넣은 옵션 = 본체
|
|
|
|
|
|
def test_틀린_그룹은_422_낡은_판은_409(client) -> None:
|
|
tid, a, b = _two_options()
|
|
ok = {"id": "g1", "이름": "가", "옵션": [a]}
|
|
for bad in (
|
|
{"없는종류": [ok]},
|
|
{tid: [{**ok, "옵션": ["없는키"]}]},
|
|
{tid: [{**ok, "id": "G-1"}]},
|
|
{tid: [ok, {**ok, "이름": "나", "옵션": []}]}, # id 겹침
|
|
{tid: [{**ok, "이름": " "}]},
|
|
{tid: [ok, {"id": "g2", "이름": "가", "옵션": []}]}, # 이름 겹침
|
|
{tid: [ok, {"id": "g2", "이름": "나", "옵션": [a]}]}, # 옵션 두 그룹
|
|
{tid: {"g1": ok}},
|
|
):
|
|
assert client.put(URL, json={"판": "1", "그룹": bad}).status_code == 422, bad
|
|
assert json.loads(Path(schema.DEFINITION_PATH).read_text("utf-8"))["판"] == 1 # 거절되면 안 씀
|
|
v1 = client.put(URL, json={"판": "1", "그룹": {tid: [ok]}}).json()["판"]
|
|
assert client.put(URL, json={"판": "낡음", "그룹": {}}).status_code == 409
|
|
res = client.put(URL, json={"판": v1, "그룹": {}})
|
|
assert res.status_code == 200 and res.json()["그룹"] == {}
|
|
|
|
|
|
def test_좌측_정의_줄은_시스템_층에_늘_고쳐_씀은_시스템_관리자만() -> None:
|
|
"""PLAN 48-2 — 다른 역할은 읽기만(화면이 칸을 잠금 · 서버 PUT 403)."""
|
|
side = (ROOT / "M02_MasterTemplete/M02_MasterTemplete_UI_Side.ts").read_text("utf-8")
|
|
assert "옵션: section(options.title, options.body)," in side
|
|
folder = ROOT / "M02_MasterTemplete/StructOptions"
|
|
assert "buildGroupBlock(" in (folder / "M02_SO_Def.ts").read_text("utf-8")
|
|
assert "buildPlaceSelect(" in (folder / "M02_SO_Def_Options.ts").read_text("utf-8")
|