Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

117 строки
4.0 KiB

  1. import unittest
  2. from datetime import date, datetime, timezone
  3. from unittest.mock import patch, MagicMock
  4. from services.dates import (
  5. parse_datetime,
  6. parse_date_arg,
  7. date_part,
  8. filter_by_date_window,
  9. )
  10. from services.headlines import prepare_headlines
  11. from structs.headline import Headline
  12. def h(text, tokens, dt=None, url="https://a.com"):
  13. return Headline(text, tokens, url, dt)
  14. class TestParseDatetime(unittest.TestCase):
  15. def test_iso_with_z(self):
  16. dt = parse_datetime("2026-09-14T14:30:00Z")
  17. self.assertIsNotNone(dt)
  18. self.assertEqual(dt.year, 2026)
  19. def test_iso_with_offset(self):
  20. dt = parse_datetime("2026-09-14T10:30:00-04:00")
  21. self.assertEqual((dt.month, dt.day), (9, 14))
  22. def test_rfc2822(self):
  23. dt = parse_datetime("Mon, 14 Sep 2026 12:30:00 GMT")
  24. self.assertEqual((dt.year, dt.month, dt.day), (2026, 9, 14))
  25. def test_human_month_day_year(self):
  26. dt = parse_datetime("September 14, 2026")
  27. self.assertEqual((dt.month, dt.day), (9, 14))
  28. def test_garbage_and_relative_return_none(self):
  29. self.assertIsNone(parse_datetime("3 hours ago"))
  30. self.assertIsNone(parse_datetime("not a date"))
  31. self.assertIsNone(parse_datetime(""))
  32. self.assertIsNone(parse_datetime(None))
  33. class TestParseDateArg(unittest.TestCase):
  34. def test_valid(self):
  35. self.assertEqual(parse_date_arg("2026-09-14"), date(2026, 9, 14))
  36. def test_invalid(self):
  37. with self.assertRaises(ValueError):
  38. parse_date_arg("14-09-2026")
  39. class TestDatePart(unittest.TestCase):
  40. def test_aware_normalized_to_utc(self):
  41. dt = datetime(2026, 9, 14, 23, 30, tzinfo=timezone.utc)
  42. self.assertEqual(date_part(dt), date(2026, 9, 14))
  43. def test_naive_kept(self):
  44. dt = datetime(2026, 9, 14, 23, 30)
  45. self.assertEqual(date_part(dt), date(2026, 9, 14))
  46. class TestFilterByDateWindow(unittest.TestCase):
  47. def setUp(self):
  48. self.headlines = [
  49. h("old story", ["old"], datetime(2026, 9, 10)),
  50. h("recent story", ["recent"], datetime(2026, 9, 14)),
  51. h("undated story", ["undated"], None),
  52. ]
  53. def test_no_bounds_keeps_all(self):
  54. kept, dropped = filter_by_date_window(self.headlines)
  55. self.assertEqual(len(kept), 3)
  56. self.assertEqual(dropped, 0)
  57. def test_since_drops_undated_and_old(self):
  58. kept, dropped = filter_by_date_window(self.headlines, since=date(2026, 9, 13))
  59. self.assertEqual([x.display_text for x in kept], ["recent story"])
  60. self.assertEqual(dropped, 1)
  61. def test_until(self):
  62. kept, _ = filter_by_date_window(self.headlines, until=date(2026, 9, 12))
  63. self.assertEqual([x.display_text for x in kept], ["old story"])
  64. def test_inclusive_bounds(self):
  65. kept, _ = filter_by_date_window(self.headlines, since=date(2026, 9, 14), until=date(2026, 9, 14))
  66. self.assertEqual([x.display_text for x in kept], ["recent story"])
  67. class TestDateCapturedFromHtml(unittest.TestCase):
  68. def setUp(self):
  69. self.stopwords = {"the", "a", "an", "in", "on"}
  70. @patch("services.headlines.requests.get")
  71. def test_time_element_associated_with_headline(self, mock_get):
  72. html = (
  73. "<html><body>"
  74. "<article><time datetime='2026-09-14T14:30:00Z'>Sep 14</time>"
  75. "<a href='/news'>Breaking News Story</a></article>"
  76. "<article><span>Another Story Here</span></article>"
  77. "</body></html>"
  78. )
  79. mock_response = MagicMock()
  80. mock_response.status_code = 200
  81. mock_response.content = html.encode("utf-8")
  82. mock_response.text = html
  83. mock_get.return_value = mock_response
  84. headlines = prepare_headlines(["https://example.com/news"], self.stopwords)
  85. by_text = {x.display_text: x for x in headlines}
  86. self.assertIsNotNone(by_text["Breaking News Story"].published_at)
  87. self.assertIsNone(by_text["Another Story Here"].published_at)
  88. if __name__ == "__main__":
  89. unittest.main()