Files
Aislo/resources/tester/test_735_1_library_delete.py
T
eomsangdonandClaude Opus 5.5 6893599fa9 feat(740-1): 일위대가 안 일위대가 = 사본 블록(원본 연결 끊음 · 하위 호표 끝수 그대로) · 초기화 이음 기계 다시 받음
- SC 안 SC = 안쪽 SC 몸 통째를 그 줄 참조 꼴 {층: 사본, 주인: 몸 지문, 키: 본 SC, 몸} 에 담음(Labor_Ref.copy_ref · read_ref 가 몸을 돌려줌 → 셈 · 알약 · B09 하위 대가 블록 · 지문 = 옛 참조 줄과 같은 길) · 엑셀 대조 거창 같음 14 그대로 · 같은 기초 겹침도 블록 안(400 없음)
- 저장 · 읽기 · 옮기기 · 찾아 넣기 모두 사본 블록 · 자기 자신을 담은 줄 = 안 펼침(저장 422 그대로)
- 단가산출에 SC 넣기 = 사본 블록 줄도 더한줄로 펼침(바깥 × 안 수량)
- 초기화 = 사본 몸 안까지 · 이음 기계 고정 줄(736-3) 단가 = M01 기계 한 시간 값 다시 받음
- 옮기기 = 쓰는 곳 검사 걷음(같은 자리 한 줄이라 안 줄 알약 차례 그대로) · 실 storage 보기만 = 1/3 SC-000002 사본 블록 · 전후 1,446,078 같음
- 낡은 시험 고침(까닭 = 하위 호표 참조가 사본 블록으로): m02_labor_ref(순환 대신 사본 · 복사 딸림 없음) · 733-2(딸림 재사용 = 구조물 조합 복사) · 735-1(쓰는 곳 = 구조물 조합 둘) · hopyo_build · 707-7(하위 호표 줄 = 사본 블록 · 본뜬)

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

182 lines
8.7 KiB
Python

