Files
Aislo/resources/tester/test_m01_auto.py
T
eomsangdonandClaude Opus 5.5 bc094969ac feat(M01): 44-5 체인톱 가격 · 오일 자재품목 연결 — 손 입력 칸 걷음
- GF000002 작업로 설치 · GF000056 어린나무 가꾸기 · GF000076 방제 벌목조재 — 입력 체인톱가격 · 체인오일단가 걷음
- 체인오일 = 호표 재료 줄(검색어 「작동유」 → 기어작동유 4,612원/ℓ) · 체인톱 손료 = 호표 재료 줄(검색어 「동력체인톱」 → 기본 400mm 530,000원 · 비목 경비 · 규격 고름 가능)
- 잡품 = 휘발유 줄 금액 × 40%(주⑤ · 오일이 재료비에 들어와 식 대상 좁힘)
- 같은 단가면 원가 전후 같음(GF000002 계 454,056) · 손 입력 빈칸 견본 대비 체인오일 · 손료가 채워짐
- 시험 — test_m01_auto 빈 원 입력 시험을 GF000051 예취기가격으로 옮김 · 체인톱 자재 시험 추가 · ui_checks/44-5_체인톱_자재.js

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EYJJRCfHfdCGNhmXTjQKV7
2026-10-05 11:31:58 +09:00

160 lines
7.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""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, "GF000051") # 예취기가격(원)
assert got["예취기가격"] is None
assert got["조림목본수"] == 0 # 원 아닌 수는 그대로 0
def test_빈_입력을_쓰는_줄만_비움(client) -> None:
got = _calc(client, "GF000051")
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, "GF000051").items() if k != "예취기가격"}
for path in ("calc", "text"):
got = client.post(f"/api/m01/{path}", json={"key": "GF000051", "inputs": given}).json()
assert got["ok"] is True, (path, got)
lines = client.post("/api/m01/calc", json={"key": "GF000051", "inputs": given}).json()["lines"]
assert {o["이름"] for o in lines if o.get("까닭")} == {"예취기 손료"}
@pytest.mark.parametrize("key", ["GF000002", "GF000056", "GF000076"])
def test_체인톱_가격_오일은_자재품목(client, key) -> None:
"""44-5 — 체인톱 가격 = 「동력체인톱」(기본 첫 규격) · 체인오일 = 「작동유」 · 손 입력 없음."""
assert not {"체인톱가격", "체인오일단가"} & set(_auto(client, key))
lines = {o["이름"]: o for o in _calc(client, key)["lines"]}
assert lines["체인톱 손료"]["품목"]["키"] == "MT016587" # 400mm · 530,000원
assert lines["체인톱 손료"]["단가"] == 530000 and "경비" in lines["체인톱 손료"]["비목"]
assert lines["체인오일"]["품목"]["키"] == "MT000583" # 기어작동유 · 4,612원/ℓ
assert lines["체인오일"]["단가"] == 4612
gas = Decimal(str(lines["휘발유(체인톱)"]["금액"]))
assert abs(Decimal(str(lines["잡품"]["금액"])) - gas * Decimal("0.4")) < Decimal(
"0.001"
) # 잡품 = 휘발유값 40% 그대로
def test_체인톱_규격은_고를_수_있음(client) -> None:
given = _auto(client, "GF000056")
body = {"key": "GF000056", "inputs": given, "고름": {"4": "MT016588"}} # 4 = 체인톱 손료 줄
got = client.post("/api/m01/calc", json=body).json()
saw = next(o for o in got["lines"] if o["이름"] == "체인톱 손료")
assert saw["품목"]["키"] == "MT016588" and saw["단가"] == 900000
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