You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

13 KiB

Phase 1 테스팅 체크리스트

실제 구현 기준 재작성 (2026-03-12). Stock Oracle(localhost:18001)을 data intermediary로 사용하는 아키텍처 기준. [x] = 테스트 통과 확인, [ ] = 미커버 또는 미통과.


1. 테스트 레벨

레벨 실행 명령 상태
단위 테스트 make test-unit 100 tests pass
리플레이 테스트 make test-replay 5 tests pass
통합 테스트 make test-integration Docker postgres 필요
운영 전 수동 점검 아래 섹션 참조

2. 단위 테스트 체크리스트

2.1 config (tests/unit/test_config.py)

  • Settings가 기본값을 올바르게 로드한다 — test_settings_defaults
  • log_level이 대문자로 정규화된다 — test_log_level_normalized
  • exhibit_cache_dir이 data_root 기반 경로를 반환한다 — test_exhibit_cache_dir
  • parquet_dir이 data_root 기반 경로를 반환한다 — test_parquet_dir
  • get_symbols가 YAML에서 심볼 목록을 반환한다 — test_get_symbols

2.2 ids (tests/unit/test_ids.py)

  • document_id 포맷이 규칙(DOC::source::issuer_id:📅:acc)을 만족한다 — test_document_id_format
  • event_id 포맷이 규칙(EVT::...)을 만족하며 event_type이 포함된다 — test_event_id_format
  • CIK에서 issuer_id가 0-padded 10자리로 생성된다 — test_issuer_id_from_cik
  • ticker에서 symbol_id가 기본 venue(XNYS)로 생성된다 — test_symbol_id_from_ticker
  • ticker가 대문자로 정규화되고 custom venue가 반영된다 — test_symbol_id_uppercase
  • sha256_checksum이 동일 입력에 대해 deterministic 하다 — test_sha256_checksum
  • 다른 입력에 대해 다른 checksum이 생성된다 — test_sha256_different
  • new_job_run_id가 유효한 UUID를 반환한다 — test_new_job_run_id

2.3 time_utils (tests/unit/test_time_utils.py)

  • utc_now()가 tz-aware datetime을 반환한다 — test_utc_now
  • UTC → Eastern 변환이 정확하다 (21:05 UTC = 16:05 ET) — test_to_eastern
  • Eastern → UTC 변환이 정확하다 — test_to_utc
  • naive datetime은 변환 시 ValueError가 발생한다 — test_naive_datetime_rejected
  • 21:05 UTC는 "post_market"으로 분류된다 — test_filing_time_bucket_post_market
  • 12:00 UTC (07:00 ET)는 "pre_market"으로 분류된다 — test_filing_time_bucket_pre_market
  • 15:00 UTC (10:00 ET)는 "regular_hours"로 분류된다 — test_filing_time_bucket_regular
  • naive datetime은 "unknown"으로 분류된다 — test_filing_time_bucket_naive

2.4 file_store (tests/unit/test_file_store.py)

  • exhibit 쓰기/읽기가 정상 작동하고 checksum을 반환한다 — test_write_and_read_exhibit
  • exists_exhibit이 존재 여부를 정확히 반환한다 — test_exists_exhibit
  • get_checksum이 동일 파일에 대해 deterministic 하다 — test_checksum_deterministic
  • exhibit 경로에 안전한 파일명(EX-99.1.txt)이 사용된다 — test_exhibit_path_safe_chars

2.5 retries (tests/unit/test_retries.py)

  • 예외 계층 구조(RetryableError/NonRetryableError/ValidationError/DependencyError)가 올바르다 — test_exception_hierarchy
  • 에러 필드(source/entity/context)가 올바르게 저장된다 — test_error_fields
  • 첫 시도에 성공하면 1회만 호출된다 — test_with_retry_succeeds_on_first_attempt
  • RetryableError 발생 시 최대 횟수까지 재시도한다 — test_with_retry_retries_on_retryable_error
  • NonRetryableError는 1회만 호출되고 즉시 raise된다 — test_with_retry_does_not_retry_non_retryable
  • max_attempts 모두 소진 후 RetryableError가 최종 raise된다 — test_with_retry_exhaustion

2.6 logging (tests/unit/test_logging.py)

  • configure_logging("DEBUG") 호출 시 예외가 발생하지 않는다 — test_configure_logging_no_error
  • bind_job_run_id 후 ContextVar에 run_id가 설정된다 — test_bind_job_run_id_in_context
  • get_logger가 info/debug/warning/error 메서드를 가진 객체를 반환한다 — test_get_logger_returns_bound_logger

