Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. import os
  2. import tempfile
  3. import unittest
  4. from unittest.mock import patch, MagicMock
  5. from services.headlines import is_headline, prepare_headlines
  6. from services.normalization import canonicalize, get_excluded_phrases, is_excluded
  7. PHRASES = [
  8. "skip to content",
  9. "privacy choices",
  10. "terms of service",
  11. "privacy policy",
  12. ]
  13. class TestCanonicalize(unittest.TestCase):
  14. def test_lowercase_and_collapse(self):
  15. self.assertEqual(canonicalize(" Terms-of-Service. "), "terms of service")
  16. def test_empty(self):
  17. self.assertEqual(canonicalize(""), "")
  18. self.assertEqual(canonicalize(None), "")
  19. class TestIsExcluded(unittest.TestCase):
  20. def test_exact_match(self):
  21. self.assertTrue(is_excluded("Terms of Service", PHRASES))
  22. def test_sub_phrase_match(self):
  23. # "privacy choices" matches the longer "Your Privacy Choices".
  24. self.assertTrue(is_excluded("Your Privacy Choices", PHRASES))
  25. def test_case_and_punctuation_insensitive(self):
  26. self.assertTrue(is_excluded("SKIP-TO-CONTENT", PHRASES))
  27. def test_no_match(self):
  28. self.assertFalse(is_excluded("Federal Reserve holds rates steady", PHRASES))
  29. def test_no_phrases(self):
  30. self.assertFalse(is_excluded("anything here now", []))
  31. self.assertFalse(is_excluded("anything here now", None))
  32. class TestGetExcludedPhrases(unittest.TestCase):
  33. def test_loads_skipping_comments_and_blanks(self):
  34. path = None
  35. try:
  36. with tempfile.NamedTemporaryFile("w", suffix=".txt", delete=False) as f:
  37. f.write("# a comment\n\nskip to content\n\nprivacy choices\n# another\n")
  38. path = f.name
  39. self.assertEqual(get_excluded_phrases(path), ["skip to content", "privacy choices"])
  40. finally:
  41. if path:
  42. os.unlink(path)
  43. class TestIsHeadlineExcluded(unittest.TestCase):
  44. def setUp(self):
  45. self.stopwords = {"the", "a", "an", "in", "on", "and", "of", "to"}
  46. def test_boilerplate_rejected(self):
  47. for text in ["Your Privacy Choices", "Skip to Content", "Terms of Service"]:
  48. with self.subTest(text=text):
  49. self.assertFalse(is_headline(text, self.stopwords, PHRASES))
  50. def test_real_headline_kept(self):
  51. self.assertTrue(is_headline("Senate passes major infrastructure bill", self.stopwords, PHRASES))
  52. class TestPrepareHeadlinesExcluded(unittest.TestCase):
  53. def setUp(self):
  54. self.stopwords = {"the", "a", "an", "in", "on", "and", "of", "to"}
  55. @patch("services.headlines.requests.get")
  56. def test_boilerplate_links_are_filtered_out(self, mock_get):
  57. html = (
  58. "<html><body>"
  59. "<a href='/skip'>Skip to Content</a>"
  60. "<a href='/terms'>Terms of Service</a>"
  61. "<a href='/privacy'>Your Privacy Choices</a>"
  62. "<a href='/article'>Senate passes major infrastructure bill</a>"
  63. "</body></html>"
  64. )
  65. mock_response = MagicMock()
  66. mock_response.status_code = 200
  67. mock_response.content = html.encode("utf-8")
  68. mock_response.text = html
  69. mock_get.return_value = mock_response
  70. headlines = prepare_headlines(["https://example.com/news"], self.stopwords, excluded_phrases=PHRASES)
  71. texts = [h.display_text for h in headlines]
  72. self.assertEqual(texts, ["Senate passes major infrastructure bill"])
  73. if __name__ == "__main__":
  74. unittest.main()