# ruff: noqa: F811 — 임시 층 fixture(world)를 test_m02_labor_ref 에서 받아 씀
"""서고 나무 지우기 = 경고 뒤 지움 · 쓰는 곳 그 줄도 지움(PLAN 735-1 · 요구 574) — 임시 storage.
미리 보기(쓰는 줄 = 종류 · 이름 · 키 · 줄) · 확인 없으면 409 목록 · 확인 = 그 줄 지움
(금액 다시 셈) + 문서 지움 + `_합친것/` 기록 · 되돌림(문서 · 자리 · 고친 문서 앞판 ·
그새 고친 문서는 건너뜀) ·
권한 밖 409(아무것도 안 바뀜) · 폴더 통째 = 한 기록 · 단가산출 바탕 · 더한줄 열쇠 당김(`strip`).
"""
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_Labor_Router as labor_router
from M02_MasterTemplete import M02_Library_Delete as deleting
from M02_MasterTemplete import M02_Library_Same_Router as same_router
from M02_MasterTemplete import M02_MasterTemplete_Store as store
from resources.tester.test_m02_labor_ref import MINE, world # noqa: F401
USER = {"user_id": 42, "company_id": 7, "role": "USER", "is_master": False, "name": "갑"}
TREE = "/api/m02/layers/personal/precombo-tree"
SAME = "/api/m02/layers/personal/precombo-same"
SK_TREE = "/api/m02/layers/personal/sangeun-tree"
SK_SAME = "/api/m02/layers/personal/sangeun-same"
FIX = {"고정": {"이름": "운반", "단위": "식", "단가": {"경비": 5000}}, "수량": {"손": 1}}
@pytest.fixture()
def api(world):
app = FastAPI()
app.include_router(labor_router.router)
app.include_router(same_router.router)
app.dependency_overrides[verify_session] = lambda: USER
return TestClient(app)
def _sc(folder, name, lines):
with store.at(folder):
return refs.create("precombo", {"이름": name, "단위": "m", "담은로직": lines})["이름"]
def _labor(folder, lines):
with store.at(folder):
store.write("labor", "x1", "", {"열": "x1", "담은로직": lines, "자재": []})
def _read(folder, kind, key):
with store.at(folder):
return store.read(kind, key)["문서"]
def _sum(folder):
return _read(folder, "labor", "x1")["계산값"]["반영합"]["계"]
def _tree(api, url=TREE, **body):
판 = api.get(url).json()["판"]
return api.post(url, json={"판": 판, **body})
def _world(world):
"""단가산출 하나(운반 3000 · 1 단위)를 구조물 조합 x1(2 단위) · 일위대가가 물게."""
me = world["personal"]
line = {"고정": {"이름": "운반", "단위": "식", "단가": {"경비": 3000}}, "수량": 1}
with store.at(me):
doc = {"이름": "임시 단가산출", "단위": "m", "바탕": None, "더한줄": [line]}
sk = refs.create("sangeun", doc)["이름"]
top = _sc(me, "위 일위대가", [{"참조": {**MINE, "키": sk}}, FIX])
_labor(me, [{"참조": {**MINE, "키": sk}, "수량": {"손": 2}}, FIX])
return me, sk, top
def test_미리보기_확인없으면_409_확인하면_그줄_지움_금액_다시셈_되돌림(api, world):
me, sk, top = _world(world)
before = _sum(me)
assert before == pytest.approx(5000 + 2 * 3000)
got = api.post(SK_TREE + "/delete-check", json={"대상": [{"키": sk}]})
assert got.status_code == 200, got.text
rows = got.json()["쓰는곳"]
assert got.json()["문서"] == [sk]
assert sorted((r["종류"], r["이름"], r["키"], r["줄"], r["쓰기"]) for r in rows) == [
("구조물 조합", "x1", sk, "1째", True),
("일위대가", "위 일위대가", sk, "1째", True),
]
res = _tree(api, SK_TREE, op="delete", 대상=[{"키": sk}])
assert res.status_code == 409 and len(res.json()["detail"]["쓰는곳"]) == 2
assert (me / "sangeun" / f"{sk}.json").is_file()
res = _tree(api, SK_TREE, op="delete", 대상=[{"키": sk}], 확인=True)
assert res.status_code == 200, res.text
assert res.json()["알림"]["기록"] == sk
assert not (me / "sangeun" / f"{sk}.json").exists()
assert [x.get("고정", {}).get("이름") for x in _read(me, "labor", "x1")["담은로직"]] == ["운반"]
assert len(_read(me, "precombo", top)["담은로직"]) == 1
assert _sum(me) == pytest.approx(5000) # 서버가 다시 셈
gone = api.get(SK_SAME).json()["합친것"]
assert [(g["키"], g["남김"], g["옮긴곳수"]) for g in gone] == [(sk, None, 2)]
back = api.post(SK_SAME + "/undo", json={"키": sk})
assert back.status_code == 200, back.text
assert back.json()["되돌린곳수"] == 2 and back.json()["못한곳"] == []
assert (me / "sangeun" / f"{sk}.json").is_file()
assert _read(me, "labor", "x1")["담은로직"][0]["참조"]["키"] == sk
assert _sum(me) == pytest.approx(before)
assert api.get(SK_SAME).json()["합친것"] == []
def test_쓰는곳_없으면_그냥_지움_기록은_남음(api, world):
me = world["personal"]
sc = _sc(me, "혼자", [FIX])
assert api.post(TREE + "/delete-check", json={"대상": [{"키": sc}]}).json()["쓰는곳"] == []
assert _tree(api, op="delete", 대상=[{"키": sc}]).status_code == 200
assert not (me / "precombo" / f"{sc}.json").exists()
assert api.post(SAME + "/undo", json={"키": sc}).status_code == 200
assert (me / "precombo" / f"{sc}.json").is_file()
def test_권한_밖이면_409_목록_아무것도_안_바뀜(api, world, monkeypatch):
me, sk, _top = _world(world)
monkeypatch.setattr(deleting, "can_write", lambda session, at: False)
res = _tree(api, SK_TREE, op="delete", 대상=[{"키": sk}], 확인=True)
assert res.status_code == 409
detail = res.json()["detail"]
assert "권한 밖" in detail["까닭"] and "x1" in detail["까닭"] and sk in detail["까닭"]
assert {r["이름"] for r in detail["막힘"]} == {"x1", "위 일위대가"}
assert (me / "sangeun" / f"{sk}.json").is_file()
assert len(_read(me, "labor", "x1")["담은로직"]) == 2
def test_폴더_통째_한_기록_되돌림_자리까지_그새_고친_문서는_건너뜀(api, world):
me = world["personal"]
a = _sc(me, "가", [FIX])
b = _sc(me, "나", [FIX])
_labor(me, [{"참조": {**MINE, "키": a}, "수량": {"손": 1}}, FIX])
with store.at(me): # 쓰는 곳 = 구조물 조합(740-1 — 일위대가 안 일위대가는 사본 블록)
other = [{"참조": {**MINE, "키": b}, "수량": {"손": 1}}, FIX]
store.write("labor", "x2", "", {"열": "x2", "담은로직": other, "자재": []})
assert _tree(api, op="add-folder", 경로=[], 이름="묶음").status_code == 200
for key in (a, b):
assert _tree(api, op="move", 키=key, 새경로=["묶음"]).status_code == 200
check = api.post(TREE + "/delete-check", json={"대상": [{"경로": ["묶음"]}]}).json()
assert sorted(check["문서"]) == [a, b]
assert sorted(r["키"] for r in check["쓰는곳"]) == [a, b]
res = _tree(api, op="delete", 대상=[{"경로": ["묶음"]}], 확인=True)
assert res.status_code == 200, res.text
name = res.json()["알림"]["기록"]
assert [g["키"] for g in api.get(SAME).json()["합친것"]] == [name]
with store.at(me): # 그새 x2 를 고침 → 그 문서는 앞판으로 안 돌림
have = store.read("labor", "x2")
lines = [{**x, "메모": "고침"} for x in have["문서"]["담은로직"]]
store.write("labor", "x2", have["판"], {**have["문서"], "담은로직": lines})
back = api.post(SAME + "/undo", json={"키": name}).json()
assert back["되돌린곳수"] == 1 and "그새 고침" in back["못한곳"][0]
assert (me / "precombo" / f"{a}.json").is_file() and (me / "precombo" / f"{b}.json").is_file()
assert api.get(TREE).json()["자리"] == {a: ["묶음"], b: ["묶음"]}
assert _read(me, "labor", "x1")["담은로직"][0]["참조"]["키"] == a
def test_단가산출_바탕_더한줄_지움_열쇠_당김():
gone = {**MINE, "키": "SK-000002"}
keep = {**MINE, "키": "SK-000003"}
doc = {
"바탕": {"참조": gone},
"열린입력": ["높이"],
"더한줄": [{"참조": gone, "수량": 1}, {"참조": keep, "수량": 1}, {"종류": "로직"}],
"덮개": {"labor:A#1": {"qty": 1}, "add:#1": {"배율": 2}, "add:#2/labor:B#1": {"qty": 3}},
"뺀줄": ["labor:A#2", "add:#0"],
"고름": {"add:#2/material:C#1": "MT1"},
"자재표시": {},
}
out = deleting.strip(doc, {refs.ref_id(gone)})
assert out["바탕"] is None and out["열린입력"] == []
assert [x.get("참조") for x in out["더한줄"]] == [keep, None]
assert out["덮개"] == {"add:#0": {"배율": 2}, "add:#1/labor:B#1": {"qty": 3}}
assert out["뺀줄"] == []
assert out["고름"] == {"add:#1/material:C#1": "MT1"}
assert deleting.strip(doc, set()) == doc # 안 걸리면 그대로