"""절전·재부팅 후 캔들 공백 백필 계획 테스트.""" from __future__ import annotations from datetime import datetime, timedelta from bithumb.data.gap_backfill import coverage_minutes, days_needed, plan_backfill def test_coverage_and_days() -> None: assert coverage_minutes(1) == 180.0 # 200봉 × 1분 × 0.9 assert coverage_minutes(15) == 2700.0 assert days_needed(50) == 2 # 1일 미만 공백 → ceil(0.03)+1 assert days_needed(1500) == 3 # 1.04일 → 2+1 def test_plan_no_gap_within_incremental_range() -> None: now = datetime(2026, 9, 10, 8, 11) db = {"XRP": {1: now - timedelta(minutes=50), 15: now - timedelta(minutes=60)}} plan = plan_backfill(db, now=now, intervals=[1, 15]) assert plan.empty and plan.describe() == "no gap" def test_plan_detects_1m_gap_only() -> None: now = datetime(2026, 9, 10, 8, 11) # 1분봉 5시간 공백(> 180분) → 백필, 15분봉은 200봉(50h) 범위 내 db = { "XRP": {1: now - timedelta(hours=5), 15: now - timedelta(hours=5)}, "SUI": {1: now - timedelta(minutes=30), 15: now - timedelta(minutes=30)}, } plan = plan_backfill(db, now=now, intervals=[1, 15]) assert plan.needs == {"XRP": {1: 2}} assert plan.symbols() == ["XRP"] and plan.intervals() == [1] and plan.max_days() == 2 def test_plan_long_outage_multiple_intervals_and_cap() -> None: now = datetime(2026, 9, 20, 0, 0) db = {"ETH": {1: now - timedelta(days=5), 15: now - timedelta(days=5), 1440: now - timedelta(days=5)}} plan = plan_backfill(db, now=now, intervals=[1, 15, 1440], max_days=4) # 1m·15m은 5일 공백 → 6일 필요하나 상한 4일, 일봉은 200봉(180일) 범위 내 assert plan.needs == {"ETH": {1: 4, 15: 4}} def test_plan_skips_never_loaded_tables() -> None: now = datetime(2026, 9, 10, 8, 11) plan = plan_backfill({"NEW": {1: None, 15: None}}, now=now, intervals=[1, 15]) assert plan.empty