# Phase 3 Testing Checklist

> Phase 3 implementation status: **COMPLETE** (2026-03-12)

## 1. 단위 테스트

### text normalizer
- [x] HTML 문서가 안정적으로 plain text로 변환된다. → `test_text_normalizer.py`
- [x] 동일 문서에 대해 해시가 안정적으로 재생산된다. → `test_text_normalizer.py`
- [x] disclaimer strip 옵션이 본문을 과도하게 삭제하지 않는다. → `test_text_normalizer.py`

### rule parser
- [x] guidance 키워드가 올바르게 분류된다. → `test_rule_parser.py`
- [x] one-off 키워드가 검출된다. → `test_rule_parser.py`
- [x] demand/pricing/margin 키워드가 검출된다. → `test_rule_parser.py`
- [x] section parser가 없는 문서에서도 안전하게 실패한다. → `test_rule_parser.py`

### span mapper
- [ ] evidence char offsets가 원문 구간과 일치한다.
- [ ] normalization 후에도 span reference가 추적 가능하다.

### llm wrapper (Ollama)
- [x] 캐시 히트 시 외부 호출이 발생하지 않는다. → `test_llm_client.py::TestOllamaClientChat::test_cache_hit_skips_llm_call`
- [x] 타임아웃 시 LLMTimeoutError 발생 → `test_llm_client.py::TestOllamaClientChat::test_chat_timeout_raises_llm_timeout_error`
- [x] 5xx 응답 시 RetryableError 발생 → `test_llm_client.py::TestOllamaClientChat::test_chat_5xx_raises_retryable_error`
- [x] 성공 응답 파싱 → `test_llm_client.py::TestOllamaClientChat::test_chat_success_returns_parsed_json`

### LLM cache
- [x] 캐시 미스 시 None 반환 → `test_llm_cache.py::TestLLMCacheStore::test_get_returns_none_on_miss`
- [x] 캐시 put이 DB 행 생성 → `test_llm_cache.py::TestLLMCacheStore::test_put_stores_entry`

### canonical merge
- [x] rule 우선 (both confident) → `test_merger.py::TestMerge::test_rule_wins_when_both_confident`
- [x] llm이 unknown signal 채움 → `test_merger.py::TestMerge::test_llm_fills_unknown_signals`
- [x] 충돌 시 보수적 merge + warning → `test_merger.py::TestMerge::test_conflict_flags_rule_llm_conflict`
- [x] llm=None → all provenance=rule → `test_merger.py::TestMerge::test_llm_none_all_provenance_is_rule`
- [x] risk_flags OR 결합 → `test_merger.py::TestMerge::test_risk_flags_are_ored`
- [x] review queue trigger (low confidence) → `test_merger.py::TestShouldQueueForReview::test_low_confidence_triggers_review`
- [x] review queue trigger (conflict) → `test_merger.py::TestShouldQueueForReview::test_conflict_triggers_review`
- [x] review queue trigger (oneoff) → `test_merger.py::TestShouldQueueForReview::test_oneoff_triggers_review`

### feature builder
- [x] reaction_close_location 계산이 정확하다. → `test_event_features.py`
- [x] rolling window가 미래 데이터를 보지 않는다. → `test_market_features.py`
- [x] null feature가 정책대로 처리된다. → `test_event_features.py`

### labeler
- [x] pre_market/regular_hours → same-day reaction → `test_labeler.py::TestComputeReactionDate::test_pre_market_on_trading_day_returns_same_day`
- [x] post_market → next trading day → `test_labeler.py::TestComputeReactionDate::test_post_market_returns_next_trading_day`
- [x] unknown → next trading day → `test_labeler.py::TestComputeReactionDate::test_unknown_returns_next_trading_day`
- [x] 주말(비거래일) pre_market → next trading day → `test_labeler.py::TestComputeReactionDate::test_pre_market_on_weekend_returns_next_trading_day`
- [x] 금요일 post_market → 월요일 → `test_labeler.py::TestComputeReactionDate::test_post_market_on_friday_returns_monday`
- [x] 1D forward return 정확 → `test_labeler.py::TestComputeLabelsFromBars::test_1d_return_calculation`
- [x] MFE = max(high-entry)/entry → `test_labeler.py::TestComputeLabelsFromBars::test_mfe_is_max_high_minus_entry`
- [x] MAE = min(low-entry)/entry → `test_labeler.py::TestComputeLabelsFromBars::test_mae_is_min_low_minus_entry`
- [x] hit_pos_1r True/False → `test_labeler.py::TestComputeLabelsFromBars::test_hit_pos_1r_true/false`
- [x] close_up_after_3d logic → `test_labeler.py::TestComputeLabelsFromBars::test_close_up_after_3d_true_when_final_close_above_entry`
- [x] 빈 bars → empty dict → `test_labeler.py::TestComputeLabelsFromBars::test_empty_bars_returns_empty_dict`
- [x] label=ok when price data available → `test_labeler.py::TestGenerateLabels::test_generate_labels_with_valid_prices`
- [x] label=unavailable when Oracle fails → `test_labeler.py::TestGenerateLabels::test_generate_labels_unavailable_when_no_price_data`

### review queue
- [x] 신규 ReviewItem 생성 → `test_review_queue.py::TestCreateReviewItem::test_create_new_review_item`
- [x] 중복 open item → 업데이트 (priority escalation) → `test_review_queue.py::TestCreateReviewItem::test_deduplicate_open_items`
- [x] resolve → status=resolved → `test_review_queue.py::TestCreateReviewItem::test_resolve_review_item`
- [x] list_review_items status 필터 → `test_review_queue.py::TestCreateReviewItem::test_list_review_items_with_status_filter`

