- 장 나눔·제원 입력 엔진과 기울기 판정 대상을 B08 로 옮김 - 창구를 /quantity/structure-sheets 로 옮기고 기초잡석 두께·지반 갈래를 함께 넘김 (옛 B07 창구는 두께를 안 넘겨 원단위 탭과 값이 갈렸음) - 구조물도 탭 조각 renderStructureSheets 신설 — 탭 등록은 브레인 몫이라 안 붙임 - B07 표준도 목록은 탭 배선 날까지 B08 엔진·창구를 불러 그대로 둠 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016VBGFXB9AbJBwXP19z75Qq
109 lines
3.9 KiB
Python
109 lines
3.9 KiB
Python
"""구조물도 창구(B08) — B07 에서 옮긴 뒤 조회·제원 저장이 한 벌로 도는지 (2026-09-13, PLAN 3장 ②).
|
|
|
|
겨누는 것 셋
|
|
① 조회가 장을 냄 — 옛 B07 `/standard-sheets` 자리
|
|
② ⚠ 기초잡석 두께가 **산출 조건 값을 따름** — 옛 B07 창구는 두께를 안 넘겨 늘 0.2 로 섰음
|
|
(원단위 탭과 구조물도가 같은 구조물에서 다른 값을 냈음)
|
|
③ 제원 저장이 그 장의 개소에 걸리고, 다시 조회하면 새 제원으로 섬
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
from fastapi import FastAPI
|
|
from fastapi.testclient import TestClient
|
|
|
|
ROOT = Path(__file__).resolve().parents[2]
|
|
sys.path.insert(0, str(ROOT))
|
|
|
|
import B08_Quantity.B08_Quantity_Router_Material as material_module # noqa: E402
|
|
import B08_Quantity.B08_Quantity_Router_StructureSheet as router_module # noqa: E402
|
|
from B05_Profile.B05_Profile_Structures_Repository import save_structures # noqa: E402
|
|
from B05_Profile.B05_Profile_Structures_Schema import StructureInstance # noqa: E402
|
|
|
|
PROJECT_ID = "22222222-2222-2222-2222-222222222222"
|
|
SHEETS = f"/api/projects/{PROJECT_ID}/quantity/structure-sheets"
|
|
|
|
|
|
@pytest.fixture()
|
|
def project(tmp_path: Path) -> Path:
|
|
root = tmp_path / "project"
|
|
root.mkdir()
|
|
wall = StructureInstance.model_validate(
|
|
{
|
|
"type_id": "masonry_wet",
|
|
"placement": "interval",
|
|
"start_m": 100.0,
|
|
"end_m": 110.0,
|
|
"options": {"height_m": 2.5, "back_len_cm": 45},
|
|
}
|
|
)
|
|
save_structures(str(root), [wall], base_revision=0)
|
|
return root
|
|
|
|
|
|
@pytest.fixture()
|
|
def client(project: Path, monkeypatch: pytest.MonkeyPatch) -> TestClient:
|
|
async def fake_root(project_id):
|
|
return str(project)
|
|
|
|
async def no_route(project_id):
|
|
return {}
|
|
|
|
monkeypatch.setattr(router_module, "_project_root", fake_root)
|
|
monkeypatch.setattr(material_module, "_section_modes", no_route)
|
|
monkeypatch.setattr(material_module, "_ground_types", no_route)
|
|
app = FastAPI()
|
|
app.include_router(router_module.router)
|
|
return TestClient(app)
|
|
|
|
|
|
def _sheet(client: TestClient) -> dict:
|
|
response = client.get(SHEETS)
|
|
assert response.status_code == 200, response.text
|
|
sheets = response.json()["sheets"]
|
|
assert len(sheets) == 1, sheets
|
|
return sheets[0]
|
|
|
|
|
|
def _unit_amount(sheet: dict, name: str) -> float:
|
|
return next(row["unit_amount"] for row in sheet["rows"] if row["name"] == name)
|
|
|
|
|
|
def test_조회가_장을_낸다(client: TestClient) -> None:
|
|
sheet = _sheet(client)
|
|
assert sheet["type_id"] == "masonry_wet"
|
|
assert sheet["member_count"] == 1
|
|
assert sheet["billing_unit"] == "m" and sheet["billing_total"] == pytest.approx(10.0)
|
|
assert all("spec" in row for row in sheet["rows"])
|
|
|
|
|
|
def test_기초잡석_두께가_산출_조건을_따른다(client: TestClient, project: Path) -> None:
|
|
기본 = _unit_amount(_sheet(client), "기초잡석")
|
|
(project / "project_settings.json").write_text(
|
|
json.dumps({"quantity": {"rubble_base_thickness_m": 0.3}}), encoding="utf-8"
|
|
)
|
|
바꿈 = _unit_amount(_sheet(client), "기초잡석")
|
|
# 기본 0.2 → 0.3 — 버림 폭에 두께 비를 곱하는 식이라 1.5 배.
|
|
assert 바꿈 == pytest.approx(기본 * 1.5)
|
|
|
|
|
|
def test_제원_저장이_그_장에_걸린다(client: TestClient) -> None:
|
|
sheet = _sheet(client)
|
|
saved = client.put(
|
|
f"{SHEETS}/spec",
|
|
json={"sheet_key": sheet["key"], "base_revision": 1, "back_len_cm": "55"},
|
|
)
|
|
assert saved.status_code == 200, saved.text
|
|
assert saved.json()["changed"] == 1
|
|
assert _sheet(client)["options"]["back_len_cm"] == 55
|
|
|
|
|
|
def test_없는_장이면_404(client: TestClient) -> None:
|
|
response = client.put(f"{SHEETS}/spec", json={"sheet_key": "없는 장", "base_revision": 1})
|
|
assert response.status_code == 404
|