Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374
  1. import unittest
  2. from structs.headline import Headline
  3. from structs.story import Story
  4. from services.stories import cluster_stories
  5. def h(text, tokens, url=""):
  6. return Headline(text, tokens, url)
  7. class TestHeadlineSourceTracking(unittest.TestCase):
  8. def test_domain_extraction_strips_www_and_lowercases(self):
  9. headline = h("A story", ["a"], "https://www.CNN.com/us")
  10. self.assertEqual(headline.domain, "cnn.com")
  11. def test_domain_extraction_section_paths_collapse(self):
  12. us = h("A story", ["a"], "https://www.cnn.com/us")
  13. politics = h("A story", ["a"], "https://cnn.com/politics")
  14. self.assertEqual(us.domain, politics.domain)
  15. def test_domain_is_none_when_source_missing(self):
  16. self.assertIsNone(h("A story", ["a"]).domain)
  17. self.assertIsNone(h("A story", ["a"], "").domain)
  18. self.assertIsNone(h("A story", ["a"], " ").domain)
  19. class TestStory(unittest.TestCase):
  20. def test_sources_dedup_preserving_order(self):
  21. story = Story([
  22. h("Story A", ["a"], "https://www.cnn.com/us"),
  23. h("Story A", ["a"], "https://foxnews.com/politics"),
  24. h("Story A", ["a"], "https://www.cnn.com/politics"),
  25. ])
  26. self.assertEqual(story.sources, ["cnn.com", "foxnews.com"])
  27. self.assertEqual(story.source_count, 2)
  28. def test_representative_is_longest_headline(self):
  29. story = Story([
  30. h("Short", ["short"], "https://a.com"),
  31. h("A much longer descriptive headline", ["long"], "https://b.com"),
  32. ])
  33. self.assertEqual(story.representative, "A much longer descriptive headline")
  34. class TestClusterStories(unittest.TestCase):
  35. def test_transitive_clustering_merges_chain(self):
  36. # Three headlines sharing tokens pairwise but not as a triple.
  37. headlines = [
  38. h("alpha beta", ["alpha", "beta"], "https://a.com"),
  39. h("beta gamma", ["beta", "gamma"], "https://b.com"),
  40. h("delta epsilon", ["delta", "epsilon"], "https://c.com"),
  41. ]
  42. stories = cluster_stories(headlines, threshold=0.4)
  43. texts = {s.representative for s in stories}
  44. # alpha/beta and beta/gamma share "beta" -> one cluster; delta/epsilon separate.
  45. self.assertEqual(len(stories), 2)
  46. self.assertIn("alpha beta", texts)
  47. self.assertIn("delta epsilon", texts)
  48. def test_non_similar_headlines_stay_separate(self):
  49. headlines = [
  50. h("apple pie", ["apple", "pie"], "https://a.com"),
  51. h("quantum physics", ["quantum", "physics"], "https://b.com"),
  52. ]
  53. stories = cluster_stories(headlines, threshold=0.6)
  54. self.assertEqual(len(stories), 2)
  55. def test_empty_input(self):
  56. self.assertEqual(cluster_stories([], 0.75), [])
  57. if __name__ == "__main__":
  58. unittest.main()