Files
Aislo/resources/tester/test_733_3_library_same_merge.py
T
eomsangdonandClaude Opus 5.5 b9e6f77dab feat(733-3): 서고 중복 찾기 · 합치기 서버 등록 · 지문 모듈 연결 · 시험
- M02_Library_Same_Router — 733-1a 지문(Same 한 벌 · above) 으로 묶음 · 합칠 때 지문 다시 셈(다르면 409)
- main.py 에 라우터 등록(로그인만 · 층 쓰기 권한은 길 안에서)
- 새 시험 test_733_3_library_same_merge(합치기 참조 옮김 · 금액 같음 · 되돌림 · 지문 · 판 409 · 권한 밖 남김 · 위 층 키 · 일반 사용자 회사 층 403 · SC 수량만 다름)
- 새 시험 test_733_3_library_same_screen(부품만 · 날 색 없음 · 나무 메뉴 · 서버 길 짝)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 13:44:00 +09:00

192 lines
8.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ruff: noqa: F811 — 임시 층 fixture(world)를 test_m02_labor_ref 에서 받아 씀
"""서고 [중복 찾기] · 합치기 · 되돌림(PLAN 733-3 · 요구서_572 3장 4) — 임시 storage.
묶음 = 이 층 + 위 층 같은 지문 · 합치기 = 쓰는 곳 참조 → 남김(금액 같음) · 지운 문서 `_합친것/` ·
되돌림 = 문서 · 참조 되살림 · 지문 · 판 다르면 409 · 권한 밖 · 같은 참조 두 번 = 그 문서 남김 ·
위 층과 같은 아래 층 문서 = 남김 = 위 층 키 · 일반 사용자 회사 층 = 403.
"""
import pytest
from fastapi import FastAPI
from fastapi.testclient import TestClient
from common_util.common_util_auth import verify_session
from M02_MasterTemplete import M02_Labor_Ref as refs
from M02_MasterTemplete import M02_Library_Same_Router as same_router
from M02_MasterTemplete import M02_MasterTemplete_Store as store
from M02_MasterTemplete import M02_Sangeun_Calc as calc
from resources.tester.test_m02_labor_ref import COMPANY, MINE, world # noqa: F401
PCT = "GC001109" # 원/㎡ · 인력 둘 + 「노무비 * 3 / 100」
USER = {"user_id": 42, "company_id": 7, "role": "USER", "is_master": False, "name": "갑"}
ADMIN = {**USER, "role": "ADMIN", "name": "관리"}
URL = "/api/m02/layers/{}/sangeun-same"
@pytest.fixture()
def api(world):
app = FastAPI()
app.include_router(same_router.router)
who = dict(USER)
app.dependency_overrides[verify_session] = lambda: who
got = TestClient(app)
got.who = who
return got
def _make(folder, **doc):
with store.at(folder):
return refs.create("sangeun", {"단위": "㎡", "더한줄": [], **doc})["이름"]
def _read(folder, key):
with store.at(folder):
return store.read("sangeun", key)["문서"]
def _total(folder, key):
with store.at(folder):
return calc.calc_doc(store.read("sangeun", key)["문서"])["기준"]["계"]
def _top(ref_keys, at=MINE):
return [{"참조": {**at, "키": k}, "수량": 2} for k in ref_keys] + [
{"고정": {"이름": "운반", "단위": "식", "단가": {"경비": 5000}}, "수량": 1}
]
def _merge(api, keep, gone, 판, layer="personal"):
body = {"남김": keep, "지움": gone, "판": 판}
return api.post(URL.format(layer) + "/merge", json=body)
def test_권한_밖이면_안_지움(api, world, monkeypatch):
me = world["personal"]
a = _make(me, 이름="가", 바탕={"로직": PCT})
c = _make(me, 이름="다", 바탕={"로직": PCT})
top = _make(me, 이름="위", 단위="식", 바탕=None, 더한줄=_top([c]))
monkeypatch.setattr(same_router, "_can_write", lambda session, at: False)
판 = api.get(URL.format("personal")).json()["판"]
res = _merge(api, {**MINE, "키": a}, [c], 판)
left = res.json()["남김"]
assert res.status_code == 200 and left[0]["키"] == c and "권한 밖" in left[0]["쓰는곳"][0]
assert res.json()["합침"] == [] and (me / "sangeun" / f"{c}.json").is_file()
assert _read(me, top)["더한줄"][0]["참조"]["키"] == c
def test_합치기_참조_옮김_금액_같음_되돌림(api, world):
me = world["personal"]
a = _make(me, 이름="밑", 바탕={"로직": PCT})
b = _make(me, 이름="다른 이름 같은 내용", 바탕={"로직": PCT})
_make(me, 이름="다른 내용", 바탕={"로직": PCT}, 더한줄=_top([])[-1:])
top = _make(me, 이름="위", 단위="식", 바탕=None, 더한줄=_top([b]))
before = _total(me, top)
got = api.get(URL.format("personal"))
assert got.status_code == 200, got.text
view = got.json()
assert len(view["묶음"]) == 1
rows = view["묶음"][0]["문서"]
assert [r["키"] for r in rows] == [a, b] and view["묶음"][0]["꼬리표"] == "내용 같음"
assert [r["쓰는곳수"] for r in rows] == [0, 1] and not any(r["위층"] for r in rows)
res = _merge(api, {**MINE, "키": a}, [b], view["판"])
assert res.status_code == 200, res.text
out = res.json()
assert out["합침"] == [{"키": b, "옮긴곳수": 1}] and out["남김"] == []
assert out["묶음"] == [] and [g["키"] for g in out["합친것"]] == [b]
assert not (me / "sangeun" / f"{b}.json").exists()
assert (me / same_router.GONE / f"{b}.json").is_file()
assert _read(me, top)["더한줄"][0]["참조"]["키"] == a
assert _total(me, top) == pytest.approx(before)
back = api.post(URL.format("personal") + "/undo", json={"키": b})
assert back.status_code == 200, back.text
assert back.json()["되돌린곳수"] == 1 and back.json()["합친것"] == []
assert (me / "sangeun" / f"{b}.json").is_file()
assert not (me / same_router.GONE / f"{b}.json").exists()
assert _read(me, top)["더한줄"][0]["참조"]["키"] == b
assert _total(me, top) == pytest.approx(before)
assert api.post(URL.format("personal") + "/undo", json={"키": b}).status_code == 404
def test_지문_판_다르면_409(api, world):
me = world["personal"]
a = _make(me, 이름="가", 바탕={"로직": PCT})
b = _make(me, 이름="나", 바탕={"로직": PCT})
판 = api.get(URL.format("personal")).json()["판"]
assert _merge(api, {**MINE, "키": a}, [b], "옛판").status_code == 409
with store.at(me): # 그새 b 내용이 바뀜
doc = store.read("sangeun", b)
store.write("sangeun", b, doc["판"], {**doc["문서"], "더한줄": _top([])[-1:]})
res = _merge(api, {**MINE, "키": a}, [b], 판)
assert res.status_code == 409 and "달라짐" in res.text
assert (me / "sangeun" / f"{b}.json").is_file()
assert _merge(api, {**MINE, "키": a}, [a], 판).status_code == 400 # 남김 = 지움
assert _merge(api, {**MINE, "키": a}, ["SK-000099"], 판).status_code == 404
def test_권한_밖_남김_둘다_담은_문서(api, world, monkeypatch):
me = world["personal"]
a = _make(me, 이름="가", 바탕={"로직": PCT})
b = _make(me, 이름="나", 바탕={"로직": PCT})
c = _make(me, 이름="다", 바탕={"로직": PCT})
both = _make(me, 이름="둘 다", 단위="식", 바탕=None, 더한줄=_top([a, b]))
before = _total(me, both)
판 = api.get(URL.format("personal")).json()["판"]
res = _merge(api, {**MINE, "키": a}, [b, c], 판) # 둘 다 담은 문서 = 두 줄 모두 a · 금액 같음
assert res.status_code == 200, res.text
assert [r["키"] for r in res.json()["합침"]] == [b, c]
assert [x["참조"]["키"] for x in _read(me, both)["더한줄"][:2]] == [a, a]
assert _total(me, both) == pytest.approx(before)
for key in (b, c):
assert api.post(URL.format("personal") + "/undo", json={"키": key}).status_code == 200
def test_위층과_같으면_남김_위층_키(api, world):
me, firm = world["personal"], world["company"]
up = _make(firm, 이름="회사 것", 바탕={"로직": PCT})
mine = _make(me, 이름="내 것", 바탕={"로직": PCT})
top = _make(me, 이름="위", 단위="식", 바탕=None, 더한줄=_top([mine]))
before = _total(me, top)
view = api.get(URL.format("personal")).json()
rows = view["묶음"][0]["문서"]
assert [(r["층"], r["키"], r["위층"]) for r in rows] == [
("company", up, True),
("personal", mine, False),
]
res = _merge(api, {**COMPANY, "키": up}, [mine], view["판"])
assert res.status_code == 200, res.text
assert _read(me, top)["더한줄"][0]["참조"] == {**COMPANY, "키": up}
assert _total(me, top) == pytest.approx(before)
assert (firm / "sangeun" / f"{up}.json").is_file()
# 위 층 문서는 지울 수 없음 · 일반 사용자는 회사 층 합치기 403
assert _merge(api, {**MINE, "키": mine}, [up], view["판"]).status_code in (403, 404)
assert api.get(URL.format("company")).status_code == 403
api.who.update(ADMIN)
assert api.get(URL.format("company")).status_code == 200
def test_일위대가_수량만_다름_꼬리표(api, world):
me = world["personal"]
line = {"고정": {"이름": "운반", "단위": "식", "단가": {"경비": 5000}}, "수량": None}
with store.at(me):
keys = [
refs.create("precombo", {"이름": n, "단위": "m", "담은로직": [line], "본뜬": src})[
"이름"
]
for n, src in (
(
"수로BOX 4.0×2.0",
{"층": "실무자료", "키": "HP-1 제1호표", "실무수량": {"줄": {"0": 3}}},
),
(
"수로BOX 2.5×2.5",
{"층": "실무자료", "키": "HP-1 제2호표", "실무수량": {"줄": {"0": 7}}},
),
)
]
got = api.get("/api/m02/layers/personal/precombo-same")
assert got.status_code == 200, got.text
group = got.json()["묶음"][0]
assert group["꼬리표"] == "수량만 다름" and [r["키"] for r in group["문서"]] == keys