"""703-1 B09 재료 창고 「지난 공사」 — 같은 회사 다른 공사 일위대가 → 이 공사 서고 사본(덮개째). - 목록 = 그 공사 서고 사본 · 구조물 사본 · 고친 칸 수(그 공사 저장 덮개) · 같은 것 한 줄 - 가져옴 = 사본 폴더 통째 → `library/…~해시` · 가져온곳 · 덮개 = 그 공사 칸 · 다시 = 같은 사본 - 새 호표(`libs`) · 같은 꼴 블록이 이 공사에 먼저 있으면 그 블록 cover - 모양 틀림 400 · 없음 404 · 다른 회사 403 · 이 공사 400 임시 층(견본 마스터 · 임시 storage) · 실제 마스터 · 프로젝트는 안 씀. """ import asyncio import json import sys from pathlib import Path import pytest from fastapi import HTTPException ROOT = Path(__file__).resolve().parents[2] if str(ROOT) not in sys.path: sys.path.insert(0, str(ROOT)) from B09_Estimation import B09_Estimation_Bill_Library as library # noqa: E402 from B09_Estimation import B09_Estimation_Past as past # noqa: E402 from B09_Estimation import B09_Estimation_Router as router # noqa: E402 from B09_Estimation import B09_Estimation_Store as store_b09 # noqa: E402 from B09_Estimation.B09_Estimation_Labor_Source import daega_inputs # noqa: E402 from config import config_system # noqa: E402 from M02_MasterTemplete import M02_Labor_Ref as refs # noqa: E402 from M02_MasterTemplete import M02_MasterTemplete_Store as store # noqa: E402 from M02_MasterTemplete import M02_Template_Layers as layers # noqa: E402 from resources.tester.helper_m02_master_sample import copy_master_sample # noqa: E402 STONE = "GF000219" # 돌쌓기 메쌓기 원/㎡ @pytest.fixture() def world(tmp_path, monkeypatch): system = copy_master_sample(tmp_path / "master") monkeypatch.setattr(store, "FOLDER", system) monkeypatch.setattr(layers, "SYSTEM_ROOT", system) monkeypatch.setattr(config_system, "STORAGE_BASE_DIR", str(tmp_path / "storage")) with store.at(system): key = refs.create( "precombo", {"이름": "지난 조합", "단위": "㎡", "담은로직": [{"로직": STONE, "수량": {"손": 2}}]}, )["이름"] a, b = (tmp_path / "storage" / "7" / "1" / n for n in ("past", "here")) a.mkdir(parents=True) b.mkdir(parents=True) folder = library.import_copy(a, "precombo", "system", None, key)["폴더"] ref = {"층": "system", "주인": None, "키": key} cover = { f"C:{refs.ref_id(ref)}": {"sangeun:GF#0": {"qty": 5}}, "K:x": {"@copy": {"from": "S:y"}}, } store_b09.write_user(a, {"daega_cover": cover}) return {"key": key, "a": a, "b": b, "folder": folder} def test_목록_서고_사본_고친_칸_수_같은_것_한_줄(world): a = world["a"] rows = past.rows_of(a) assert [(r["cover"], r["name"], r["unit"], r["kind"], r["covers"]) for r in rows] == [ (f"L:{world['folder']}", "지난 조합", "㎡", "사전조합", 1) ] # 같은 원본 · 같은 값 · 덮개 없음 = 한 줄로 셈(값 해시 다른 사본) library.import_copy(a, "precombo", "system", None, world["key"], haul={"given": {}}) assert len(past.rows_of(a)) == 1 def test_가져옴_사본_통째_덮개째_새_호표_다시는_같은_사본(world): a, b, folder = world["a"], world["b"], world["folder"] ref = {"project_id": "P-A", "프로젝트": "지난"} made = past.import_past(b, a, ref, f"L:{folder}", {"이름": "나"}) name = made["폴더"] assert made["만듦"] and made["덮개"] == {"sangeun:GF#0": {"qty": 5}} assert name.startswith(f"precombo@system@-@{world['key']}~") and library.NAME.match(name) copy = library.library_dir(b) / name src = library.library_dir(a) / folder for f in ("일위대가.json", "단가.json"): assert (copy / f).read_bytes() == (src / f).read_bytes() manifest = json.loads((copy / "manifest.json").read_text("utf-8")) assert manifest["가져온곳"] == {**ref, "사본": f"L:{folder}"} and manifest["종류"] == "precombo" got = daega_inputs(b) bid = got["libs"][name] block = next(x for x in got["blocks"] if x["id"] == bid) # 사전조합 덮개 = 참조(공사 안 같은 사전조합 한 덮개) — 덮개자리 = 그 cover assert (block["name"], block["cover"]) == ("지난 조합", made["덮개자리"]) assert made["덮개자리"] == "C:참조|system||" + world["key"] again = past.import_past(b, a, ref, f"L:{folder}") assert (again["폴더"], again["만듦"]) == (name, False) def test_같은_꼴_블록이_먼저_있으면_그_cover(world): a, b = world["a"], world["b"] mine = library.import_copy(b, "precombo", "system", None, world["key"])["폴더"] name = past.import_past(b, a, {"project_id": "P-A"}, f"L:{world['folder']}")["폴더"] got = daega_inputs(b) assert got["libs"][name] == got["libs"][mine] block = next(x for x in got["blocks"] if x["id"] == got["libs"][name]) assert block["cover"].startswith("C:") # 사전조합 = 같은 참조 한 덮개(화면이 빈 때만 얹음) def test_구조물조합_사본_덮개자리_새_폴더(world): a, b = world["a"], world["b"] folder = library.import_copy(a, "combo", "system", None, "SS000011")["폴더"] store_b09.write_user(a, {"daega_cover": {f"L:{folder}": {"x#0": {"qty": 2}}}}) row = next(r for r in past.rows_of(a) if r["cover"] == f"L:{folder}") assert (row["kind"], row["covers"]) == ("구조물 조합", 1) made = past.import_past(b, a, {"project_id": "P-A"}, f"L:{folder}") assert made["덮개자리"] == f"L:{made['폴더']}" and made["덮개"] == {"x#0": {"qty": 2}} got = daega_inputs(b) block = next(x for x in got["blocks"] if x["id"] == got["libs"][made["폴더"]]) assert block["cover"] == made["덮개자리"] def test_모양_틀림_없음(world): a, b = world["a"], world["b"] for bad in ("L:../x", "X:y", "S:../../z"): with pytest.raises(ValueError): past.import_past(b, a, {}, bad) with pytest.raises(LookupError): past.import_past(b, a, {}, "S:nope") with pytest.raises(LookupError): past.import_past(b, a, {}, "L:precombo@system@-@SC-000999") def test_길_다른_회사_403_이_공사_400(world, monkeypatch): rows = { "here": {"company_id": 7, "root": world["b"]}, "far": {"company_id": 8, "root": world["a"]}, } async def project(_session, pid): return {"name": pid, **rows["here" if pid.startswith("0") else "far"]} monkeypatch.setattr(router, "_project", project) pid = router.UUID("00000000-0000-0000-0000-000000000001") body = router.PastBody(project_id="far", cover=f"L:{world['folder']}") with pytest.raises(HTTPException) as e: asyncio.run(router.past_import(pid, body, {"user_id": 1})) assert e.value.status_code == 403 same = router.PastBody(project_id=str(pid), cover="L:x") with pytest.raises(HTTPException) as e: asyncio.run(router.past_import(pid, same, {"user_id": 1})) assert e.value.status_code == 400 assert not library.library_dir(world["b"]).exists() # 아무것도 안 씀