Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016VBGFXB9AbJBwXP19z75Qq
109 lines
4.3 KiB
Python
109 lines
4.3 KiB
Python
"""라이브러리 — STmate 출력 엑셀에서 고정형 항목 뽑아 넣기 창구(PLAN 4장 · 2026-09-14 브레인 판정).
|
|
|
|
두 걸음: [읽기]는 호표 수·구성 줄 수·못 읽은 사유만 돌려주고 아무것도 안 씀 → [넣기]는 같은 파일을
|
|
서버가 **다시 읽어** 고른 호표를 개인 단에 씀(브라우저가 보낸 줄을 받아 적지 않음 · 개인 단만 · 판정 Ⓗ).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import io
|
|
import json
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
import openpyxl
|
|
import pytest
|
|
from fastapi import FastAPI
|
|
from fastapi.testclient import TestClient
|
|
|
|
ROOT = Path(__file__).resolve().parents[2]
|
|
if str(ROOT) not in sys.path:
|
|
sys.path.insert(0, str(ROOT))
|
|
|
|
import B08_Quantity.B08_Quantity_Engine_StructureLibrary as library_module # noqa: E402
|
|
import B08_Quantity.B08_Quantity_Router_StmateLibrary as router_module # noqa: E402
|
|
from common_util.common_util_auth import verify_session # noqa: E402
|
|
|
|
PROJECT_ID = "33333333-3333-3333-3333-333333333333"
|
|
BASE = f"/api/projects/{PROJECT_ID}/quantity/structure-sheets/library/stmate"
|
|
|
|
|
|
def _xlsx() -> bytes:
|
|
book = openpyxl.Workbook()
|
|
ws = book.active
|
|
ws.title = "일위대가표"
|
|
for row in [
|
|
["일 위 대 가 표"],
|
|
["공사명 : 시험 공사"],
|
|
["명 칭", "규 격", "수 량", "단위"],
|
|
[None],
|
|
[" 제 1 호표"],
|
|
["돌기슭막이(메쌓기)", "H=2.0", None, "m"],
|
|
["메쌓기", "L3=55cm이하", 2.09, "m2"],
|
|
["고임돌채집", "기계", 0.31, "m3"],
|
|
["합 계"],
|
|
[" 제 2 호표"],
|
|
["규준틀설치", "종단", None, "개소"],
|
|
["각재", "외송", "약간", "M3"],
|
|
["합 계"],
|
|
]:
|
|
ws.append(row)
|
|
buffer = io.BytesIO()
|
|
book.save(buffer)
|
|
return buffer.getvalue()
|
|
|
|
|
|
@pytest.fixture()
|
|
def client(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> TestClient:
|
|
monkeypatch.setattr(library_module, "STORAGE_BASE_DIR", str(tmp_path / "storage"))
|
|
app = FastAPI()
|
|
app.include_router(router_module.router)
|
|
app.dependency_overrides[verify_session] = lambda: {"company_id": 7, "user_id": 42}
|
|
return TestClient(app)
|
|
|
|
|
|
def _files() -> dict:
|
|
return {"file": ("견본.xlsx", _xlsx(), "application/octet-stream")}
|
|
|
|
|
|
def test_읽기는_수와_사유만_주고_안_쓴다(client: TestClient, tmp_path: Path) -> None:
|
|
body = client.post(f"{BASE}/read", files=_files()).json()
|
|
assert body["counts"] == {"hopyo": 1, "rows": 2}
|
|
assert body["hopyo"][0]["name"] == "돌기슭막이(메쌓기)" and body["hopyo"][0]["rows"] == 2
|
|
assert any("C12" in p for p in body["problems"]) # 수량이 수가 아닌 2호표는 사유로
|
|
assert not (tmp_path / "storage").exists()
|
|
|
|
|
|
def test_넣기는_서버가_다시_읽어_개인_단에_고정형으로(client: TestClient, tmp_path: Path) -> None:
|
|
response = client.post(
|
|
f"{BASE}/save", files=_files(), data={"hopyo_no": "1", "type_id": "masonry_dry"}
|
|
)
|
|
assert response.status_code == 200, response.text
|
|
code = response.json()["code"]
|
|
saved = json.loads(
|
|
(tmp_path / "storage" / "7" / "42" / "library" / f"{code}.json").read_text(encoding="utf-8")
|
|
)
|
|
assert saved["library_tier"] == "personal" and saved["origin"]["project"] == "시험 공사"
|
|
assert [r["amount"] for r in saved["rows"]] == ["2.09", "0.31"]
|
|
dirs = library_module.tier_dirs(7, 42)
|
|
assert [i["kind"] for i in library_module.list_items(dirs, "masonry_dry")] == ["fixed"]
|
|
|
|
|
|
def test_회사_없으면_403_없는_호표는_404_모르는_종류는_400(client: TestClient) -> None:
|
|
bad = client.post(
|
|
f"{BASE}/save", files=_files(), data={"hopyo_no": "2", "type_id": "masonry_dry"}
|
|
)
|
|
assert bad.status_code == 404 # 못 읽은 호표는 넣을 수 없음
|
|
unknown = client.post(f"{BASE}/save", files=_files(), data={"hopyo_no": "1", "type_id": "없음"})
|
|
assert unknown.status_code == 400
|
|
client.app.dependency_overrides[verify_session] = lambda: {"company_id": None, "user_id": 1}
|
|
denied = client.post(
|
|
f"{BASE}/save", files=_files(), data={"hopyo_no": "1", "type_id": "masonry_dry"}
|
|
)
|
|
assert denied.status_code == 403
|
|
|
|
|
|
def test_앱에_창구가_걸린다() -> None:
|
|
main = (ROOT / "main.py").read_text(encoding="utf-8")
|
|
assert "B08_Quantity_Router_StmateLibrary" in main and "b08_stmate_library_router" in main
|