# TRACK_B_FOLLOWUP — 신규 실패 5건 해소 (테스트 격리)

> **범위: 테스트 격리만.** 기능 변경·PR·merge 전부 금지.

## base / branch
- **기존 브랜치 `task/task-2801-dev6` 에 이어서 커밋** (7커밋 위에 추가)
- worktree: `/home/jay/projects/InsuRo/.worktrees/task-2801-dev6`

## expected_files (정확히 1파일)
```yaml
allowed_resources:
  paths: ["server/tests/conftest.py"]
  new_file_limit: 0
  commands: ["git","python3","pytest"]
  merge_policy: "none"
```

## 문제 (ANU 실측 + Codex 확인)
`task/task-2801-dev6` 은 테스트 66개를 추가했으나 **신규 실패 5건**을 만들었다. clean main 17 failed → B 22 failed.
```
test_main.py::TestCostCircuitBreaker::{blocks_when_exceeded, passes_when_under_limit, passes_on_db_failure}
test_gdrive.py::TestGetDriveService::test_get_drive_service_missing_env
test_gdrive_sync.py::TestSyncDriveFolderReturnValue::test_sync_uses_env_folder_id_when_none
```
**5건 전부 순서의존** — 단독 실행 시 전부 PASS. 기능 결함이 아니라 **테스트 격리 실패**다.

### 원인 A — env 누수 (gdrive 2건)
- `server/tests/test_consultation_history_v1.py` 의 `ch_env` fixture(170-186행) 와 `test_feature_disabled_returns_403`(717-725행) 이 **`import main as main_mod`** 를 수행
- `server/main.py:53-54` 가 import 시 **`load_dotenv()`** 실행 → `.env` 의 `INSURO_GOOGLE_CLIENT_ID/SECRET/REFRESH_TOKEN`·`INSURO_GOOGLE_DRIVE_FOLDER_ID` 가 **프로세스 환경에 주입**되고 monkeypatch 관리 밖이라 이후 테스트까지 남는다
- `server/gdrive.py:33-40` 은 **`INSURO_GOOGLE_*` 를 `GOOGLE_*` 보다 우선** 사용. 그런데 `test_gdrive.py:52-61` 은 **`GOOGLE_*` 만 삭제** → "env 없으면 raise" 가 raise 하지 않음
- 동일 패턴: `server/gdrive_sync.py:188-190` (`INSURO_GOOGLE_DRIVE_FOLDER_ID` 우선) vs `test_gdrive_sync.py:650-672` (`GOOGLE_DRIVE_FOLDER_ID` 만 설정)
- **ANU 실증**: `pytest test_gdrive.py::...missing_env test_consultation_history_v1.py` → **39 passed** / **역순** → **1 failed** (`DID NOT RAISE`)

### 원인 B — rate limiter 전역 버킷 누수 (CostCircuitBreaker 3건)
- `server/main.py:289` `limiter = Limiter(...)` 모듈 전역 + 해당 엔드포인트 `@limiter.limit("5/minute")`
- 신규 테스트 66개가 실행 순서를 바꾸면서 기존 누수가 표면화

## 수정 요구 (`server/tests/conftest.py` 에만)

### 1. limiter reset — **이미 검증된 구현을 가져온다**
브랜치 `task/ci-2-260719` 커밋 **`e404e44f42527b3c16c0df1d5805ffd393eb51b1`** 의 `conftest.py` 변경(175-228행, 252행 이하)을 **그대로 반영**:
- `_reset_rate_limiter_storage()` + 함수스코프 autouse `_reset_rate_limiter_bucket_per_test()`
- **fail-closed**: import 실패·reset 실패 시 `RuntimeError`(조용히 넘어가지 말 것)
- **negative control fixture 유지**: 5/minute 초과 시 429 가 여전히 발생함을 세션 1회 검증
> ANU 가 이미 검증한 구현이다(인위적 reset 실패 주입 → exit 1 실증). **재설계하지 말고 반영만 하라.**

### 2. Google env 격리 — 신규 추가
- **함수스코프 `autouse=True` fixture** 로 각 테스트 시작 시 `monkeypatch.delenv(..., raising=False)`:
  `INSURO_GOOGLE_CLIENT_ID` · `INSURO_GOOGLE_CLIENT_SECRET` · `INSURO_GOOGLE_REFRESH_TOKEN` · `INSURO_GOOGLE_DRIVE_FOLDER_ID`
- **`monkeypatch` 를 쓸 것** — `patch.dict(os.environ)` 아님. 저장소 기존 패턴(`env_vars(monkeypatch)`, 18-32행)과 일치시킨다.
- ⚠️ 기존 `env_vars` fixture 의 동작을 깨뜨리지 말 것(그 fixture 는 값을 **설정**한다. 격리 fixture 와 순서 충돌 없는지 확인).

## 완료 판정 (전부 ANU 재실행 대상)
1. `python3 -m pytest server/tests -q -p no:randomly` → **17 failed 이하** (= clean main 기준선). **신규 실패 0**
2. 위 5건이 **전체 스위트에서 PASS**
3. **negative control 동작**: 429 관측 assert 유지
4. **fail-closed 유지**: reset 실패 시 세션이 명시적으로 실패
5. 테스트 **개수 감소 0** · skip/xfail 증가 **0**
6. B 의 신규 테스트 66개 **전부 유지·통과**

## ★ 절대 제약
- **PR 생성 0 · main merge 0.** 브랜치 커밋 **정확히 1개**.
- **금지**: skip · xfail · assertion 완화 · `server/main.py`/`gdrive.py`/`gdrive_sync.py` 등 **소스 수정** · 기존 테스트 삭제 · retry 증설
- `server/tests/conftest.py` **외 파일 수정 0**. 위반 시 STOP_REPORT.

## 봇 안전 프로토콜
- Edit·commit·finish 직전마다 **cancel 마커 재확인**
- **`.done` 자동마커는 완료 증거가 아니다** — ANU 독립검증이 권위. 산출물(커밋·보고서) 반드시 남길 것
- 실패·중단 시에도 보고서 + ANU callback

## 보고
- `/home/jay/workspace/memory/reports/trackB-followup-isolation.md` (저장소 밖 · git stage 0)
- diff / 원인 A·B 각 조치 근거 / **전체 pytest 실행 원문(전후 카운트)** / 5건 개별 PASS 증거 / MATCH·GAP
- 봇 종료 전 ANU callback(envelope만·UTF-8 ≤3900B, collector=ANU key c119085addb0f8b7)

## goal_assertions (auto-generated)
- `pytest test_gdrive.py::...missing_env test_consultation_history_v1.py`
- `python3 -m pytest server/tests -q -p no:randomly`
