- GET /api/m01/logic/auto — 고르기 목록 첫째 · 범위 최솟값 · 찾기 조건 값은 그 표 가장 낮은 줄 · 나누는 수 1 · 그 밖 0 (master_auto.py) - run — 후보 0 · 값 빈 품목(덧줄 포함)은 단가·금액 null + 까닭 · 소계·계는 빈 줄 빼고 더함 · 없는 키는 지금처럼 멈춤 - 자동값으로 로직 1,354 전부 계산 · 멈춤 0 · 시험 test_m01_auto.py Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LcqZtEzEf1sYSU4FUmVLNJ
97 lines
4.0 KiB
Python
97 lines
4.0 KiB
Python
"""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["폼타이"] == "아님" and got["합판"] == "MT000018"
|
||
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 == []
|