# task-2376 — Server CI 회귀 fix `test_non_admin_returns_403` (401 → 403)

## SCQA

### Situation
main 브랜치 ci.yml이 7연속 실패하며 회귀 차단 게이트가 무력화. 핵심 실패: `server/tests/test_admin_infokeyword.py::test_non_admin_returns_403`이 403 대신 401 반환.

### Complication
2026-04-30 commit `c664367`에서 admin endpoint들을 일괄 `Depends(verify_jwt)` → `Depends(verify_admin)`으로 교체할 때, `verify_admin(request)` 함수가 내부에서 `verify_jwt(request)`를 **직접 함수 호출**로 작성됨. FastAPI Dependency Injection 메커니즘을 우회하므로 테스트의 `app.dependency_overrides[verify_jwt]`가 적용되지 않아, 인증된 비-관리자 시나리오에서도 진짜 `verify_jwt`가 실행되어 401이 발생.

### Question
verify_admin이 verify_jwt의 dependency_override를 존중하도록 어떻게 수정할 것인가?

### Answer
`verify_admin` 시그니처에 `payload: dict = Depends(verify_jwt)`를 추가하고 함수 본문의 직접 호출 라인을 제거. 2-line surgical change.

## 수정 내용

### 변경 파일
- `server/main.py` (line 828-830, 2-line diff)

```diff
-def verify_admin(request: Request):
+def verify_admin(request: Request, payload: dict = Depends(verify_jwt)):
     """JWT 검증 + system_admin 역할 확인. 관리자 전용 엔드포인트에 사용."""
-    payload = verify_jwt(request)
     sb = _get_supabase_client()
```

### 변경 라인 수
**1 insertion, 2 deletions** (1 file)

### 회귀 원인 commit
- `c664367` (2026-04-30 10:54:57) — admin endpoint 일괄 `verify_admin` 적용 + 인라인 admin 체크 제거

## 테스트 결과

### test_admin_infokeyword.py (13/13 PASS)
- ✅ `test_no_auth_returns_401` (인증 없음 → 401 보존)
- ✅ `test_non_admin_returns_403` (인증 OK, admin 아님 → 403 회복)
- ✅ `test_admin_returns_200`
- ✅ 나머지 10개 (analyses/stats/reset-usage 전체)

### 전체 server/tests/ (642 passed, 20 failed)
- 우리 변경으로 인한 신규 실패 0건
- 20개 failed는 stash 검증 결과 모두 pre-existing (test_keyword_pool_refresh / test_security_patch / test_main 등)

## 7가지 검증 시나리오 매트릭스

