"""일위대가 복사(PLAN 39-1b B5 · B6) — 항목 하나 복사(같은 층 · 다른 층 · 실무문서 조합) · 구조물 조합 복사 · 분류 더하기 = 새 일위대가번(조합 키는 안 받음). 시스템 층 = 견본 표(임시 폴더) · 조합 · 로직은 M01 마스터(읽기만) · 개인 층 = 임시 storage. """ from __future__ import annotations import json from pathlib import Path from typing import Any import pytest from fastapi import FastAPI from fastapi.testclient import TestClient from common_util.common_util_auth import verify_session from config import config_system from M02_MasterTemplete import M02_MasterTemplete_Items as items_store from M02_MasterTemplete import M02_MasterTemplete_Router_Items as items_router from M02_MasterTemplete import M02_MasterTemplete_Store as store from M02_MasterTemplete import M02_Template_Layers as layers from resources.tester.test_m02_structure import write_sample_table TEN = "GC000421" URL = "/api/m02/layers/{}/{}/{}/copy" @pytest.fixture() def world(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> dict[str, Any]: system = tmp_path / "system" system.mkdir() write_sample_table(system) monkeypatch.setattr(store, "FOLDER", system) monkeypatch.setattr(layers, "SYSTEM_ROOT", system) (tmp_path / "storage").mkdir() monkeypatch.setattr(config_system, "STORAGE_BASE_DIR", str(tmp_path / "storage")) with store.at(system): for cid in ("rv", "c1"): store.create_structure(cid) got = store.read("labor", "rv") logic = [{"로직": TEN, "입력값": {}, "수량": {"손": 2}}] store.write("labor", "rv", got["판"], {**got["문서"], "단위": "개소", "담은로직": logic}) personal = layers.personal_dir(7, 42) items_store.ensure(personal) with store.at(personal): got = store.read("table", store.STRUCTURE_TABLE) got["문서"]["열"] = [{"id": "a", "머리": ["돌", "가"], "꼴": "수"}] store.write("table", store.STRUCTURE_TABLE, got["판"], got["문서"]) session: dict[str, Any] = {"user_id": 42, "company_id": 7, "role": "USER", "is_master": False} app = FastAPI() app.include_router(items_router.router) app.dependency_overrides[verify_session] = lambda: session return {"client": TestClient(app), "session": session, "system": system, "personal": personal} def _as(world: dict, role: str = "USER") -> TestClient: world["session"]["role"] = role return world["client"] def _labor(folder: Path, cid: str) -> dict: return json.loads((folder / "labor" / f"{cid}.json").read_bytes()) def test_같은_층_일위대가_하나_복사_번호_열은_받는_쪽(world): system = world["system"] before = _labor(system, "c1") got = _as(world, "SYSTEM_ADMIN").post(URL.format("system", "labor", "c1") + "?from=rv") assert got.status_code == 200 c1 = _labor(system, "c1") assert (c1["열"], c1["일위대가번"]) == ("c1", before["일위대가번"]) assert (c1["단위"], c1["담은로직"][0]["로직"]) == ("개소", TEN) assert c1["계산값"]["반영합"]["계"] > 0 # 서버가 다시 셈 def test_다른_층에서_내_개인으로_저장_뒤에도_덮어씀(world): client, personal = _as(world), world["personal"] url = URL.format("personal", "labor", "a") + "?from=rv&from_layer=system" for _ in range(2): # 저장된 데이터도 다시 바꿈 assert client.post(url).status_code == 200 assert _labor(personal, "a")["담은로직"][0]["로직"] == TEN area = URL.format("personal", "area", "a") + "?from=rv&from_layer=system" assert client.post(area).status_code == 200 def test_권한_없는_원본_층_403_없는_원본_404(world): client = _as(world) other = URL.format("personal", "labor", "a") + "?from=x&from_layer=personal&from_owner=7/99" assert client.post(other).status_code == 403 missing = URL.format("personal", "labor", "a") + "?from=없는열&from_layer=system" assert client.post(missing).status_code == 404 # 시스템 층 쓰기는 시스템 관리자만 assert client.post(URL.format("system", "labor", "c1") + "?from=rv").status_code == 403 def test_실무문서_조합에서_일위대가로_시스템_관리자만(world): url = URL.format("system", "labor", "c1") + "?from=UA000008&from_layer=practice" assert _as(world).post(url).status_code == 403 admin = _as(world, "SYSTEM_ADMIN") assert admin.post(url).status_code == 200 c1 = _labor(world["system"], "c1") assert c1["본뜬"] == {"층": "system", "주인": None, "키": "UA000008"} assert len(c1["담은로직"]) == 14 and all(x["입력값"] == {} for x in c1["담은로직"]) area = URL.format("system", "area", "c1") + "?from=UA000008&from_layer=practice" assert admin.post(area).status_code == 403 # 실무문서는 일위대가로만 bad = URL.format("system", "labor", "c1") + "?from=UP1-000001&from_layer=practice" assert admin.post(bad).status_code == 400 def test_구조물_조합_복사는_새_일위대가번_조합_키는_안_받음(world): system, personal = world["system"], world["personal"] with store.at(system): key = next(r["키"] for r in store.read_combos()["줄"] if r["열"] == "rv") body = {"from_layer": "system", "key": key} made = _as(world).post("/api/m02/layers/personal/items/copy", json=body).json() got = _labor(personal, made["열"]) src = _labor(system, "rv") assert got["담은로직"] == src["담은로직"] and got["일위대가번"] != src["일위대가번"] assert got["일위대가번"].startswith("SU-") table = json.loads((personal / "table" / f"{store.STRUCTURE_TABLE}.json").read_bytes()) assert next(c for c in table["열"] if c["id"] == made["열"]).get("일위대가") is None def test_분류_더하기_복사도_새_번호로_일위대가_문서(world): system = world["system"] with store.at(system): got = store.read("table", store.STRUCTURE_TABLE) doc = got["문서"] rv = next(c for c in doc["열"] if c["id"] == "rv") doc["열"].append({**rv, "id": "rv2", "본뜬열": "rv", "일위대가": None}) store.write("table", store.STRUCTURE_TABLE, got["판"], doc) new, src = _labor(system, "rv2"), _labor(system, "rv") assert new["담은로직"] == src["담은로직"] and new["일위대가번"] != src["일위대가번"]