2.7 oracle_client

기존 테스트 (tests/unit/test_oracle_client.py)

  • FilingsService.search_filings가 FilingSearchResponse를 반환한다 — test_search_filings
  • FilingsService.get_exhibit가 ExhibitResponse를 반환한다 — test_get_exhibit
  • PriceService.get_daily_bars가 PriceDataResponse를 반환한다 — test_get_daily_bars
  • 404 응답 시 OracleNotFoundError가 발생한다 — test_not_found_raises_oracle_not_found
  • 500 응답 시 OracleServerError가 발생한다 — test_server_error_raises_oracle_server_error
  • 일시적 500 후 성공 시 정상 결과를 반환한다 — test_get_retries_on_transient_error_then_succeeds
  • FinraService.get_short_volume가 ShortVolumeResponse를 반환한다 — test_get_short_volume
  • FinancialService.get_financial_data가 FinancialDataResponse를 반환한다 — test_get_financial_data
  • ConnectError 시 OracleConnectionError가 발생한다 (3회 재시도 후) — test_connection_error_raises_oracle_connection_error
  • ReadTimeout 시 OracleTimeoutError가 발생한다 (3회 재시도 후) — test_timeout_raises_oracle_timeout_error
  • context manager 없이 get() 호출 시 RuntimeError가 발생한다 — test_client_without_context_manager_raises

FredService (tests/unit/test_fred_service.py)

  • FredService.get_observations가 FredProxyResponse(series_id, observations)를 반환한다 — test_get_observations
  • FredService.get_series_info가 FredSeriesInfo(id, frequency)를 반환한다 — test_get_series_info

2.8 parser

text_normalizer (tests/unit/test_text_normalizer.py)

  • HTML 태그가 제거된다
  • 연속 공백/개행이 정규화된다
  • 빈 문자열 입력이 처리된다

rule_parser (tests/unit/test_rule_parser.py)

  • Item 2.02가 earnings_release로 분류된다
  • Item 7.01이 analyst_day로 분류된다
  • Item 1.01이 agreement_signed으로 분류된다
  • Item 8.01이 other_disclosure로 분류된다
  • 긍정 키워드로 bullish 방향이 감지된다
  • 부정 키워드로 bearish 방향이 감지된다
  • Guidance raised/lowered 구문이 올바르게 분류된다
  • non_gaap_heavy risk flag가 감지된다
  • financing_related risk flag가 감지된다
  • evidence에 매칭 rule_id가 포함된다
  • 메타데이터(filing_date, accepted_at_utc)가 event_date/filing_time_bucket에 반영된다

schema_validator (tests/unit/test_schema_validator.py)

  • 유효한 parser output이 validate 통과한다
  • 잘못된 event_type enum은 invalid 처리된다
  • confidence가 범위(0~1)를 벗어나면 invalid 처리된다
  • 필수 필드 누락 시 invalid 처리된다

llm_parser_stub (tests/unit/test_llm_parser_stub.py)

  • enabled=False 인 경우 parse()가 None을 반환한다 — test_disabled_returns_none
  • enabled=True 인 경우 parse()가 NotImplementedError를 발생시킨다 — test_enabled_raises_not_implemented

2.9 features

market_features (tests/unit/test_market_features.py)

  • event_date 기준 reaction_day_return이 계산된다
  • 전일 대비 pre_event_return이 계산된다
  • bars가 없으면 빈 dict가 반환된다
  • event_date가 bars에 없어도 가장 가까운 날짜로 폴백한다
  • avg_volume_20d가 bars 수에 맞게 계산된다

event_features (tests/unit/test_event_features.py)

  • bullish 방향 시 guidance_direction_score > 0이다
  • bearish 방향 시 guidance_direction_score < 0이다
  • risk_flags 비율이 올바르게 계산된다
  • confidence.overall이 feature에 포함된다
  • signals dict가 올바르게 변환된다

financial_features (tests/unit/test_financial_features.py)

  • 최신 period의 eps/gross_margin/operating_margin이 추출된다
  • period가 2개 이상일 때 eps_growth_qoq가 계산된다
  • period가 2개 이상일 때 revenue_growth_qoq가 계산된다
  • periods가 비어 있으면 빈 dict가 반환된다
  • prior eps가 0일 때 eps_growth_qoq가 None이다

