"""DB 타임스탬프 KST 통일 검증. 2026-09-23: 모델 기본값은 datetime.utcnow(UTC), 일부 경로는 _get_kst_now(KST)로 저장해 같은 테이블 안에서 9시간이 섞였다(화면 매매 시각이 02:14 처럼 표시). 조회 구간도 utcnow 기준이라 비교가 9시간 어긋났다(추가매수 쿨다운 상시 발동). """ import inspect import pathlib from datetime import datetime, timedelta import pytz from app.core.timeutil import KST, now_kst, today_kst from app.models.models import ActiveTrade, Portfolio, TradeHistory def test_now_kst_is_naive_korean_time(): v = now_kst() assert v.tzinfo is None real = datetime.now(pytz.timezone("Asia/Seoul")).replace(tzinfo=None) assert abs((real - v).total_seconds()) < 5 assert today_kst() == real.date() def test_now_kst_differs_from_utc_by_nine_hours(): delta = now_kst() - datetime.utcnow() assert 8.9 * 3600 < delta.total_seconds() < 9.1 * 3600 def test_model_defaults_use_kst(): for model, cols in [ (TradeHistory, ["created_at"]), (ActiveTrade, ["created_at", "updated_at"]), (Portfolio, ["updated_at"]), ]: for col in cols: default = model.__table__.columns[col].default assert default is not None, f"{model.__name__}.{col} 기본값 없음" assert getattr(default.arg, "__name__", "") == "now_kst", ( f"{model.__name__}.{col} 이 KST 기준이 아님" ) def test_no_utcnow_left_in_app_code(): """app/ 전체에서 utcnow() 호출이 남아 있으면 다시 섞인다.""" root = pathlib.Path(__file__).resolve().parents[1] / "app" offenders = [] for f in root.rglob("*.py"): for i, line in enumerate(f.read_text().splitlines(), 1): code = line.split("#", 1)[0] if "utcnow()" in code: offenders.append(f"{f.relative_to(root)}:{i}") assert not offenders, f"utcnow() 잔존: {offenders}" def test_cooldown_compares_same_basis(): from app.services import trade_executor as te src = inspect.getsource(te) i = src.index("recent_buys[0].created_at") window = src[max(0, i - 300): i + 100] assert "now_kst()" in window, "쿨다운 비교가 KST 기준이 아님"