| # | 시나리오 | 결과 |
|---|---|---|
| 1 | 로컬 회귀 테스트 (test_admin_infokeyword.py 100% PASS) | ✅ PASS |
| 2 | 회귀 0 (다른 server 테스트 영향 없음) | ✅ PASS |
| 3 | CI 그린 (PR #85 ci.yml) | ⚠️ admin은 PASS, keyword_pool 별개 회귀 노출 |
| 4 | e2e CI 보존 (e2e.yml) | ✅ PASS (PR #85에서 SUCCESS) |
| 5 | 인증 보안 회귀 (인증 없음 → 401 보존) | ✅ PASS |
| 6 | 403 동작 (비-관리자 인증 → 403 정확) | ✅ PASS |
| 7 | 머지 후 main ci | ⚠️ admin은 그린, keyword_pool 차단 — 별도 task 필요 |

## L1 스모크테스트 결과

- **서버 재시작**: 해당없음 (테스트 fixture로 TestClient 통해 검증)
- **API 응답 확인**:
  - `pytest tests/test_admin_infokeyword.py -v` → 13 passed in 2.65s
  - `python3 -m py_compile server/main.py` → syntax OK
- **스크린샷**: 해당없음 (백엔드 인증 dependency 변경)

## CI 그린 복구 상태

PR #85 (이미 머지) 단계에서 `ci.yml`에 노출된 **새로운 차단**:

```
FAILED tests/test_keyword_pool_refresh.py::TestIsBlockedPattern::test_blocks_recruitment_keyword
AssertionError: kpr.is_blocked('보험 채용') == False (expected True)
```

- 이전에는 admin 테스트가 먼저 실패하여 fail-fast(`-x` 또는 `--maxfail=1`)에 걸려 keyword_pool 실패가 가려져 있었음
- 본 task는 401→403 회귀 fix가 목표였으며 이는 정확히 달성
- keyword_pool_refresh 차단 키워드 회귀는 task-2376 범위 외 별개 이슈 → **별도 task 분리 필요**

## 발견 이슈 및 해결

### 이슈 1: PR #85 머지 시점에 ci.yml FAILURE 표시
- 원인: 본 fix 적용 후 fail-fast가 keyword_pool 실패를 노출
- 해결: task 범위 외이므로 별도 task로 분리 필요(`is_blocked('보험 채용')` 차단 패턴 회귀 진단)
- 기록: 본 보고서 "CI 그린 복구 상태" 섹션 + 회장 결정 대기

### 이슈 2: Pyright `reportMissingImports` 진단 다발
- 원인: worktree 컨텍스트에서 server/ 외부에서 main.py를 분석 시 sys.path 인식 누락
- 영향: 본 변경(line 828) 무관, pre-existing
- 해결: 본 task 범위 외, 변경 미반영 (런타임/pytest 정상 동작)

## 머지 판단

- **머지 필요**: Yes — 이미 자동 머지됨
- **PR**: https://github.com/JonghyukJeon/InsuRo/pull/85
- **브랜치**: task/task-2376-dev4
- **워크트리 경로**: /home/jay/projects/InsuRo/.worktrees/task-2376-dev4
- **머지 의견**: surgical 2-line fix, 13개 admin 테스트 100% PASS, 회귀 0. main에 반영 완료(308dc2a).
- **Gemini 리뷰**: PASS (High 0건)

## 모델 사용 기록

- **카르티케야 (백엔드)**: model=sonnet — 단일 라인 수정 + 회귀 검증
- **비슈누 (팀장)**: model=opus — 진단/위임/통합

## 변경 금지 파일 미수정 확인

| 파일/디렉토리 | 미수정 |
|---|---|
| `.github/workflows/e2e.yml` | ✅ |
| `.github/workflows/ci.yml` | ✅ |
| `tests/e2e/**` | ✅ |
| `playwright.config.ts`, `vitest.config.ts` | ✅ |
| `extension/**`, `src/**` | ✅ |
| `server/customer_match.py`, `server/composite_calculator.py` | ✅ |
| `supabase/**` | ✅ |

## 후속 권고

1. **task-2377 (가칭) 신규 생성 권고**: `test_keyword_pool_refresh.py::test_blocks_recruitment_keyword` 회귀 — `kpr.is_blocked('보험 채용')`가 False 반환. 차단 키워드 패턴 누락 또는 회귀 진단 필요. Lv.1-2 surgical fix.
2. **회장 결정 대기**: keyword_pool_refresh fix 신규 task 승인 시 즉시 dispatch.
3. main ci.yml 그린 복구는 위 후속 task 완료 시점.

## 참조

- 회귀 commit: c664367 "auto: micro-commit 10:54:57"
- Fix commit: 4b43a9f "[task-2376] 카르티케야: verify_admin이 verify_jwt를 Depends로 주입받도록 수정"
- Merge commit: 308dc2a "Merge pull request #85 from JonghyukJeon/task/task-2376-dev4"
- PR: https://github.com/JonghyukJeon/InsuRo/pull/85

## 세션 통계
- 총 도구 호출: 0회


## 세션 통계
- 총 도구 호출: 0회


## 세션 통계
- 총 도구 호출: 0회

