You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

99 regels
3.4 KiB

  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()