diff --git a/requirements-test.txt b/requirements-test.txt index 1346600..4c7875f 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -9,9 +9,6 @@ pytest==7.4.4 pytest-cov==4.1.0 pytest-mock==3.12.0 -# HTTP mocking for integration tests -responses==0.24.1 - # Code quality flake8==7.0.0 black==24.1.1 diff --git a/test_integration.py b/test_integration.py index 2c5a367..dfe04f1 100644 --- a/test_integration.py +++ b/test_integration.py @@ -4,10 +4,7 @@ import unittest from unittest.mock import Mock, patch import tempfile import os -import time from bot import MastodonRSSBot -import responses -import feedparser class TestRSSFeedIntegration(unittest.TestCase): @@ -31,37 +28,17 @@ class TestRSSFeedIntegration(unittest.TestCase): if os.path.exists(self.test_config["state_file"]): os.remove(self.test_config["state_file"]) - @responses.activate + @patch("bot.feedparser.parse") @patch("bot.Mastodon") - def test_end_to_end_rss_to_mastodon(self, mock_mastodon): + def test_end_to_end_rss_to_mastodon(self, mock_mastodon, mock_parse): """Test complete flow from RSS feed to Mastodon post""" - # Mock RSS feed response - rss_feed = """ - - - Test Feed - https://example.com - Test RSS Feed - - First Article - https://example.com/article1 - This is the first article - - - Second Article - https://example.com/article2 - This is the second article - - -""" - - responses.add( - responses.GET, - "https://example.com/feed.xml", - body=rss_feed.encode("utf-8"), - status=200, - content_type="application/xml; charset=utf-8", - ) + # Create mock feed object + mock_feed = Mock() + mock_feed.entries = [ + {"title": "First Article", "link": "https://example.com/article1"}, + {"title": "Second Article", "link": "https://example.com/article2"}, + ] + mock_parse.return_value = mock_feed # Mock Mastodon instance mock_instance = Mock() @@ -82,31 +59,16 @@ class TestRSSFeedIntegration(unittest.TestCase): self.assertIn("Second Article", calls[1][0][0]) self.assertIn("https://example.com/article2", calls[1][0][0]) - @responses.activate + @patch("bot.feedparser.parse") @patch("bot.Mastodon") - def test_atom_feed_parsing(self, mock_mastodon): + def test_atom_feed_parsing(self, mock_mastodon, mock_parse): """Test parsing Atom feeds""" - atom_feed = """ - - Test Atom Feed - - 2024-01-01T00:00:00Z - - Atom Article - - https://example.com/atom1 - 2024-01-01T00:00:00Z - This is an atom article - -""" - - responses.add( - responses.GET, - "https://example.com/feed.xml", - body=atom_feed.encode("utf-8"), - status=200, - content_type="application/atom+xml; charset=utf-8", - ) + # Create mock Atom feed object + mock_feed = Mock() + mock_feed.entries = [ + {"title": "Atom Article", "link": "https://example.com/atom1"} + ] + mock_parse.return_value = mock_feed mock_instance = Mock() mock_mastodon.return_value = mock_instance @@ -118,28 +80,14 @@ class TestRSSFeedIntegration(unittest.TestCase): calls = mock_instance.status_post.call_args_list self.assertIn("Atom Article", calls[0][0][0]) - @responses.activate + @patch("bot.feedparser.parse") @patch("bot.Mastodon") - def test_persistence_across_runs(self, mock_mastodon): + def test_persistence_across_runs(self, mock_mastodon, mock_parse): """Test that processed entries persist across multiple bot runs""" - rss_feed = """ - - - Test Feed - - Article 1 - https://example.com/1 - - -""" - - responses.add( - responses.GET, - "https://example.com/feed.xml", - body=rss_feed.encode("utf-8"), - status=200, - content_type="application/xml; charset=utf-8", - ) + # Create mock feed object + mock_feed = Mock() + mock_feed.entries = [{"title": "Article 1", "link": "https://example.com/1"}] + mock_parse.return_value = mock_feed mock_instance = Mock() mock_mastodon.return_value = mock_instance @@ -157,70 +105,31 @@ class TestRSSFeedIntegration(unittest.TestCase): # Total posts should be 1 self.assertEqual(mock_instance.status_post.call_count, 1) - @responses.activate + @patch("bot.feedparser.parse") @patch("bot.Mastodon") - def test_incremental_feed_updates(self, mock_mastodon): + def test_incremental_feed_updates(self, mock_mastodon, mock_parse): """Test handling of new entries added to feed over time""" - # Initial feed with 2 articles - initial_feed = """ - - - Test Feed - - Article 1 - https://example.com/1 - - - Article 2 - https://example.com/2 - - -""" - - responses.add( - responses.GET, - "https://example.com/feed.xml", - body=initial_feed, - status=200, - content_type="application/xml", - ) - mock_instance = Mock() mock_mastodon.return_value = mock_instance - # First run + # First run - initial feed with 2 articles + mock_feed = Mock() + mock_feed.entries = [ + {"title": "Article 1", "link": "https://example.com/1"}, + {"title": "Article 2", "link": "https://example.com/2"}, + ] + mock_parse.return_value = mock_feed + bot = MastodonRSSBot(**self.test_config) count1 = bot.process_new_entries() self.assertEqual(count1, 2) - # Update feed with 1 new article - responses.reset() - updated_feed = """ - - - Test Feed - - Article 3 - https://example.com/3 - - - Article 2 - https://example.com/2 - - - Article 1 - https://example.com/1 - - -""" - - responses.add( - responses.GET, - "https://example.com/feed.xml", - body=updated_feed, - status=200, - content_type="application/xml", - ) + # Second run - updated feed with 1 new article + mock_feed.entries = [ + {"title": "Article 3", "link": "https://example.com/3"}, + {"title": "Article 2", "link": "https://example.com/2"}, + {"title": "Article 1", "link": "https://example.com/1"}, + ] # Second run - should only post the new article count2 = bot.process_new_entries() @@ -229,16 +138,12 @@ class TestRSSFeedIntegration(unittest.TestCase): # Verify only 3 total posts self.assertEqual(mock_instance.status_post.call_count, 3) - @responses.activate + @patch("bot.feedparser.parse") @patch("bot.Mastodon") - def test_network_error_handling(self, mock_mastodon): + def test_network_error_handling(self, mock_mastodon, mock_parse): """Test handling of network errors when fetching feed""" - responses.add( - responses.GET, - "https://example.com/feed.xml", - body="Network error", - status=500, - ) + # Simulate network error by returning None + mock_parse.return_value = None mock_instance = Mock() mock_mastodon.return_value = mock_instance @@ -250,25 +155,15 @@ class TestRSSFeedIntegration(unittest.TestCase): self.assertEqual(count, 0) self.assertEqual(mock_instance.status_post.call_count, 0) - @responses.activate + @patch("bot.feedparser.parse") @patch("bot.Mastodon") - def test_malformed_xml_handling(self, mock_mastodon): + def test_malformed_xml_handling(self, mock_mastodon, mock_parse): """Test handling of malformed XML feeds""" - malformed_feed = """ - - - Broken Feed - <item> - <title>Article - """ # Intentionally malformed - - responses.add( - responses.GET, - "https://example.com/feed.xml", - body=malformed_feed, - status=200, - content_type="application/xml", - ) + # Create mock feed with bozo_exception (feedparser's error indicator) + mock_feed = Mock() + mock_feed.entries = [] + mock_feed.bozo_exception = Exception("XML parsing error") + mock_parse.return_value = mock_feed mock_instance = Mock() mock_mastodon.return_value = mock_instance