### snapshot export
- [x] manifest.json 생성 (snapshot_id, created_at, row_counts) → `test_snapshot_export.py::TestExportDatasetSnapshot::test_manifest_is_written`
- [x] train/valid/test Parquet 파일 생성 → `test_snapshot_export.py::TestExportDatasetSnapshot::test_parquet_files_created`
- [x] temporal split proportions → `test_snapshot_export.py::TestTemporalSplit::test_split_proportions`
- [x] temporal split order preserved → `test_snapshot_export.py::TestTemporalSplit::test_split_preserves_temporal_order`

## 2. 통합 테스트

- [x] SEC raw 문서 하나가 parser output까지 도달한다. → `test_filing_pipeline.py`
- [x] parser output이 feature builder로 연결된다. → `test_feature_pipeline.py`
- [x] feature + price data가 labeler로 연결된다. → `test_label_pipeline.py::test_label_pipeline_end_to_end`
- [x] Oracle 실패 시 unavailable label 생성 → `test_label_pipeline.py::test_label_pipeline_handles_missing_price_data`
- [x] review queue item이 실제로 생성된다. → `test_review_queue_integration.py::test_low_confidence_merge_creates_review_item`
- [x] conflict 시 P0 review item 생성 → `test_review_queue_integration.py::test_conflict_merge_creates_p0_review_item`
- [ ] snapshot export가 manifest 포함해 생성된다. (needs real DB)

## 3. Replay 테스트

- [x] 동일 문서 + 동일 prompt_version → LLM 캐시 히트 → `test_llm_cache_replay.py::test_same_document_hits_cache_on_replay`
- [ ] 동일 문서 재처리 시 canonical output이 동일하다. → `test_determinism.py`
- [ ] parser_version 변경 시 diff report 생성 가능하다.
- [ ] historical day replay가 live path와 같은 코드 경로를 탄다.

## 4. Gold set 테스트

- [ ] event_type accuracy baseline 이상 → `apps/qa/evaluate_gold_set.py`
- [ ] guidance_direction accuracy baseline 이상 → `apps/qa/evaluate_gold_set.py`
- [ ] oneoff precision/recall baseline 이상 → `apps/qa/evaluate_gold_set.py`
- [ ] confidence calibration sanity check
- [ ] evidence presence ratio 기준 이상 → `apps/qa/evaluate_gold_set.py`

## 5. Leakage 테스트

- [ ] next_open entry dataset에 entry day 장중 정보가 포함되지 않는다.
- [ ] FINRA post-close data가 당일 아침 feature로 들어가지 않는다.
- [ ] forward returns를 만드는 price bars가 feature 계산에 재사용되지 않는다.
- [x] snapshot split이 시간 순서를 위반하지 않는다. → `test_snapshot_export.py::TestTemporalSplit::test_split_preserves_temporal_order`

## 6. 운영 전 체크리스트

- [x] parser schema version 고정 → `libs/parser/rule_parser.py::PARSER_VERSION`
- [x] prompt version 고정 → `libs/llm/prompts.py::PROMPT_VERSION`
- [ ] gold set 리포트 생성 완료
- [ ] review backlog acceptable
- [ ] null rate report 검토 완료
- [ ] label distribution sanity check 완료
- [x] dataset manifest에 commit hash 포함 → `libs/export/snapshot_export.py::_get_git_commit_hash`
- [x] raw prompt/response 보관 정책 확인 → `libs/db/models.py::LLMCallCache`

## 7. 신규 파일 목록 (Phase 3)

| 경로 | 상태 |
|---|---|
| `libs/llm/__init__.py` | ✅ |
| `libs/llm/exceptions.py` | ✅ |
| `libs/llm/client.py` | ✅ |
| `libs/llm/cache.py` | ✅ |
| `libs/llm/prompts.py` | ✅ |
| `libs/llm/parser.py` | ✅ |
| `libs/parser/merger.py` | ✅ |
| `libs/labeler/__init__.py` | ✅ |
| `libs/labeler/reaction_date.py` | ✅ |
| `libs/labeler/label_generator.py` | ✅ |
| `libs/review/__init__.py` | ✅ |
| `libs/review/queue.py` | ✅ |
| `libs/export/__init__.py` | ✅ |
| `libs/export/snapshot_export.py` | ✅ |
| `libs/db/models.py` (+ 3 models) | ✅ |
| `libs/db/migrations/versions/0002_phase3_tables.py` | ✅ |
| `libs/common/config.py` (+ Ollama settings) | ✅ |
| `apps/pipeline/label_generator/main.py` | ✅ |
| `apps/pipeline/dataset_export/main.py` | ✅ |
| `apps/review/cli.py` | ✅ |
| `apps/qa/evaluate_gold_set.py` | ✅ |
| `tests/unit/test_llm_client.py` | ✅ |
| `tests/unit/test_llm_cache.py` | ✅ |
| `tests/unit/test_merger.py` | ✅ |
| `tests/unit/test_labeler.py` | ✅ |
| `tests/unit/test_review_queue.py` | ✅ |
| `tests/unit/test_snapshot_export.py` | ✅ |
| `tests/integration/test_label_pipeline.py` | ✅ |
| `tests/integration/test_review_queue_integration.py` | ✅ |
| `tests/replay/test_llm_cache_replay.py` | ✅ |
| `.env.example` (+ Ollama vars) | ✅ |
