o2o-negosium-original/lps/tests/test_api_guard.py
민헌 c81df5bd88 refactor(lps): 설정을 TOML 단일 소스로 통합 — env/.env 이중 관리 제거
설정이 .env(compose 주입)·config.toml·코드 곳곳의 os.environ 직독 3계층에
흩어져 관리가 어려웠다. TOML 하나로 통합한다(협의 결정).

- 신설 [WorkerConfig](동시성·폴백·프로필·데드라인·유예·Chrome·하트비트),
  [AlertConfig](웹훅·쿨다운·임계 10종). [WebServerConfig].api_keys(guard),
  [DecodoConfig].ip_request_budget/port_cooldown_sec 추가 — 흩어져 있던
  LPS_* env 20여 개를 섹션으로 흡수.
- server_configs 의 env override 계층(DB_*·시크릿·NAVER_KEYS 등) 삭제.
  남는 env 는 APP_ENV(부트스트랩)·PROCESS_COUNT/WORKER_CONCURRENCY(실행
  스크립트 대화형 입력 전용)·LPS_LIVE(테스트 옵트인)뿐.
- Docker: env 주입 → config.docker.toml 마운트 + APP_ENV=docker.
  이미지 무시크릿 유지, 마운트 누락 시 FileNotFoundError 즉시 실패.
  .env.example 삭제, config.docker.toml.example 신설.
- negodata 호출부: guard 키를 env 직독에서 [WebServerConfig].lps_api_key
  (+기존 관례대로 env override)로 이동.
- 실행 스크립트: 프로필·폴백·예산 프롬프트 제거(toml 소스 안내),
  동시성/프로세스 수만 임시 override 로 유지.
- docs 7종·example toml 의 env 표기를 toml 키로 일괄 갱신.
- 전체 145 passed + APP_ENV=docker 로딩·API 기동 스모크 확인.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 21:11:31 +09:00

46 lines
1.6 KiB
Python

"""API guard 테스트 — [WebServerConfig].api_keys 설정 시에만 /v1 에 X-API-Key 검증(개발=빈값=개방 모드).
auth.configured_keys 가 매 요청 config 를 읽으므로 monkeypatch.setattr 만으로 on/off 를 전환한다
(앱 재기동 불필요).
"""
import pytest
from config.server_configs import web_server_config
@pytest.fixture
def guarded(monkeypatch):
monkeypatch.setattr(web_server_config, "api_keys", ["k1", "k2"])
async def test_open_mode_without_keys(client, monkeypatch):
monkeypatch.setattr(web_server_config, "api_keys", [])
r = await client.get("/v1/lps/queue/stats") # 개방 모드 — 헤더 없이 통과
assert r.status_code == 200
async def test_guarded_rejects_missing_header(client, guarded):
r = await client.get("/v1/lps/queue/stats")
assert r.status_code == 401
async def test_guarded_rejects_wrong_key(client, guarded):
r = await client.get("/v1/lps/queue/stats", headers={"X-API-Key": "nope"})
assert r.status_code == 401
async def test_guarded_accepts_any_configured_key(client, guarded):
for key in ("k1", "k2"): # 복수 키 — 무중단 키 교체용
r = await client.get("/v1/lps/queue/stats", headers={"X-API-Key": key})
assert r.status_code == 200
async def test_guard_covers_post_search(client, guarded):
r = await client.post("/v1/lps/search", json={"data": []})
assert r.status_code == 401 # enqueue(비용 발생 경로)도 보호
async def test_healthz_stays_open(client, guarded):
assert (await client.get("/healthz")).status_code == 200 # LB 프로브는 guard 밖