"""URL의 project_id가 남의 회사 것이면 막히는지 (2026-09-01 신설 가드). 지금까지 B03~B07은 로그인·회사 소속만 보고 프로젝트 주인은 보지 않았다. 특히 B07 도각 저장은 그 회사의 공용 양식을 덮어쓰므로 영향이 크다. DB는 읽기만 한다. """ import asyncio import sys from pathlib import Path import pytest from fastapi import HTTPException PROJECT_ROOT = Path(__file__).resolve().parents[2] sys.path.insert(0, str(PROJECT_ROOT)) from common_util.common_util_auth import require_project_access # noqa: E402 from config.config_db import close_db_pool, get_db_pool, init_db_pool # noqa: E402 class _Request: """path_params만 있으면 되는 최소 요청 대역.""" def __init__(self, **path_params): self.path_params = path_params async def _checks(): await init_db_pool() try: pool = get_db_pool() async with pool.acquire() as connection, connection.cursor() as cursor: await cursor.execute( "SELECT id, company_id FROM projects WHERE deleted_at IS NULL LIMIT 1" ) row = await cursor.fetchone() if not row: return None await _assert_guard(row[0], row[1]) return True finally: await close_db_pool() def test_project_access_guard(): if asyncio.run(_checks()) is None: pytest.skip("검사할 프로젝트가 DB에 없다") async def _assert_guard(project_id, company_id): owner = {"role": "USER", "company_id": company_id} other = {"role": "USER", "company_id": company_id + 1000} admin = {"role": "SYSTEM_ADMIN", "company_id": None} # 같은 회사면 통과한다. assert await require_project_access(_Request(project_id=project_id), owner) is owner # 다른 회사면 403. with pytest.raises(HTTPException) as blocked: await require_project_access(_Request(project_id=project_id), other) assert blocked.value.status_code == 403 # 없는 프로젝트는 404. with pytest.raises(HTTPException) as missing: await require_project_access( _Request(project_id="00000000-0000-0000-0000-000000000000"), owner ) assert missing.value.status_code == 404 # 시스템 관리자와 프로젝트 경로가 없는 요청은 그대로 지나간다. assert await require_project_access(_Request(project_id=project_id), admin) is admin assert await require_project_access(_Request(), other) is other