# -*- coding: utf-8 -*- """349 — 안쪽 호출이 진짜 `get_section_detail` 길로 종횡단 상세를 받는지(요구 231 · 232 흠). 343-2 ETag 가 GET 처리기에 `request` 를 더해 안쪽 호출([저장] 뒤 서버 재계산 · 3D 길 모형 서버 짓기 · B09 인쇄 도면)이 TypeError 로 끊겼음. 이 시험은 `get_section_detail` 을 갈음하지 않고 그 아래 DB · 파일 읽기만 가짜로 둠 — 안쪽 함수 모양이 다시 바뀌면 빨강. """ from __future__ import annotations import asyncio import copy import sys from pathlib import Path from types import SimpleNamespace from uuid import uuid4 import pytest ROOT = Path(__file__).resolve().parents[2] if str(ROOT) not in sys.path: sys.path.insert(0, str(ROOT)) import B05_Profile.B05_Profile_Corridor_Prebuild as corridor # noqa: E402 import B06_Section.B06_Section_Router as b06_router # noqa: E402 import B06_Section.B06_Section_Router_Design as b06_design # noqa: E402 import B06_Section.B06_Section_Server_Calc_Prebuild as prebuild # noqa: E402 from B09_Estimation.draft.B09_Draft_Print_Drawings_Source import section_detail # noqa: E402 DETAIL = { "longitudinal": {"length_m": 40.0, "stations": [{"chainage_m": 0.0}]}, "cross_sections": [ {"chainage_m": 0.0, "design": {"ground_type": "soil"}}, {"chainage_m": 20.0, "design": {"ground_type": "rock"}}, ], } class _Stop(Exception): """상세를 받은 뒤 서버 재계산을 멈춤 — 그 뒤 길은 342-2 시험 몫.""" @pytest.fixture def real_detail(monkeypatch, tmp_path): """`get_section_detail` 아래 읽기만 가짜 — 처리 함수 자체는 진짜.""" async def run_with(fn, *args): if fn is b06_router.get_longitudinal_section: return {"longitudinal_file_path": "longitudinal.json", "data": None} if fn is b06_router.get_project_storage_relative_path: return "p" return [] # 횡단 설계 행 monkeypatch.setattr(b06_router, "run_with_connection", run_with) monkeypatch.setattr(b06_router, "resolve_stored_project_path", lambda path: str(tmp_path)) monkeypatch.setattr(b06_router, "_read_section_detail", lambda *a: copy.deepcopy(DETAIL)) monkeypatch.setattr(b06_router, "_attach_default_designs", lambda *a: None) return tmp_path def test_corridor_prebuild_reads_detail(real_detail) -> None: detail = asyncio.run(corridor._section_detail(uuid4(), 3)) assert detail is not None assert [row["chainage_m"] for row in detail["cross_sections"]] == [0.0, 20.0] def test_server_recompute_reads_detail(real_detail, monkeypatch) -> None: seen: list = [] async def run_with(fn, *args): if fn is prebuild.get_project_storage_relative_path: return "p" return {} async def haul_inputs(project_id): return {} def enforce(longitudinal, sections, *rest): seen.append(copy.deepcopy(sections)) raise _Stop monkeypatch.setattr(prebuild, "run_with_connection", run_with) monkeypatch.setattr(prebuild, "haul_inputs_for", haul_inputs) monkeypatch.setattr(prebuild, "resolve_stored_project_path", lambda path: str(real_detail)) monkeypatch.setattr(prebuild, "get_db_pool", lambda: None) monkeypatch.setattr(b06_design, "stored_standard_cross_section", lambda row: None) monkeypatch.setattr(b06_design, "enforce_design_lines", enforce) with pytest.raises(_Stop): asyncio.run(prebuild.recompute_server_side(uuid4(), 3)) assert [row["chainage_m"] for row in seen[0]] == [0.0, 20.0] def test_print_drawings_reads_detail(real_detail) -> None: detail = asyncio.run(section_detail(uuid4(), 3)) assert detail is not None and len(detail["cross_sections"]) == 2 def test_route_handler_keeps_etag(real_detail) -> None: def request(tag): return SimpleNamespace(headers={"if-none-match": tag} if tag else {}) handler = next( route.endpoint for route in b06_router.router.routes if getattr(route, "path", "").endswith("/sections/{route_id}/detail") and "GET" in route.methods ) first = asyncio.run(handler(uuid4(), 3, request(None))) etag = first.headers["ETag"] assert first.status_code == 200 and b"cross_sections" in first.body assert asyncio.run(handler(uuid4(), 3, request(etag))).status_code == 304