Files
Aislo/resources/tester/test_b03_direct_upload_guards.py
T
eomsangdonandClaude Opus 5 0ef32b5279 chore(tester): 시험을 resources/tester/ 로 옮김 — 창끼리 건너가게
⚠ **뿌리** — `tmp/` 는 창 사이에 안 건너감(실측 확인: 상대 창이 놓은 `tmp/_sync_probe.txt`
가 시간을 두고 두 번 봐도 안 보임). 그래서 **정본(등록부 스키마)만 건너가고 그것을 읽는
시험은 안 건너가** 오늘 두 번, 같은 시험이 **연 창은 통과·받은 창은 실패**가 됐음.

- `tmp/tests/*` 를 `resources/tester/` 로 **복사**(127 파일). 내용은 **한 줄도 안 고침**
- `tmp/tests` 는 **남겨 둠** — 되돌릴 자리(사용자 지시)
- 실행: `./venv/Scripts/python.exe -m pytest resources/tester/ -q`
  옮기기 전과 **같은 수**: 617 통과 / 22 건너뜀 / 실패 0

⇒ 이제 시험·예외·까닭이 **정본과 함께** 움직임. 오늘 세운 「예외는 정본 스키마에」와 짝임.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-09 17:12:30 +09:00

121 lines
3.9 KiB
Python

"""직행 업로드(`POST /api/projects/{id}/files`)가 청크 경로와 같은 보호막을 갖는지.
왜 있나(2026-09-08) — 창 넷이 DB·저장소를 함께 쓰는데 이 갈래만
① 분석 중 차단(`is_analysis_running`) ② 같은 이름 옛 행 내리기(`supersede_previous_input_files`)
가 빠져 있었다. 빠지면 분석 2개가 같은 산출물 경로에서 부딪히고, 같은 파일이 두 줄로
활성으로 남아 어느 것으로 도는지 순서에 달린다.
파일 내용(소스 문자열)에 기대지 않는다 — **함수 객체를 실제로 호출**해 확인한다.
"""
from __future__ import annotations
import asyncio
from types import SimpleNamespace
from typing import Any
import pytest
import B03_FileInput.B03_FileInput_Router as router_module
class _Cursor:
async def __aenter__(self) -> "_Cursor":
return self
async def __aexit__(self, *_: Any) -> None:
return None
async def execute(self, *_: Any, **__: Any) -> None:
return None
async def fetchone(self) -> None:
return None
class _Connection:
def cursor(self, *_: Any, **__: Any) -> _Cursor:
return _Cursor()
class _Acquire:
async def __aenter__(self) -> _Connection:
return _Connection()
async def __aexit__(self, *_: Any) -> None:
return None
class _Pool:
def acquire(self) -> _Acquire:
return _Acquire()
class _Upload(SimpleNamespace):
"""UploadFile 흉내 — 라우터가 마지막에 `close()` 를 부른다."""
async def close(self) -> None:
return None
def _upload(filename: str) -> _Upload:
return _Upload(filename=filename, size=10)
_FULL_SET = ["a.las", "b.tif", "b.tfw", "b.prj", "route.csv"]
def test_분석_중이면_직행_업로드를_409로_막는다(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(router_module, "get_db_pool", lambda: _Pool())
monkeypatch.setattr(
router_module, "get_project_storage_relative_path", _async_return("1/3/proj")
)
monkeypatch.setattr(router_module, "resolve_stored_project_path", lambda _p: ".")
monkeypatch.setattr(router_module, "is_analysis_running", _async_return(True))
response = asyncio.run(
router_module.upload_project_files(
project_id=_PROJECT_ID,
files=[_upload(name) for name in _FULL_SET],
las_free=False,
session={"role": "USER"},
)
)
assert response.status_code == 409
def test_직행_업로드가_같은_이름_옛_행을_내린다() -> None:
"""`supersede_previous_input_files` 가 이 모듈에 실제로 이어져 있는지."""
from B03_FileInput.B03_FileInput_Repository import (
supersede_previous_input_files as repository_function,
)
assert router_module.supersede_previous_input_files is repository_function
def test_세_갈래가_같은_보호막을_쓴다() -> None:
"""청크·임시배치·직행이 같은 두 함수를 쓰는지 — 한 갈래만 빠지는 사고를 막는다."""
import B03_FileInput.B03_FileInput_Router_Chunks as chunks
import B03_FileInput.B03_FileInput_Router_Temp as temp
for module in (router_module, chunks, temp):
assert hasattr(module, "is_analysis_running"), module.__name__
assert hasattr(module, "supersede_previous_input_files"), module.__name__
# 확인 단계도 세 갈래가 다 들고 있어야 한다 — 하나만 빠지면 그 갈래가 500 으로 죽는다
# (2026-09-08 실제로 청크 갈래에서 `NameError` 로 겪었다).
for module in (router_module, chunks, temp):
assert hasattr(module, "OutputsWouldBeDiscarded"), module.__name__
assert hasattr(module, "_confirm_replace_response"), module.__name__
from uuid import UUID # noqa: E402 — 아래 상수에서만 쓴다
_PROJECT_ID = UUID("fa76c162-71c7-46e5-a95d-fb3930665a45")
def _async_return(value: Any):
async def _inner(*_: Any, **__: Any) -> Any:
return value
return _inner