"""M01 견본 입력(`GET /logic/auto`) · 값 빈 품목은 그 줄만 비움 — PLAN 3-2 · 계약 3-1 표. 자동값 = 고르기 목록 첫째 · 범위 최솟값 · 찾기 조건 값은 그 표 가장 낮은 줄 · 나누는 수 1 · 그 밖 0. 정본 폴더를 읽기만 함(쓰지 않음). """ from __future__ import annotations from decimal import Decimal import pytest from fastapi import FastAPI from fastapi.testclient import TestClient from M01_MasterData import M01_MasterData_Router as router_module from M01_MasterData import M01_MasterData_Store as store @pytest.fixture(scope="module") def client() -> TestClient: app = FastAPI() app.include_router(router_module.router) return TestClient(app) def _auto(client: TestClient, key: str) -> dict: got = client.get("/api/m01/logic/auto", params={"key": key}) assert got.status_code == 200, got.text return got.json()["값"] def _calc(client: TestClient, key: str) -> dict: got = client.post("/api/m01/calc", json={"key": key, "inputs": _auto(client, key)}).json() assert got["ok"] is True, got return got def test_고르기는_목록_첫째_범위는_최솟값(client) -> None: got = _auto(client, "GF000160") # 12-4 합판거푸집 assert got["횟수"] == "1회사용시" and got["폼타이"] == "아님" assert "합판" not in got # 재료 입력 키 목록은 걷음 — 품목은 기본 검색어(PLAN 8-3) assert got["소형할증"] == 0 # 범위 [0, 30] assert got["높이할증"] == 0 # 찾기·나누기에 안 쓰임 def test_찾기_조건_값은_표_가장_낮은_줄(client) -> None: assert _auto(client, "GC000561")["층수층"] == 0 # 가장 낮은 줄 「6 미만」 — 아래 끝이 열림 assert _auto(client, "GC000559")["강재총사용량"] == 0 # 「60 미만」 # 두 표가 같은 입력을 씀 — 한 표는 6 부터 · 한 표는 15 부터 → 둘 다에 드는 가장 낮은 값 assert _auto(client, "GC000724")["규격"] == 15 def test_나누는_수는_1(client) -> None: assert _auto(client, "GC000998")["수직고초과단계"] == 1 assert _auto(client, "GC000278")["두께"] == 1 def test_없는_로직은_404(client) -> None: assert client.get("/api/m01/logic/auto", params={"key": "GF999999"}).status_code == 404 def test_견본_금액은_앞과_같음(client) -> None: assert Decimal(str(_calc(client, "GF000160")["sums"]["계"])) == Decimal("117485.212") assert Decimal(str(_calc(client, "GF000219")["sums"]["계"])) == Decimal("79279.668") def test_후보_0_값_빈_품목은_그_줄만_비움(client) -> None: got = _calc(client, "GC000027") # 가새(후보 0) · 발판(값 없음) blank = {one["이름"]: one for one in got["lines"] if one.get("까닭")} assert len(blank) == 2 for one in blank.values(): assert one["단가"] is None and one["금액"] is None assert "후보 없음" in blank["가새 L1518-2개"]["까닭"] assert "값 없음" in blank["발판 45×200×2000"]["까닭"] rest = sum(Decimal(str(one["금액"])) for one in got["lines"] if one["금액"] is not None) assert Decimal(str(got["sums"]["계"])) == rest > 0 # 빈 줄은 빼고 더함 def test_빈_줄을_부르는_덧줄도_비움(client) -> None: got = _calc(client, "GF000185") # 레미콘 후보 0 → 레미콘 금액을 부르는 덧줄 blank = [one for one in got["lines"] if one.get("까닭")] assert [one["이름"] for one in blank] == ["구체콘크리트(철근) 레미콘", "콘크리트다짐"] assert "구체콘크리트(철근) 레미콘" in blank[1]["까닭"] and blank[1]["금액"] is None def test_자동값으로_로직_전부_멈춤_0() -> None: files, whole = store.loaded() stuck = [] for data in files.values(): if data.get("그룹") != "로직": continue for row in data.get("줄") or []: try: store.cm.mf.run(whole, str(row["키"]), store.mau.values(whole, row)) except (store.cm.mf.FormulaError, ArithmeticError, KeyError, TypeError) as e: stuck.append(f"{row['키']} {e}") assert stuck == [] def test_원_단위_입력은_자동값_null(client) -> None: got = _auto(client, "GF000056") # 체인톱가격(원) · 체인오일단가(원/ℓ) assert got["체인톱가격"] is None and got["체인오일단가"] is None assert got["소작업로"] == 0 # 원 아닌 수는 그대로 0 def test_빈_입력을_쓰는_줄만_비움(client) -> None: got = _calc(client, "GF000056") blank = {one["이름"]: one for one in got["lines"] if one.get("까닭")} assert set(blank) == {"체인오일", "체인톱 손료"} for one in blank.values(): assert one["단가"] is None and one["금액"] is None and "비어 있음" in one["까닭"] rest = sum(Decimal(str(one["금액"])) for one in got["lines"] if one["금액"] is not None) assert Decimal(str(got["sums"]["계"])) == rest > 0 # 빈 줄은 빼고 더함 assert got["sums"]["경비"] == 0 def test_빠진_원_입력도_null_처럼_줄만_비움(client) -> None: given = { k: v for k, v in _auto(client, "GF000056").items() if k not in ("체인톱가격", "체인오일단가") } for path in ("calc", "text"): got = client.post(f"/api/m01/{path}", json={"key": "GF000056", "inputs": given}).json() assert got["ok"] is True, (path, got) lines = client.post("/api/m01/calc", json={"key": "GF000056", "inputs": given}).json()["lines"] assert {o["이름"] for o in lines if o.get("까닭")} == {"체인오일", "체인톱 손료"} def test_표_찾기_실패는_그_줄만_비우고_까닭은_날글_없이(client) -> None: given = {**_auto(client, "GC001000"), "시공구분": "기계시공"} # 표 1-2-3 에 그 조합 줄이 없음 got = client.post("/api/m01/calc", json={"key": "GC001000", "inputs": given}).json() assert got["ok"] is True, got blank = [one for one in got["lines"] if one.get("까닭")] assert blank and all(one["금액"] is None for one in blank) why = blank[0]["까닭"] assert "표 QC001869(1-2-3)에 시공구분 기계시공" in why and "인 줄이 없음" in why assert not any(ch in why for ch in "{}'") and "Decimal" not in why rest = sum(Decimal(str(one["금액"])) for one in got["lines"] if one["금액"] is not None) assert Decimal(str(got["sums"]["계"])) == rest