"""PLAN 821-2 M01 기계 저장 — 지킴 시험(임시 사본 폴더만 · 정본 안 씀). ① 새 기계 = 단위 · 출처 자동(마스터 단위 · 「자체」) · 계수 서버 재계산 · 다른 줄 그대로 ② 고침 = 그 칸 한 줄만 · 품셈 입력이 같으면 저장 계수 그대로(점보드릴 777) · 바뀌면 다시 셈 ③ 기계 길은 시스템 관리자만(아니면 403) · 양식 목록 · 비슷한 기계 찾기 · 쓰는 곳 """ from __future__ import annotations import difflib import json import shutil import sys from pathlib import Path import pytest from fastapi import Depends, FastAPI from fastapi.testclient import TestClient from common_util import common_util_auth as auth from M01_MasterData import M01_MasterData_Router as router_module from M01_MasterData import M01_MasterData_Store as store sys.path.insert( 0, str(Path(__file__).resolve().parents[2] / "resources" / "master_data" / "scripts") ) import master_machine_coef as mc # noqa: E402 REAL = store.FOLDER MACHINE = "기계.json" @pytest.fixture def copy(tmp_path: Path, monkeypatch) -> Path: for path in REAL.glob("*.json"): if not path.name.startswith("_") or path.name == store.mk.BOOK.name: shutil.copy(path, tmp_path / path.name) monkeypatch.setattr(store, "FOLDER", tmp_path) return tmp_path def _app(role: str = "SYSTEM_ADMIN") -> TestClient: app = FastAPI() app.dependency_overrides[auth.verify_session] = lambda: { "user_id": 1, "name": "시험", "role": role, "company_id": 1, } deps = [Depends(auth.verify_session), Depends(auth.require_system_admin)] app.include_router(router_module.router, dependencies=deps) return TestClient(app) def _save(client: TestClient, version: str, change: dict) -> None: res = client.post( "/api/m01/save", json={"files": [{"file": MACHINE, "version": version, "changes": [change]}]}, ) assert res.status_code == 200, res.text def _diff(before: str, after: str) -> list[str]: return [ x for x in difflib.unified_diff(before.splitlines(), after.splitlines(), lineterm="", n=0) if x[:1] in "+-" and not x.startswith(("+++", "---")) ] def test_새_기계_단위_출처_자동_계수_서버_재계산(copy: Path) -> None: client = _app() lists = client.get("/api/m01/machine/lists").json() assert {"경유", "휘발유"} <= {f["value"] for f in lists["fuels"]} assert any(j["value"] == "1048" for j in lists["jobs"]) and lists["hour"] == 0.125 found = client.get("/api/m01/machine/find", params={"q": "굴착기 1.0㎥"}).json()["rows"] seed = next(r for r in found if r["키"] == "EQ000022") before = (copy / MACHINE).read_text(encoding="utf-8") version = client.get("/api/m01/machine/row", params={"key": "EQ000022"}).json()["version"] values = {**seed["값"], "내용시간": 8000, "손료계수": 1, "상각비계수": 1} # 계수 = 틀린 값 row = { "키": "", "원문번호": "", **{k: seed[k] for k in ("원문", "구분", "상세구분")}, "이름": "시험기계sub1", "규격": "9.9㎥", "단위": {}, "값": values, "출처": {}, "비고": None, } _save(client, version, {"op": "add", "row": row}) after = (copy / MACHINE).read_text(encoding="utf-8") rows = json.loads(after)["줄"] new = rows[-1] assert new["이름"] == "시험기계sub1" and new["키"].startswith("EQ") assert new["값"] | mc.coef(8000, 1250, 0.9, 0.7, 0.1) == new["값"] # 서버가 다시 셈 assert new["값"]["손료계수"] == 1125 + 875 + 496 assert set(new["단위"]) == set(new["값"]) == set(new["출처"]) assert new["단위"] == { k: seed_unit for k, seed_unit in _units(before).items() if k in new["값"] } assert set(new["출처"].values()) == {"자체"} assert json.loads(before)["줄"] == rows[:-1] # 다른 줄 그대로 assert all(x.startswith("+") for x in _diff(before, after)) # 글은 더하기만 def _units(text: str) -> dict: return next(r for r in json.loads(text)["줄"] if r["키"] == "EQ000022")["단위"] def _edit(client: TestClient, key: str, change) -> tuple[dict, dict]: got = client.get("/api/m01/machine/row", params={"key": key}).json() row = got["row"] _save(client, got["version"], {"op": "edit", "key": key, "row": change(row)}) now = client.get("/api/m01/machine/row", params={"key": key}).json()["row"] return row, now def test_고침_그_줄_그_칸만(copy: Path) -> None: client = _app() before = (copy / MACHINE).read_text(encoding="utf-8") row, now = _edit(client, "EQ000022", lambda r: {**r, "값": {**r["값"], "가격": 150000}}) assert now == {**row, "값": {**row["값"], "가격": 150000}} lines = _diff(before, (copy / MACHINE).read_text(encoding="utf-8")) assert len(lines) == 2 and "142069" in lines[0] and "150000" in lines[1] # 그 칸 한 줄만 def test_입력_같으면_저장_계수_그대로_바뀌면_다시_셈(copy: Path) -> None: client = _app() # 가격만 고침 + 화면이 계수를 틀리게 보내도 — 입력이 같으면 원문 표 값(정비비계수 777) 그대로 row, now = _edit( client, "EQ000347", lambda r: {**r, "값": {**r["값"], "가격": 999, "정비비계수": 778, "손료계수": 1}}, ) assert now["값"] == {**row["값"], "가격": 999} _, now = _edit(client, "EQ000347", lambda r: {**r, "값": {**r["값"], "내용시간": 10000}}) assert {k: now["값"][k] for k in mc.COEFS} == mc.coef(10000, 800, 0.9, 0.7, 0.1) def test_기계_길은_시스템_관리자만() -> None: for role in ("USER", "COMPANY_ADMIN"): client = _app(role) assert client.get("/api/m01/machine/lists").status_code == 403 assert client.get("/api/m01/machine/hourly", params={"keys": "EQ000022"}).status_code == 403 assert client.post("/api/m01/save", json={"files": []}).status_code == 403 def test_쓰는_곳_찾기(copy: Path, monkeypatch) -> None: from M02_MasterTemplete import M02_Template_Layers as layers monkeypatch.setattr(layers, "layer_files", lambda kind: []) # 공유 storage 안 읽음 uses = _app().get("/api/m01/machine/uses", params={"key": "EQ000022"}).json()["uses"] assert any("GC000267" in u for u in uses) assert _app().get("/api/m01/machine/uses", params={"key": "EQ999999"}).status_code == 404