diff --git a/backend/app/core/config.py b/backend/app/core/config.py index 6862aac..18d5afa 100644 --- a/backend/app/core/config.py +++ b/backend/app/core/config.py @@ -40,6 +40,12 @@ class Settings(BaseSettings): ) algorithm: str = "HS256" access_token_expire_minutes: int = 30 + master_feed_limit: int = Field( + 100, + validation_alias=AliasChoices( + "MASTER_FEED_LIMIT", "LETTERFEED_MASTER_FEED_LIMIT" + ), + ) settings = Settings() diff --git a/backend/app/services/feed_generator.py b/backend/app/services/feed_generator.py index de036a9..f1ffa2a 100644 --- a/backend/app/services/feed_generator.py +++ b/backend/app/services/feed_generator.py @@ -77,7 +77,7 @@ def generate_feed(db: Session, feed_identifier: str): def generate_master_feed(db: Session): """Generate a master Atom feed for all newsletters.""" - entries = get_all_entries(db) + entries = get_all_entries(db, limit=settings.master_feed_limit) feed_url = f"{settings.app_base_url}/feeds/all" diff --git a/backend/app/tests/services/test_feed_generator.py b/backend/app/tests/services/test_feed_generator.py new file mode 100644 index 0000000..f5f9abc --- /dev/null +++ b/backend/app/tests/services/test_feed_generator.py @@ -0,0 +1,49 @@ +from unittest.mock import MagicMock, patch + +import pytest +from feedgen.feed import FeedGenerator + +from app.services.feed_generator import generate_master_feed + + +@pytest.fixture +def mock_db_session(): + """Fixture for a mock database session.""" + return MagicMock() + + +@patch("app.services.feed_generator.settings") +def test_generate_master_feed_respects_limit(mock_settings, mock_db_session): + """Test that the master feed generation respects the MASTER_FEED_LIMIT.""" + # Arrange + limit = 5 + mock_settings.master_feed_limit = limit + + # Create more mock entries than the limit + mock_entries = [MagicMock() for _ in range(limit + 5)] + + with ( + patch( + "app.services.feed_generator.get_all_entries", return_value=mock_entries + ) as mock_get_all_entries, + patch( + "app.services.feed_generator._create_feed_generator" + ) as mock_create_feed_generator, + patch( + "app.services.feed_generator._add_entries_to_feed" + ) as mock_add_entries_to_feed, + ): + mock_fg = MagicMock(spec=FeedGenerator) + mock_create_feed_generator.return_value = mock_fg + mock_fg.atom_str.return_value = "fake_atom_string" + + # Act + result = generate_master_feed(mock_db_session) + + # Assert + mock_get_all_entries.assert_called_once_with(mock_db_session, limit=limit) + mock_create_feed_generator.assert_called_once() + mock_add_entries_to_feed.assert_called_once_with( + mock_fg, mock_entries, is_master_feed=True + ) + assert result == "fake_atom_string" diff --git a/backend/pyproject.toml b/backend/pyproject.toml index d2d4762..5bbf51f 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -50,6 +50,9 @@ lint.ignore = [ [tool.ruff.lint.pydocstyle] convention = "google" +[tool.ruff.lint.isort] +known-first-party = ["app"] + [tool.mypy] python_executable=".venv/bin/python"