"""263-2 운반 값 사본 — 사전조합 · 구조물 조합 담은 로직 거리 칸 = 거리이정표 거리 · 같은 값 = 사본 하나 · 다른 거리 = 사본 둘 · 거리 바뀐 뒤 셈 = 새 사본 · 옛 사본 prune · 옛 이름(해시 없음) 폴더 읽힘 · 손 값(own) 이김.""" import shutil import sys from pathlib import Path import pytest ROOT = Path(__file__).resolve().parents[2] sys.path.insert(0, str(ROOT)) from B08_Quantity.B08_Quantity_Haul_Cycle import cycle_time # noqa: E402 from B09_Estimation import B09_Estimation_Bill_Library as library # noqa: E402 from B09_Estimation.B09_Estimation_Labor_Source import daega_inputs # noqa: E402 from B09_Estimation.B09_Estimation_Store import _clean_ref # noqa: E402 from M02_MasterTemplete import M02_Labor_Ref as refs # noqa: E402 KEY = "SC-999263" DOC = { "종류": "사전조합", "키": KEY, "이름": "시험 덤프운반 조합", "단위": "㎥", "담은로직": [ {"로직": "GF000284", "입력값": {"재료": "토사", "지역": "전국평균", "보정작업": "해당 없음"}, "수량": {"손": 1}}, {"로직": "GF000134", "입력값": {"qt": 15, "f": 1, "E": 0.9, "지역": "전국평균"}, "수량": {"손": 1}}, ], } # fmt: skip NEAR = {"L1": 10, "L2": 2} FAR = {"L1": 30, "L2": 2} LID = f"{refs.ref_id({'층': 'system', '주인': None, '키': KEY})}#0" LID2 = LID[:-1] + "1" @pytest.fixture(autouse=True) def fake_precombo(monkeypatch): real = refs.read_ref monkeypatch.setattr(refs, "read_ref", lambda ref: DOC if ref.get("키") == KEY else real(ref)) def _import(root, dist, given=None, own=None): haul = {"given": given, "own": own, "dist": dist} return library.import_copy(root, "precombo", "system", None, KEY, None, haul)["폴더"] def _given(root, folder): files = library._read_copy(library.library_dir(root) / folder) manifest = library.read_json(library.library_dir(root) / folder / library.MANIFEST_NAME) return manifest.get("값") or {}, files def test_거리_칸_채움_같은_값_사본_하나_다른_거리_둘(tmp_path): a = _import(tmp_path, NEAR) b = _import(tmp_path, NEAR) c = _import(tmp_path, FAR) assert a == b and a != c and "~" in a values, _ = _given(tmp_path, a) assert values[LID] == {"운반거리": 12} # L1 + L2 assert values[LID2] == {"Cm": cycle_time(NEAR)["cm"]} # 사본 안 로직 줄 입력값 = 덮은 값 · 원값 = 덮기 전(없던 칸) labor = library.read_json(library.library_dir(tmp_path) / c / library.LABOR_FILE) doc = labor["참조사본"][LID.split("#")[0]] assert ( doc["담은로직"][0]["입력값"]["운반거리"] == 32 and doc["담은로직"][0]["입력값"]["재료"] == "토사" ) # 대가 블록 둘(값 다름 = 블록 다름) libs = daega_inputs(tmp_path)["libs"] assert set(libs) == {a, c} and libs[a] != libs[c] def test_손_값_이김(tmp_path): own = [f"{LID}|운반거리"] folder = _import(tmp_path, NEAR, {LID: {"운반거리": 5, "재료": "암절취"}}, own) values, _ = _given(tmp_path, folder) assert values[LID] == {"운반거리": 5, "재료": "암절취"} lines = library.library_inputs(tmp_path, "precombo", "system", None, KEY, folder) first = next(x for x in lines if x["id"] == LID) assert first["로직"] == "GF000284" and first["값"]["재료"] == "토사" # 덮기 전 값 assert [i["이름"] for i in first["입력"]] == ["재료", "운반거리", "지역", "보정작업"] def test_거리_바뀐_뒤_셈_새_사본_옛_사본_prune(tmp_path): old = _import(tmp_path, NEAR) ref = {"tab": "dg", "key": "DG:x", "lib": old, "given": {}, "own": []} user = {"haul_refs": {"haul:r-u1": ref}, "haul_dist": {"haul:r-u1": FAR}} calc = library.resolve_hauls(tmp_path, user) new = calc["haul_refs"]["haul:r-u1"]["lib"] assert new != old and new == _import(tmp_path, FAR) libs = daega_inputs(tmp_path)["libs"] library.fix_keys(calc, libs) assert calc["haul_refs"]["haul:r-u1"]["key"] == libs[new] assert library.prune(tmp_path, calc) == [old] # 같은 거리 다시 = 그대로 assert library.resolve_hauls(tmp_path, user)["haul_refs"]["haul:r-u1"]["lib"] == new # 설계내역서 덮개 줄도 같이 bill = {"bill": {"rows": [{"key": "haul:r-u1", "ref": ref}]}, "haul_dist": {"haul:r-u1": NEAR}} assert library.resolve_hauls(tmp_path, bill)["bill"]["rows"][0]["ref"]["lib"] == old def test_옛_이름_폴더_읽힘(tmp_path): hashed = _import(tmp_path, NEAR) plain = library.import_copy(tmp_path, "precombo", "system", None, KEY)["폴더"] assert "~" not in plain and library.NAME.match(plain) ref = {"tab": "dg", "key": "DG:x", "lib": plain} assert library.resolve(tmp_path, ref, NEAR) == hashed # 옛 사본에서 거리 값 사본 shutil.rmtree(library.library_dir(tmp_path) / hashed) assert library.resolve(tmp_path, ref, NEAR) == hashed # 없으면 옛 사본에서 다시 만듦 assert library.resolve(tmp_path, ref, None) == plain # 거리 없음 = 그대로 assert {f.name for f, _m in library.copies(tmp_path)} == {plain, hashed} def test_연결_손_값_저장_꼴(): ref = {"tab": "dg", "key": "DG:x", "lib": "precombo@system@-@SC-1~abcdef12", "given": {LID: {"운반거리": 5, "x": [1]}}, "own": [f"{LID}|운반거리", 3]} # fmt: skip got = _clean_ref(ref) assert got["given"] == {LID: {"운반거리": 5}} and got["own"] == [f"{LID}|운반거리"] assert "given" not in _clean_ref({"tab": "dg", "key": "DG:x"}) def test_있는_사본에서_다시_적용(tmp_path, monkeypatch): near = _import(tmp_path, NEAR) monkeypatch.setattr(refs, "read_ref", lambda ref: None) # 원본 지워짐 = 굳힌 사본에서 haul = {"given": {}, "own": [], "dist": FAR, "lib": near} got = library.import_copy(tmp_path, "precombo", "system", None, KEY, None, haul) values, _ = _given(tmp_path, got["폴더"]) assert got["폴더"] != near and values[LID] == {"운반거리": 32} def test_모달_조합_입력(): modal = (ROOT / "B09_Estimation/B09_Estimation_UI_Haul_Modal.ts").read_text("utf-8") for want in ("fetchLibInputs(p)", "libRows()", "st.libOwn.has(id)", "parseLib(", "importLib(o, { kind, layer, owner, key, lib, haul })"): # fmt: skip assert want in modal, want link = (ROOT / "B09_Estimation/B09_Estimation_UI_Tabs_Bill_Link.ts").read_text("utf-8") assert '"library-inputs"' in link and "...lib.haul" in link