Files
Aislo/resources/tester/test_m01_auto.py
T
eomsangdonandClaude Opus 5.5 3f581ca41b feat(m01): 로직 재료 줄 키 → 기본 검색어 바꿔 넣기 (PLAN 8-3)
- 재료 줄 591 → {구분, 상세구분, 검색어} · 재료 입력 키 목록 76 걷음 · 넘기던 인자 뗌
- 결속선 등 원문대로 고른 품목(어닐링철선)이 검색 첫째 그대로
- 1,354 자동값 금액 변화 0 · 계산전부 멈춤 0 · check_master 로직 0 · 단위 16 그대로
- 못 맞춘 26줄은 옛 대표 · 조건 그대로: 값 빈 자리표 MT0306… 14 · 같은 단위 후보 0 빈 줄 12
- 도구: 빈 줄은 안 바꿈 · 판정은 서버 찾기(master_pick.search) · 시험 test_m01_search_words

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V1MKKZKpUHTPKb513FneU8
2026-09-24 21:05:58 +09:00

141 lines
6.3 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, "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