"""Test geomob module.""" from datetime import date from unittest.mock import Mock, mock_open, patch import lxml.html import pytest from agenda.geomob import ( GeomobEvent, extract_events, find_new_events, geomob_email, get_cached_upcoming_events_list, update, ) def test_geomob_event_dataclass(): """Test GeomobEvent dataclass creation and properties.""" event = GeomobEvent( date=date(2024, 7, 15), href="/event/london-2024-07-15", hashtag="#geomobLDN" ) assert event.date == date(2024, 7, 15) assert event.href == "/event/london-2024-07-15" assert event.hashtag == "#geomobLDN" def test_geomob_event_frozen(): """Test that GeomobEvent is frozen (immutable).""" event = GeomobEvent( date=date(2024, 7, 15), href="/event/london-2024-07-15", hashtag="#geomobLDN" ) with pytest.raises(AttributeError): event.date = date(2024, 8, 15) def test_extract_events_with_valid_html(): """Test extracting events from valid HTML.""" html_content = """
  1. July 15, 2024 #geomobLDN
  2. August 20, 2024 #geomobBER
""" tree = lxml.html.fromstring(html_content) events = extract_events(tree) assert len(events) == 2 assert events[0].date == date(2024, 7, 15) assert events[0].href == "/event/london-2024-07-15" assert events[0].hashtag == "#geomobLDN" assert events[1].date == date(2024, 8, 20) assert events[1].href == "/event/berlin-2024-08-20" assert events[1].hashtag == "#geomobBER" def test_extract_events_empty_list(): """Test extracting events from HTML with no events.""" html_content = """
""" tree = lxml.html.fromstring(html_content) events = extract_events(tree) assert events == [] def test_extract_events_no_event_list(): """Test extracting events from HTML with no event list.""" html_content = """

No events here

""" tree = lxml.html.fromstring(html_content) events = extract_events(tree) assert events == [] def test_find_new_events_with_new_events(): """Test finding new events when there are some.""" prev_events = [ GeomobEvent(date(2024, 7, 15), "/event/london-2024-07-15", "#geomobLDN"), GeomobEvent(date(2024, 8, 20), "/event/berlin-2024-08-20", "#geomobBER"), ] cur_events = [ GeomobEvent(date(2024, 7, 15), "/event/london-2024-07-15", "#geomobLDN"), GeomobEvent(date(2024, 8, 20), "/event/berlin-2024-08-20", "#geomobBER"), GeomobEvent(date(2024, 9, 25), "/event/paris-2024-09-25", "#geomobPAR"), ] new_events = find_new_events(prev_events, cur_events) assert len(new_events) == 1 assert new_events[0].date == date(2024, 9, 25) assert new_events[0].href == "/event/paris-2024-09-25" assert new_events[0].hashtag == "#geomobPAR" def test_find_new_events_no_new_events(): """Test finding new events when there are none.""" events = [ GeomobEvent(date(2024, 7, 15), "/event/london-2024-07-15", "#geomobLDN"), GeomobEvent(date(2024, 8, 20), "/event/berlin-2024-08-20", "#geomobBER"), ] new_events = find_new_events(events, events) assert new_events == [] def test_find_new_events_empty_previous(): """Test finding new events when previous list is empty.""" cur_events = [ GeomobEvent(date(2024, 7, 15), "/event/london-2024-07-15", "#geomobLDN"), GeomobEvent(date(2024, 8, 20), "/event/berlin-2024-08-20", "#geomobBER"), ] new_events = find_new_events([], cur_events) assert len(new_events) == 2 assert set(new_events) == set(cur_events) def test_geomob_email_single_event(): """Test generating email for a single new event.""" events = [GeomobEvent(date(2024, 7, 15), "/event/london-2024-07-15", "#geomobLDN")] subject, body = geomob_email(events, "https://thegeomob.com") assert subject == "1 New Geomob Event(s) Announced" assert "Hello," in body assert "Here are the new Geomob events:" in body assert "Date: 2024-07-15" in body assert "URL: https://thegeomob.com/event/london-2024-07-15" in body assert "Hashtag: #geomobLDN" in body assert "----------------------------------------" in body def test_geomob_email_single_event_no_protocol(): """Test generating email for a single new event with base URL without protocol.""" events = [GeomobEvent(date(2024, 7, 15), "/event/london-2024-07-15", "#geomobLDN")] subject, body = geomob_email(events, "thegeomob.com") assert subject == "1 New Geomob Event(s) Announced" assert "Hello," in body assert "Here are the new Geomob events:" in body assert "Date: 2024-07-15" in body assert "URL: thegeomob.com/event/london-2024-07-15" in body assert "Hashtag: #geomobLDN" in body assert "----------------------------------------" in body def test_geomob_email_multiple_events(): """Test generating email for multiple new events.""" events = [ GeomobEvent(date(2024, 7, 15), "/event/london-2024-07-15", "#geomobLDN"), GeomobEvent(date(2024, 8, 20), "/event/berlin-2024-08-20", "#geomobBER"), ] subject, body = geomob_email(events, "https://thegeomob.com") assert subject == "2 New Geomob Event(s) Announced" assert "Date: 2024-07-15" in body assert "Date: 2024-08-20" in body assert body.count("----------------------------------------") == 2 def test_geomob_email_no_double_slash(): """Test that URL construction doesn't create double slashes.""" events = [GeomobEvent(date(2024, 7, 15), "/event/london-2024-07-15", "#geomobLDN")] subject, body = geomob_email(events, "https://thegeomob.com") # This should not raise an AssertionError due to double slashes assert "https://thegeomob.com/event/london-2024-07-15" in body def test_geomob_email_empty_list(): """Test that geomob_email raises assertion error with empty list.""" with pytest.raises(AssertionError): geomob_email([], "https://thegeomob.com") def test_geomob_email_detects_double_slash_in_path(): """Test that the function detects double slashes in the URL path.""" events = [GeomobEvent(date(2024, 7, 15), "//event/london-2024-07-15", "#geomobLDN")] # This should raise an AssertionError due to double slash in path with pytest.raises(AssertionError, match="Double slash found in URL"): geomob_email(events, "https://thegeomob.com") @patch("agenda.utils.get_most_recent_file") @patch("lxml.html.parse") def test_get_cached_upcoming_events_list_with_file(mock_parse, mock_get_file): """Test getting cached events when file exists.""" mock_get_file.return_value = "/path/to/recent.html" # Mock the HTML parsing mock_tree = Mock() mock_parse.return_value.getroot.return_value = mock_tree # Mock extract_events by patching it with patch("agenda.geomob.extract_events") as mock_extract: mock_events = [ GeomobEvent(date(2024, 7, 15), "/event/london-2024-07-15", "#geomobLDN") ] mock_extract.return_value = mock_events result = get_cached_upcoming_events_list("/geomob/dir") assert result == mock_events mock_get_file.assert_called_once_with("/geomob/dir", "html") mock_parse.assert_called_once_with("/path/to/recent.html") mock_extract.assert_called_once_with(mock_tree) @patch("agenda.utils.get_most_recent_file") def test_get_cached_upcoming_events_list_no_file(mock_get_file): """Test getting cached events when no file exists.""" mock_get_file.return_value = None result = get_cached_upcoming_events_list("/geomob/dir") assert result == [] mock_get_file.assert_called_once_with("/geomob/dir", "html") @patch("agenda.mail.send_mail") @patch("requests.get") @patch("agenda.geomob.get_cached_upcoming_events_list") @patch("os.path.join") @patch("builtins.open", new_callable=mock_open) @patch("agenda.geomob.datetime") def test_update_with_new_events( mock_datetime, mock_open_file, mock_join, mock_get_cached, mock_requests, mock_send_mail, ): """Test update function when there are new events.""" # Mock config config = {"DATA_DIR": "/data"} # Mock datetime mock_now = Mock() mock_now.strftime.return_value = "2024-07-15_12:00:00" mock_datetime.now.return_value = mock_now # Mock os.path.join mock_join.return_value = "/data/geomob/2024-07-15_12:00:00.html" # Mock previous events prev_events = [ GeomobEvent(date(2024, 7, 15), "/event/london-2024-07-15", "#geomobLDN") ] mock_get_cached.return_value = prev_events # Mock HTTP response mock_response = Mock() mock_response.text = "mock content" mock_response.content = b"mock content" mock_requests.return_value = mock_response # Mock current events (with one new event) with patch("agenda.geomob.extract_events") as mock_extract: cur_events = [ GeomobEvent(date(2024, 7, 15), "/event/london-2024-07-15", "#geomobLDN"), GeomobEvent(date(2024, 8, 20), "/event/berlin-2024-08-20", "#geomobBER"), ] mock_extract.return_value = cur_events update(config) # Verify file was written mock_open_file.assert_called_once_with( "/data/geomob/2024-07-15_12:00:00.html", "w" ) mock_open_file().write.assert_called_once_with("mock content") # Verify email was sent mock_send_mail.assert_called_once() args = mock_send_mail.call_args[0] assert args[1] == "1 New Geomob Event(s) Announced" # subject @patch("requests.get") @patch("agenda.geomob.get_cached_upcoming_events_list") def test_update_no_changes(mock_get_cached, mock_requests): """Test update function when there are no changes.""" config = {"DATA_DIR": "/data"} # Mock identical events events = [GeomobEvent(date(2024, 7, 15), "/event/london-2024-07-15", "#geomobLDN")] mock_get_cached.return_value = events # Mock HTTP response mock_response = Mock() mock_response.content = b"mock content" mock_requests.return_value = mock_response with patch("agenda.geomob.extract_events") as mock_extract: mock_extract.return_value = events # Same events with patch("builtins.open") as mock_open_file: with patch("agenda.mail.send_mail") as mock_send_mail: update(config) # Verify no file was written and no email sent mock_open_file.assert_not_called() mock_send_mail.assert_not_called() @patch("agenda.mail.send_mail") @patch("requests.get") @patch("agenda.geomob.get_cached_upcoming_events_list") @patch("os.path.join") @patch("builtins.open", new_callable=mock_open) @patch("agenda.geomob.datetime") def test_update_events_changed_but_no_new( mock_datetime, mock_open_file, mock_join, mock_get_cached, mock_requests, mock_send_mail, ): """Test update function when events changed but no new ones added.""" config = {"DATA_DIR": "/data"} # Mock datetime mock_now = Mock() mock_now.strftime.return_value = "2024-07-15_12:00:00" mock_datetime.now.return_value = mock_now # Mock os.path.join mock_join.return_value = "/data/geomob/2024-07-15_12:00:00.html" # Mock previous events prev_events = [ GeomobEvent(date(2024, 7, 15), "/event/london-2024-07-15", "#geomobLDN"), GeomobEvent(date(2024, 8, 20), "/event/berlin-2024-08-20", "#geomobBER"), ] mock_get_cached.return_value = prev_events # Mock HTTP response mock_response = Mock() mock_response.text = "mock content" mock_response.content = b"mock content" mock_requests.return_value = mock_response # Mock current events (one event removed, but no new ones) with patch("agenda.geomob.extract_events") as mock_extract: cur_events = [ GeomobEvent(date(2024, 7, 15), "/event/london-2024-07-15", "#geomobLDN") ] mock_extract.return_value = cur_events update(config) # Verify file was written (events changed) mock_open_file.assert_called_once() # Verify no email was sent (no new events) mock_send_mail.assert_not_called()