2.10 db models (tests/unit/test_db_models.py)

  • IssuerMaster 필드(issuer_id, issuer_name, ticker, is_active)가 올바르게 설정된다 — test_issuer_master_fields
  • Document 기본값(parsed_status="pending")이 올바르게 설정된다 — test_document_defaults
  • Event 기본값(status="pending")이 올바르게 설정된다 — test_event_defaults
  • JobRun 필드(records_seen, records_written)가 올바르게 설정된다 — test_job_run_fields
  • DB enum 값(JobStatus/ParsedStatus/EventDirection/EventType/ParserKind)이 올바르게 정의된다 — test_enums

3. 통합 테스트 체크리스트

Docker postgres 필요 (docker compose up postgres). make test-integration으로 실행.

3.1 Filing Pipeline (tests/integration/test_filing_pipeline.py)

  • Document upsert helper가 중복 없이 idempotent 하다 — test_document_upsert_idempotency_via_helpers
  • 동일 document_id 재삽입 시 중복 row가 생기지 않는다 — test_document_upsert_idempotency
  • Event + EventParse lifecycle이 정상 작동한다 — test_event_parse_lifecycle

3.2 Sync Jobs (tests/integration/test_sync_jobs.py)

  • MacroSync: macro_observations 테이블에 적재된다 — test_macro_series_insert
  • ShortVolumeSync: short_sale_daily 테이블에 적재된다 — test_short_sale_daily_insert
  • IssuerSync: issuer_master 테이블이 갱신된다 (테스트 미작성)

3.3 DB Migration (tests/integration/test_db_migration.py)

  • 14개 테이블이 모두 생성된다 — test_all_tables_exist
  • DB 연결이 정상이다 — test_db_health

3.4 Feature Pipeline (tests/integration/test_feature_pipeline.py)

  • market_v1/event_v1 스냅샷이 정상 생성된다 — test_feature_snapshot_created
  • financial_service 제공 시 financial_v1 스냅샷이 추가 생성된다 — test_financial_v1_snapshot_created

4. 리플레이 테스트 체크리스트 (tests/replay/)

4.1 결정성 (test_determinism.py)

  • 동일 텍스트에 대해 동일 parser version이면 event_type/guidance/confidence가 동일하다 — test_parser_determinism
  • 부정/혼합 텍스트에서도 동일 결과가 나온다 — test_parser_determinism_negative_text
  • event feature 계산이 deterministic 하다 — test_feature_determinism

4.2 Idempotency (test_idempotency.py)

  • 동일 document_id로 parser를 두 번 실행해도 동일한 결과가 나온다
  • checksum이 동일 내용에 대해 항상 동일하다

5. 운영 전 수동 점검

5.1 환경

  • docker compose up postgresmake bootstrap 성공
  • make migrate 실행 후 14개 테이블 생성 확인
  • python -m apps.pipeline.filing_poller.main --dry-run 실행 가능

5.2 관측 가능성

  • 모든 job에 job_run_id가 JSON 로그에 찍힌다
  • 실패 시 로그만 보고 실패 지점을 식별 가능하다
  • records_seen / records_written / records_skipped가 job_runs에 기록된다

5.3 데이터 품질

  • Oracle에서 실제 filing 1건을 fetch해 문서 내용 확인
  • parser 결과 5건을 수동 검수해 event_type/guidance 품질 확인
  • feature snapshot 1건을 조회해 필드값이 합리적인지 확인

5.4 실패 안전성

  • Stock Oracle 응답 없을 때 OracleConnectionError → retry → 로그 확인
  • parser invalid output이 Event 테이블로 흘러가지 않음을 확인
  • DB 장애 시 JobRun status가 "failed"로 기록됨을 확인

6. CI 최소 요구 사항

  • make test-unit 통과 (105 tests)
  • make test-replay 통과 (5 tests)
  • ruff check 통과 (format check는 기존 파일 formatting 필요)
  • make test-integration 통과 (9 tests pass)
  • migration smoke test 통과

7. Phase 1 승인 기준

항목 상태 비고
단위 테스트 자동화 세트 105 tests pass
리플레이 테스트 5 tests pass
OracleClient 에러 처리 Connection/Timeout/ServerError 모두 커버
parser schema validation JSON schema 기반 validation 테스트 완료
feature snapshot 생성 market_v1/event_v1/financial_v1 코드 완료
retry/backoff 정책 with_retry exhaustion 테스트 포함
로깅 구성 configure_logging/bind/get_logger 테스트 완료
통합 테스트 9 tests pass (Docker postgres 실제 연동, Oracle은 httpx_mock)
운영 전 수동 점검 실제 Oracle 연동 환경에서 수행 필요