"""회사에 올리기 요청 · 승인(PLAN 706-1 · `M02_Share_Request`) — 임시 storage. ① 일반 사용자 요청(구조물 항목 · 사전조합) → 장부 한 줄 · 같은 것 다시 = 409 「요청 중」. ② 회사 관리자 승인 = 회사 층 고른 폴더에 사본(원본 개인 것 그대로) · 거절 = 사유 · 요청자 알림 → 봄. ③ 권한 — 일반 승인 403 · 다른 회사 관리자는 그 요청을 못 봄(404) · 남의 회사 장부 안 봄 · 목록은 자기 것만. """ from __future__ import annotations import json from typing import Any from fastapi import FastAPI from fastapi.testclient import TestClient from common_util.common_util_auth import verify_session from M02_MasterTemplete import M02_Labor_Router as labor_router from M02_MasterTemplete import M02_MasterTemplete_Router_Items as items_router from M02_MasterTemplete import M02_MasterTemplete_Router_Layers as layers_router from M02_MasterTemplete import M02_Share_Request as share from resources.tester import test_m02_tree as base from resources.tester.test_m02_tree import Tree world = base.world # 같은 임시 storage 판 URL = "/api/m02/share-requests" def _client(world: dict[str, Any]) -> TestClient: app = FastAPI() for one in (layers_router, items_router, labor_router, share): app.include_router(one.router) app.dependency_overrides[verify_session] = lambda: world["session"] return TestClient(app) def _as(world: dict[str, Any], role: str = "USER", user: int = 42, company: int = 7) -> None: world["session"].update(role=role, user_id=user, company_id=company, name=f"사람{user}") def _mine(world: dict[str, Any], client: TestClient) -> tuple[str, str]: """일반 사용자 42 개인 층 — 항목 「배수/sub7임시」 · 사전조합 하나 → (항목 키, 사전조합 키).""" _as(world) t = Tree(client, "personal") t.do(op="add-folder", 경로=[], 이름="배수") t.do(op="add-item", 경로=["배수"], 이름="sub7임시", 단위="m") doc = { "문서": {"이름": "sub7임시 사전", "구분": "C 사면안정", "상세구분": "옹벽", "담은로직": []} } pre = client.post("/api/m02/layers/personal/library/precombo", json=doc).json()["이름"] return t.item(["배수", "sub7임시"])["key"], pre def test_요청_승인_거절_알림(world): client = _client(world) item, pre = _mine(world, client) got = client.post(URL, json={"종류": "item", "키": item, "글": " 회사 공용으로 "}) assert got.status_code == 200, got.text one = got.json() assert (one["이름"], one["경로"], one["주인"], one["글"]) == ( "sub7임시", ["배수"], "7/42", "회사 공용으로", ) assert client.post(URL, json={"종류": "item", "키": item}).status_code == 409 # 요청 중 two = client.post(URL, json={"종류": "precombo", "키": pre}).json() assert client.post(URL, json={"종류": "item", "키": "SP42-999999"}).status_code == 404 assert client.post(URL, json={"종류": "precombo", "키": "SC-999999"}).status_code == 404 mine = client.get(URL).json() assert mine["관리"] is False and [r["id"] for r in mine["줄"]] == [one["id"], two["id"]] _as(world, "ADMIN", user=50) # 회사 7 관리자 company = Tree(client, "company") company.do(op="add-folder", 경로=[], 이름="공용") boss = client.get(URL).json() assert boss["관리"] is True and len(boss["줄"]) == 2 ok = client.post(f"{URL}/{one['id']}/approve", json={"경로": ["공용"]}) assert ok.status_code == 200, ok.text assert ok.json()["상태"] == "승인" and ok.json()["봄"] is False company = Tree(client, "company") assert ["공용", "sub7임시"] in company.paths() # 고른 폴더에 사본 again = client.post(f"{URL}/{one['id']}/approve", json={"경로": []}) assert again.status_code == 409 # 이미 승인 no = client.post(f"{URL}/{two['id']}/reject", json={"사유": "이름 다시"}) assert no.json()["상태"] == "거절" and no.json()["사유"] == "이름 다시" _as(world) # 요청자 — 원본 그대로 · 결과 둘 · 봄 뒤 0 assert ["배수", "sub7임시"] in Tree(client, "personal").paths() rows = client.get(URL).json()["줄"] assert [(r["상태"], r["봄"]) for r in rows] == [("승인", False), ("거절", False)] assert client.post(f"{URL}/seen").json() == {"봄": 2} assert client.post(f"{URL}/seen").json() == {"봄": 0} # 거절된 것은 다시 요청할 수 있음 assert client.post(URL, json={"종류": "precombo", "키": pre}).status_code == 200 def test_사전조합_승인_폴더_자리(world): client = _client(world) _, pre = _mine(world, client) rid = client.post(URL, json={"종류": "precombo", "키": pre}).json()["id"] _as(world, "ADMIN", user=50) tree_url = "/api/m02/layers/company/precombo-tree" now = client.get(tree_url).json() client.post(tree_url, json={"op": "add-folder", "경로": [], "이름": "옹벽", "판": now["판"]}) made = client.post(f"{URL}/{rid}/approve", json={"경로": ["옹벽"]}).json()["사본"] assert client.get(tree_url).json()["자리"] == {made: ["옹벽"]} doc = client.get(f"/api/m02/layers/company/templates/precombo/{made}").json()["문서"] assert doc["이름"] == "sub7임시 사전" assert doc["본뜬"]["주인"] == "7/42" # 원본 표시 def test_권한_일반_승인_403_다른_회사(world): client = _client(world) item, _ = _mine(world, client) rid = client.post(URL, json={"종류": "item", "키": item}).json()["id"] assert client.post(f"{URL}/{rid}/approve", json={}).status_code == 403 # 일반 = 승인 못 함 assert client.post(f"{URL}/{rid}/reject", json={}).status_code == 403 _as(world, "USER", user=43) # 같은 회사 다른 일반 — 남의 요청 안 보임 assert client.get(URL).json()["줄"] == [] _as(world, "ADMIN", user=90, company=9) # 다른 회사 관리자 assert client.get(URL).json()["줄"] == [] assert client.post(f"{URL}/{rid}/approve", json={}).status_code == 404 # 자기 장부에 없음 assert client.get(URL, params={"company": "7"}).json()["회사"] == "9" # 남의 회사 고르기 무시 _as(world, "SYSTEM_ADMIN", user=1, company=None) assert len(client.get(URL, params={"company": "7"}).json()["줄"]) == 1 ledger = world["storage"] / "7" / "templates" / share.FILE assert json.loads(ledger.read_bytes())["줄"][0]["상태"] == "요청"