"""717-1 상세 부품 공용 — 길 주입(TS) · M02 `logic-lib` 읽기 · 셈 길(로그인 · 쓰기 없음). TS = `helper_717_1_logic_api_paths.cjs`(가짜 fetch 주소) · 서버 = 정본 읽기만(파일 지문 같음). """ from __future__ import annotations import hashlib import json import subprocess from pathlib import Path import pytest from fastapi import Depends, FastAPI from fastapi.testclient import TestClient from common_util.common_util_auth import verify_session from M01_MasterData import M01_MasterData_Store as store from M02_MasterTemplete import M02_Logic_Lib_Read as lib ROOT = Path(__file__).resolve().parents[2] HELPER = Path(__file__).parent / "helper_717_1_logic_api_paths.cjs" KEY = "GF000001" # 경계표시 — 보통인부 한 줄 BASE = "/api/m02/logic-lib" @pytest.mark.skipif(not (ROOT / "config" / "node_modules").is_dir(), reason="의존성 미설치") def test_길_주입_없으면_m01_있으면_넘긴_길() -> None: done = subprocess.run( # noqa: S603 — 고정 실행 파일 ["node", str(HELPER)], cwd=str(ROOT), capture_output=True, text=True, encoding="utf-8" ) assert done.returncode == 0, done.stderr out = json.loads(done.stdout) assert out["before"] == out["after"] # 안 넘기면 지금 그대로 assert all(u.startswith("/m01/") for u in out["before"]) shared, admin = out["injected"][:-2], out["injected"][-2:] assert all(u.startswith(("/m02/logic-lib/", "/m02/calc-x/")) for u in shared) assert [u for u in shared if u.startswith("/m02/calc-x/")] == [ "/m02/calc-x/calc", "/m02/calc-x/text", ] assert admin == ["/m01/groups", "/m01/save"] # 관리자 · 저장 길은 늘 M01 @pytest.fixture(scope="module") def user() -> TestClient: """main.py 와 같은 등록(로그인만) · 일반 사용자.""" app = FastAPI() app.include_router(lib.router, dependencies=[Depends(verify_session)]) app.dependency_overrides[verify_session] = lambda: { "user_id": 42, "company_id": 7, "role": "USER", "is_master": False, } # fmt: skip return TestClient(app) def _prints() -> dict[str, str]: return {p.name: hashlib.sha256(p.read_bytes()).hexdigest() for p in store.FOLDER.glob("*.json")} def test_일반_계정_읽기(user: TestClient) -> None: assert user.get(f"{BASE}/logic/auto", params={"key": KEY}).status_code == 200 assert user.get(f"{BASE}/elements", params={"group": "인력", "q": "LB000002"}).json()["items"] assert user.get(f"{BASE}/pick", params={"kind": "price", "q": "시멘트"}).status_code == 200 assert user.get(f"{BASE}/rows", params={"file": "기계.json", "size": 3}).json()["rows"] assert user.get(f"{BASE}/materials", params={"sub": "시멘트"}).status_code == 200 flow = user.get(f"{BASE}/logic/flow", params={"key": KEY}) assert flow.status_code == 200 and "flow" in flow.json() found = user.get(f"{BASE}/elements", params={"group": "소요량", "q": "QF000025"}).json() file = next(i["file"] for i in found["items"] if i["ref"] == "QF000025") assert user.get(f"{BASE}/table", params={"file": file, "key": "QF000025"}).json()["table"] def test_요소_표_밖_파일은_막음(user: TestClient) -> None: logic_file = store.logics("", "", "", None, "")[0]["file"] assert user.get(f"{BASE}/rows", params={"file": logic_file}).status_code == 403 assert user.get(f"{BASE}/table", params={"file": "기계.json", "key": "x"}).status_code == 403 assert user.get(f"{BASE}/rows", params={"file": "재료_../x.json"}).status_code == 404 def test_저장_안_한_줄_셈_쓰기_없음(user: TestClient) -> None: before = _prints() saved = user.post(f"{BASE}/calc", json={"key": KEY, "inputs": {}}).json() row = store.logic(KEY)["logic"] same = user.post(f"{BASE}/calc", json={"key": KEY, "inputs": {}, "row": row}).json() line = row["호표"][0] row2 = {**row, "호표": [{**line, "수량": f"({line['수량']}) * 2"}, *row["호표"][1:]]} double = user.post(f"{BASE}/calc", json={"key": KEY, "inputs": {}, "row": row2}).json() text = user.post(f"{BASE}/text", json={"key": KEY, "inputs": {}, "row": row2}).json() assert saved["ok"] and same["sums"] == saved["sums"] # 751-3 — 줄 0.1 원 · 소계 1 원 버림이라 두 배는 1 원 안 assert double["sums"]["노무비"] == pytest.approx(saved["sums"]["노무비"] * 2, abs=2) assert text["ok"] and text["계"] == pytest.approx(double["sums"]["계"], rel=1e-6) assert store.logic(KEY)["logic"]["호표"][0]["수량"] == line["수량"] # 정본 그대로 assert _prints() == before