diff --git a/tests/test_gwr.py b/tests/test_gwr.py new file mode 100644 index 0000000..03cd342 --- /dev/null +++ b/tests/test_gwr.py @@ -0,0 +1,208 @@ +"""Tests for agenda.gwr module.""" + +import os +import tempfile +from datetime import date +from typing import Any +from unittest.mock import AsyncMock, patch + +import pytest + +from agenda.gwr import ( + advance_ticket_date, + advance_tickets_page_html, + extract_dates, + extract_weekday_date, + parse_date_string, +) + + +class TestParseDateString: + """Tests for parse_date_string function.""" + + def test_parse_date_with_year(self) -> None: + """Test parsing date string with year included.""" + result = parse_date_string("Monday 25 December 2023") + assert result == date(2023, 12, 25) + + def test_parse_date_without_year(self) -> None: + """Test parsing date string without year (should use current year).""" + current_year = date.today().year + result = parse_date_string("Monday 25 December") + assert result == date(current_year, 12, 25) + + def test_parse_different_formats(self) -> None: + """Test parsing various date formats.""" + result = parse_date_string("Friday 1 January 2024") + assert result == date(2024, 1, 1) + + result = parse_date_string("Saturday 29 February 2024") + assert result == date(2024, 2, 29) + + +class TestExtractDates: + """Tests for extract_dates function.""" + + def test_extract_valid_dates(self) -> None: + """Test extracting dates from valid HTML.""" + html = """ + WeekdaysMonday 25 December 2023 + SaturdaysSaturday 23 December 2023 + SundaysSunday 24 December 2023 + """ + result = extract_dates(html) + expected = { + "Weekdays": date(2023, 12, 25), + "Saturdays": date(2023, 12, 23), + "Sundays": date(2023, 12, 24), + } + assert result == expected + + def test_extract_dates_with_asterisks(self) -> None: + """Test extracting dates when HTML contains asterisks.""" + html = """ + WeekdaysMonday 25 December 2023** + """ + result = extract_dates(html) + expected = {"Weekdays": date(2023, 12, 25)} + assert result == expected + + def test_extract_dates_no_match(self) -> None: + """Test extracting dates when no pattern matches.""" + html = "

No relevant table data here

" + result = extract_dates(html) + assert result is None + + def test_extract_dates_whitespace_handling(self) -> None: + """Test extracting dates with various whitespace.""" + html = """ + Weekdays Monday 25 December 2023 + """ + result = extract_dates(html) + expected = {"Weekdays": date(2023, 12, 25)} + assert result == expected + + +class TestExtractWeekdayDate: + """Tests for extract_weekday_date function.""" + + def test_extract_valid_weekday_date(self) -> None: + """Test extracting weekday date from valid HTML.""" + html = """ + WeekdaysMonday 25 December 2023 + """ + result = extract_weekday_date(html) + assert result == date(2023, 12, 25) + + def test_extract_weekday_date_with_asterisks(self) -> None: + """Test extracting weekday date when HTML contains asterisks.""" + html = """ + WeekdaysMonday 25 December 2023** + """ + result = extract_weekday_date(html) + assert result == date(2023, 12, 25) + + def test_extract_weekday_date_no_match(self) -> None: + """Test extracting weekday date when no pattern matches.""" + html = "

No weekday data here

" + result = extract_weekday_date(html) + assert result is None + + def test_extract_weekday_date_multiline(self) -> None: + """Test extracting weekday date with multiline content.""" + html = """ + + Weekdays + Monday 25 December 2023 + + """ + result = extract_weekday_date(html) + assert result == date(2023, 12, 25) + + +class TestAdvanceTicketsPageHtml: + """Tests for advance_tickets_page_html function.""" + + @pytest.mark.asyncio + async def test_cache_hit(self) -> None: + """Test using cached HTML when file is fresh.""" + with tempfile.TemporaryDirectory() as temp_dir: + cache_file = os.path.join(temp_dir, "advance-tickets.html") + test_content = "test content" + + # Create a fresh cache file + with open(cache_file, "w") as f: + f.write(test_content) + + result = await advance_tickets_page_html(temp_dir, ttl=3600) + assert result == test_content + + @pytest.mark.asyncio + async def test_force_cache(self) -> None: + """Test forcing cache usage even when file doesn't exist.""" + with tempfile.TemporaryDirectory() as temp_dir: + cache_file = os.path.join(temp_dir, "advance-tickets.html") + test_content = "cached content" + + with open(cache_file, "w") as f: + f.write(test_content) + + result = await advance_tickets_page_html(temp_dir, force_cache=True) + assert result == test_content + + @pytest.mark.asyncio + @patch("httpx.AsyncClient") + async def test_fetch_from_web(self, mock_client: Any) -> None: + """Test fetching from web when cache is stale.""" + mock_response = AsyncMock() + mock_response.text = "fresh content" + mock_client.return_value.__aenter__.return_value.get.return_value = ( + mock_response + ) + + with tempfile.TemporaryDirectory() as temp_dir: + result = await advance_tickets_page_html(temp_dir, ttl=0) + assert result == "fresh content" + + # Check that content was cached + cache_file = os.path.join(temp_dir, "advance-tickets.html") + with open(cache_file) as f: + cached_content = f.read() + assert cached_content == "fresh content" + + +class TestAdvanceTicketDate: + """Tests for advance_ticket_date function.""" + + @pytest.mark.asyncio + @patch("agenda.gwr.advance_tickets_page_html") + async def test_advance_ticket_date_success(self, mock_html: Any) -> None: + """Test successfully extracting advance ticket date.""" + mock_html.return_value = """ + WeekdaysMonday 25 December 2023 + """ + + result = await advance_ticket_date("/fake/dir") + assert result == date(2023, 12, 25) + mock_html.assert_called_once_with("/fake/dir", force_cache=False) + + @pytest.mark.asyncio + @patch("agenda.gwr.advance_tickets_page_html") + async def test_advance_ticket_date_no_match(self, mock_html: Any) -> None: + """Test when no weekday date can be extracted.""" + mock_html.return_value = "

No relevant data

" + + result = await advance_ticket_date("/fake/dir") + assert result is None + + @pytest.mark.asyncio + @patch("agenda.gwr.advance_tickets_page_html") + async def test_advance_ticket_date_force_cache(self, mock_html: Any) -> None: + """Test advance_ticket_date with force_cache parameter.""" + mock_html.return_value = """ + WeekdaysTuesday 26 December 2023 + """ + + result = await advance_ticket_date("/fake/dir", force_cache=True) + assert result == date(2023, 12, 26) + mock_html.assert_called_once_with("/fake/dir", force_cache=True)