Files
Aislo/resources/tester/test_733_3_library_same_merge.py
T
eomsangdonandClaude Opus 5.5 4c8a06559a feat(740-1): 단가산출 · 일위대가 서로 독립 — 넣기 펼침(M01 키 줄 + 덮개 사본 · 본뜬) · 초기화 길 · 옮기기 함수 · B09 덮개 줄 굳힘
- 새 M02_Library_Flat: SK 에 SK = 더한줄 로직 한 줄에 원본 몸 통째(덮개 · 뺀줄 · 고름 · 더한줄 · % 덧줄까지 같은 금액) · SC 에 SK = 로직 줄 + 패치 · SK 에 SC = 줄마다 더한줄(수량 = 저장 수량.손 › 실무수량 › 1) · 바탕 SK · 패치 SK 참조 = 덮개 겹침 · 줄 본뜬 {층, 주인, 키, 이름, 때}
- 저장 · 읽기 · 층 복사 때 서버가 펼침(못 펼친 줄 = 옛 참조 그대로) · SC 안 SC(하위 호표)는 저장 · 읽기에서 참조 그대로(엑셀 하위 호표 끝수 · B09 하위 대가) · 찾아 넣기 길만 펴 넣음
- 길: POST {kind}-flat(넣을 줄 · 못 펼침 400) · POST {kind}-reset(미리 보기 걷힐 수 · 씀)
- 초기화 = 덮개 · 뺀줄 · 더한줄 단가 걷음(안쪽 넣은 몸까지) · 항목 · 수량 · 입력값 · 고름 남김
- 옮기기 = migrate(apply) · 전후 1 단위 금액 같을 때만 씀 · 공유 storage 는 브레인
- 둘째 기초 세부 셈 = 넣은 SK 몸이면 이름 없는 SK 로(Sangeun_Calc.sub_run)
- B09: SC 사본 덮개 줄 = 굳힌 줄(굳힌 {키, 줄 …}) → 사본 셈 · 대가 블록이 덮개를 읽음(옛 산근 길은 덮개 무시 · 실측 6,451 대 M02 210,866)
- 733 지문 = 옛 참조 줄 펼친 꼴로 대조
- 낡은 시험 고침(까닭 = 단가산출이 낀 참조가 이제 펼쳐 담김): 715 store(바탕 · 사전조합 SK · 층 복사) · 717-2a 목록 바탕 · 717-4 SC 안 SK · 719-1 원본판(원본 고쳐도 그대로) · 719-2 속 일위대가(빠진 줄 없음 · 쓰지 않는 _calc_given 지움) · 725-1 SK 안 SC · 729 SK 줄 본뜬 · 733-3 쓰는 곳 = 구조물 조합(SK 안 SK 참조 없음) · 572 지문 대조 대상

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

215 lines
9.3 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 · 권한 밖 · 같은 참조 두 번 = 그 문서 남김 ·
쓰는 곳 = 구조물 조합(740-1 — 단가산출 · 일위대가끼리는 펼쳐 담겨 참조 없음) ·
위 층과 같은 아래 층 문서 = 남김 = 위 층 키 · 일반 사용자 회사 층 = 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 _user(folder, ref_keys, at=MINE, col="x1"):
"""쓰는 곳 = 구조물 조합(740-1 — 단가산출 · 일위대가끼리는 펼쳐 담겨 참조 없음)."""
lines = [{**x, "수량": {"손": x["수량"]}} for x in _top(ref_keys, at)]
with store.at(folder):
store.write("labor", col, "", {"열": col, "담은로직": lines, "자재": []})
return col
def _lines(folder, col):
with store.at(folder):
return store.read("labor", col)["문서"]["담은로직"]
def _sum(folder, col):
from M02_MasterTemplete import M02_Labor_Doc as labor
with store.at(folder):
doc = labor.clean({**store.read("labor", col)["문서"], "열": col})
return float(labor.calc(doc, {})["반영합"]["계"])
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 = _user(me, [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 _lines(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 = _user(me, [b])
before = _sum(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 _lines(me, top)[0]["참조"]["키"] == a
assert _sum(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 _lines(me, top)[0]["참조"]["키"] == b
assert _sum(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 = _user(me, [a, b])
before = _sum(me, both)
판 = api.get(URL.format("personal")).json()["판"]
# 둘 다 담은 구조물 조합 = 같은 참조 두 번이 되어 못 옮김 → b 남김 · c 만 합침 · 금액 같음
res = _merge(api, {**MINE, "키": a}, [b, c], 판)
assert res.status_code == 200, res.text
assert [r["키"] for r in res.json()["합침"]] == [c]
assert [r["키"] for r in res.json()["남김"]] == [b]
assert [x["참조"]["키"] for x in _lines(me, both)[:2]] == [a, b]
assert _sum(me, both) == pytest.approx(before)
assert api.post(URL.format("personal") + "/undo", json={"키": c}).status_code == 200
def test_위층과_같으면_남김_위층_키(api, world):
me, firm = world["personal"], world["company"]
up = _make(firm, 이름="회사 것", 바탕={"로직": PCT})
mine = _make(me, 이름="내 것", 바탕={"로직": PCT})
top = _user(me, [mine])
before = _sum(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 _lines(me, top)[0]["참조"] == {**COMPANY, "키": up}
assert _sum(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