설정이 .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>
46 lines
1.6 KiB
Python
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 밖
|