"""Tests for news time window filtering.""" from __future__ import annotations import os import unittest from datetime import datetime, timezone, timedelta from unittest.mock import patch from zoneinfo import ZoneInfo from daily.news.fetch import _cutoff_datetime, _parse_datetime, _within_window class NewsWindowTests(unittest.TestCase): def test_cutoff_floor_today_excludes_yesterday_even_within_24h(self): tz = ZoneInfo("Asia/Shanghai") # 2026-07-09 09:00 CST = 2026-07-09 01:00 UTC fixed = datetime(2026, 7, 9, 1, 0, tzinfo=timezone.utc) with patch("daily.news.fetch._now_utc", return_value=fixed): with patch.dict(os.environ, {"DAILY_AI_NEWS_HOURS": "24"}, clear=False): cutoff = _cutoff_datetime(floor_today=True) start_today_cst = datetime(2026, 7, 9, 0, 0, tzinfo=tz).astimezone(timezone.utc) self.assertEqual(cutoff, start_today_cst) yesterday = datetime(2026, 7, 8, 20, 0, tzinfo=tz).astimezone(timezone.utc) self.assertFalse(_within_window({"published": yesterday.isoformat()}, cutoff)) def test_cutoff_rolling_only_includes_last_24h(self): fixed = datetime(2026, 7, 9, 12, 0, tzinfo=timezone.utc) with patch("daily.news.fetch._now_utc", return_value=fixed): with patch.dict(os.environ, {"DAILY_AI_NEWS_HOURS": "24"}, clear=False): cutoff = _cutoff_datetime(floor_today=False) self.assertEqual(cutoff, fixed - timedelta(hours=24)) def test_within_window_rejects_missing_datetime(self): cutoff = datetime(2026, 7, 9, 0, 0, tzinfo=timezone.utc) self.assertFalse(_within_window({"title": "x", "link": "https://a.com"}, cutoff)) def test_parse_date_only_uses_local_noon(self): with patch.dict(os.environ, {"DAILY_AI_NEWS_TZ": "Asia/Shanghai"}, clear=False): dt = _parse_datetime("2026-07-09") self.assertIsNotNone(dt) assert dt is not None local = dt.astimezone(ZoneInfo("Asia/Shanghai")) self.assertEqual(local.hour, 12) class NewsFormatTests(unittest.TestCase): def test_ai_news_lines_use_desc_as_link_text(self): from daily.format_wecom import _ai_news_lines lines = _ai_news_lines( [ { "title": "English Title", "link": "https://example.com/a", "source_name": "Src", "published_fmt": "07-11", "desc_short": "中文摘要一句", } ] ) self.assertEqual(len(lines), 1) self.assertIn("[中文摘要一句](https://example.com/a)", lines[0]) self.assertNotIn("English Title", lines[0]) self.assertNotIn("> ", lines[0]) def test_ai_news_lines_fallback_to_title(self): from daily.format_wecom import _ai_news_lines lines = _ai_news_lines( [ { "title": "仅标题", "link": "https://example.com/b", "source_name": "Src", "published_fmt": "", "desc_short": "", } ] ) self.assertIn("[仅标题](https://example.com/b)", lines[0]) class NewsSummaryTests(unittest.TestCase): def test_brief_news_summary_no_ellipsis(self): from daily.news.fetch import brief_news_summary text = ( "Meta told Dylan Byers, of Puck News, that the company removed " "the controversial AI feature after user backlash on Instagram." ) out = brief_news_summary(text, limit=72) self.assertNotIn("...", out) self.assertLessEqual(len(out), 72) self.assertTrue(out.startswith("Meta told")) def test_brief_news_summary_filters_junk(self): from daily.news.fetch import brief_news_summary self.assertEqual(brief_news_summary("点击查看原文>"), "") self.assertEqual(brief_news_summary("Article URL: https://example.com"), "") def test_sync_wecom_news_rows_after_localize(self): from daily.news.fetch import _to_wecom_news_row, sync_wecom_news_rows row = _to_wecom_news_row( { "title": "t", "link": "https://a.com/x", "source_name": "s", "published_fmt": "07-11", "summary": "Short english stub that was truncated early...", } ) flat = [ { "link": "https://a.com/x", "summary": "苹果指控 OpenAI 窃取硬件商业机密,诉讼称 misconduct 涉及多名前员工。", } ] sync_wecom_news_rows([row], flat) self.assertNotIn("...", row["desc_short"]) self.assertIn("苹果", row["desc_short"]) def test_finalize_wecom_news_forces_chinese(self): from daily.news.fetch import finalize_wecom_news_items items = [ { "link": "https://a.com/1", "desc_short": "Meta removed the feature after backlash.", "summary_plain": "Meta removed the feature after backlash.", } ] with patch( "daily.localize.localize_brief_descriptions", return_value={"wecom-news:https://a.com/1": "Meta 在舆论压力下移除了该功能"}, ): finalize_wecom_news_items(items, force_chinese=True) self.assertIn("Meta", items[0]["desc_short"]) self.assertNotIn("backlash", items[0]["desc_short"]) class NewsPickTests(unittest.TestCase): def test_pick_and_backfill_to_limit(self): from daily.news.fetch import _fill_picked_to_limit, _pick_news_items flat = [ {"link": f"https://a.com/{i}", "title": f"t{i}", "category_id": "media", "summary": "s"} for i in range(12) ] picked = _pick_news_items(flat, 10, ("media",)) self.assertEqual(len(picked), 10) picked = _fill_picked_to_limit(picked[:3], [flat], 10) self.assertEqual(len(picked), 10)