근본원인: stock 컨테이너는 python:3.12-alpine + tzdata 미설치라 TZ=Asia/Seoul이 무효 → date.today()가 UTC를 반환. AI 뉴스 리포트 cron은 08:00 KST(=전날 23:00 UTC)라 asof가 어제로 계산돼 라벨·기사 윈도우·news_sentiment 저장이 전부 하루 밀렸음 (월요일은 일요일 UTC로 계산돼 skip_weekend까지). - screener/router.py: _today_kst()(=utcnow+9h, holdings_intel 관용) 추가. /snapshot/refresh · /snapshot/refresh-news-sentiment의 asof 기본값을 KST로. - ai_news/analyzer.py: score_sentiment(asof=...) → 프롬프트 앞에 "오늘 날짜" 명시, LLM이 현재 일자 기준으로 뉴스 평가(사용자 요청). - ai_news/pipeline.py: refresh_daily가 asof를 score_sentiment까지 스레딩. - 테스트: _today_kst KST 보정 + analyzer asof 주입 2종 TDD Red→Green. 기존 pipeline 목 시그니처에 asof 반영. stock 전체 149 passed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EqCYBhvTcdeCTUDX3RhWx9
52 lines
1.9 KiB
Python
52 lines
1.9 KiB
Python
import datetime as dt
|
|
from unittest.mock import AsyncMock, patch
|
|
from fastapi.testclient import TestClient
|
|
|
|
from app.main import app
|
|
|
|
|
|
def test_today_kst_uses_kst_offset_not_utc(monkeypatch):
|
|
"""컨테이너가 UTC(Alpine, tzdata 미설치)라 date.today()는 08시 KST에 어제를 준다.
|
|
_today_kst()는 UTC+9로 보정해 오늘(KST)을 반환해야 한다."""
|
|
from app.screener import router
|
|
|
|
class _FrozenDT(dt.datetime):
|
|
@classmethod
|
|
def utcnow(cls):
|
|
# 2026-07-01 23:30 UTC == 2026-07-02 08:30 KST (AI 뉴스 리포트 시각대)
|
|
return dt.datetime(2026, 7, 1, 23, 30, 0)
|
|
|
|
monkeypatch.setattr(router.dt, "datetime", _FrozenDT)
|
|
assert router._today_kst() == dt.date(2026, 7, 2)
|
|
|
|
|
|
def test_refresh_news_sentiment_weekend_skip():
|
|
# 2026-05-16 = Saturday
|
|
client = TestClient(app)
|
|
resp = client.post(
|
|
"/api/stock/screener/snapshot/refresh-news-sentiment?asof=2026-05-16"
|
|
)
|
|
assert resp.status_code == 200
|
|
assert resp.json()["status"] == "skipped_weekend"
|
|
|
|
|
|
def test_refresh_news_sentiment_weekday_invokes_pipeline():
|
|
fake_summary = {
|
|
"asof": "2026-05-13", "updated": 3, "failures": [],
|
|
"duration_sec": 1.0, "tokens_input": 100, "tokens_output": 20,
|
|
"top_pos": [], "top_neg": [], "model": "m",
|
|
"mapping": {"total_articles": 5, "matched_pairs": 8, "hit_tickers": 3},
|
|
}
|
|
with patch("app.screener.router._ai_pipeline") as mp, \
|
|
patch("app.screener.router._ai_telegram") as mt:
|
|
mp.refresh_daily = AsyncMock(return_value=fake_summary)
|
|
mt.build_message = lambda **kw: f"TEXT_with_mapping={kw.get('mapping')}"
|
|
client = TestClient(app)
|
|
resp = client.post(
|
|
"/api/stock/screener/snapshot/refresh-news-sentiment?asof=2026-05-13"
|
|
)
|
|
assert resp.status_code == 200
|
|
body = resp.json()
|
|
assert body["mapping"]["hit_tickers"] == 3
|
|
assert "mapping=" in body["telegram_text"]
|