test: improve email processing coverage

This commit is contained in:
Leon
2025-07-17 14:40:01 +02:00
parent 19426e3108
commit 9d982314d8
2 changed files with 75 additions and 49 deletions

View File

@@ -205,7 +205,10 @@ def test_update_newsletter(db_session: Session):
"""Test updating a newsletter.""" """Test updating a newsletter."""
unique_email = f"sender_{uuid.uuid4()}@test.com" unique_email = f"sender_{uuid.uuid4()}@test.com"
newsletter_data = NewsletterCreate( newsletter_data = NewsletterCreate(
name="Newsletter to Update", sender_emails=[unique_email] name="Newsletter to Update",
sender_emails=[unique_email],
move_to_folder="OldFolder",
extract_content=False,
) )
newsletter = create_newsletter(db_session, newsletter_data) newsletter = create_newsletter(db_session, newsletter_data)
@@ -213,7 +216,10 @@ def test_update_newsletter(db_session: Session):
updated_email = f"updated_sender_{uuid.uuid4()}@test.com" updated_email = f"updated_sender_{uuid.uuid4()}@test.com"
updated_newsletter_data = NewsletterUpdate( updated_newsletter_data = NewsletterUpdate(
name="Updated Newsletter", sender_emails=[updated_email] name="Updated Newsletter",
sender_emails=[updated_email],
move_to_folder="NewFolder",
extract_content=True,
) )
from app.crud.newsletters import update_newsletter from app.crud.newsletters import update_newsletter
@@ -224,6 +230,8 @@ def test_update_newsletter(db_session: Session):
assert updated_newsletter.name == "Updated Newsletter" assert updated_newsletter.name == "Updated Newsletter"
assert len(updated_newsletter.senders) == 1 assert len(updated_newsletter.senders) == 1
assert updated_newsletter.senders[0].email == updated_email assert updated_newsletter.senders[0].email == updated_email
assert updated_newsletter.move_to_folder == "NewFolder"
assert updated_newsletter.extract_content is True
def test_delete_newsletter(db_session: Session): def test_delete_newsletter(db_session: Session):

View File

@@ -1,97 +1,115 @@
import imaplib import imaplib
from email.message import Message from email.message import Message
from unittest.mock import MagicMock from unittest.mock import MagicMock, patch
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
from app.crud.newsletters import create_newsletter from app.crud.newsletters import create_newsletter
from app.crud.settings import create_or_update_settings from app.crud.settings import create_or_update_settings
from app.models.newsletters import Newsletter
from app.schemas.newsletters import NewsletterCreate from app.schemas.newsletters import NewsletterCreate
from app.schemas.settings import SettingsCreate from app.schemas.settings import SettingsCreate
from app.services.email_processor import _process_single_email from app.services.email_processor import _process_single_email
def _setup_test_email_processing(
db_session: Session,
newsletter_create_data: NewsletterCreate,
settings_create_data: SettingsCreate,
) -> tuple[MagicMock, Newsletter, SettingsCreate]:
"""Help to set up mocks and data for email processing tests."""
settings = create_or_update_settings(db_session, settings_create_data)
newsletter = create_newsletter(db_session, newsletter_create_data)
mock_mail = MagicMock(spec=imaplib.IMAP4_SSL)
msg = Message()
msg["From"] = newsletter_create_data.sender_emails[0]
msg["Subject"] = "Test Email"
msg["Message-ID"] = "<test-message-id>"
mock_mail.fetch.return_value = ("OK", [(b"1 (RFC822)", msg.as_bytes())])
return mock_mail, newsletter, settings
def test_process_single_email_with_newsletter_move_folder(db_session: Session): def test_process_single_email_with_newsletter_move_folder(db_session: Session):
"""Test that the per-newsletter move_to_folder is used when set, overriding the global setting.""" """Test that the per-newsletter move_to_folder is used, overriding the global setting."""
# 1. ARRANGE # 1. ARRANGE
# Global settings with a move folder
settings_data = SettingsCreate( settings_data = SettingsCreate(
imap_server="test.com", imap_server="test.com",
imap_username="test", imap_username="test",
imap_password="password", imap_password="password",
move_to_folder="GlobalArchive", move_to_folder="GlobalArchive",
) )
settings = create_or_update_settings(db_session, settings_data)
# Newsletter with a specific move folder
newsletter_data = NewsletterCreate( newsletter_data = NewsletterCreate(
name="Test Newsletter", name="Test Newsletter",
sender_emails=["test@example.com"], sender_emails=["test@example.com"],
move_to_folder="NewsletterArchive",
) )
newsletter = create_newsletter(db_session, newsletter_data) mock_mail, newsletter, settings = _setup_test_email_processing(
newsletter.move_to_folder = "NewsletterArchive" db_session, newsletter_data, settings_data
db_session.commit() )
sender_map = {newsletter.senders[0].email: newsletter}
# Mock IMAP mail object
mock_mail = MagicMock(spec=imaplib.IMAP4_SSL)
# Mock email message
msg = Message()
msg["From"] = "test@example.com"
msg["Subject"] = "Test Email"
msg["Message-ID"] = "<test-message-id>"
# Mock mail.fetch to return the message
mock_mail.fetch.return_value = ("OK", [(b"1 (RFC822)", msg.as_bytes())])
sender_map = {"test@example.com": newsletter}
# 2. ACT # 2. ACT
_process_single_email("1", mock_mail, db_session, sender_map, settings) _process_single_email("1", mock_mail, db_session, sender_map, settings)
# 3. ASSERT # 3. ASSERT
# Check that the email was moved to the newsletter-specific folder
mock_mail.copy.assert_called_once_with("1", "NewsletterArchive") mock_mail.copy.assert_called_once_with("1", "NewsletterArchive")
mock_mail.store.assert_any_call("1", "+FLAGS", "\\Deleted") mock_mail.store.assert_any_call("1", "+FLAGS", "\\Deleted")
def test_process_single_email_with_global_move_folder(db_session: Session): def test_process_single_email_with_global_move_folder(db_session: Session):
"""Test that the global move_to_folder is used when the per-newsletter setting is not set.""" """Test that the global move_to_folder is used when the per-newsletter one is not set."""
# 1. ARRANGE # 1. ARRANGE
# Global settings with a move folder
settings_data = SettingsCreate( settings_data = SettingsCreate(
imap_server="test.com", imap_server="test.com",
imap_username="test", imap_username="test",
imap_password="password", imap_password="password",
move_to_folder="GlobalArchive", move_to_folder="GlobalArchive",
) )
settings = create_or_update_settings(db_session, settings_data)
# Newsletter without a specific move folder
newsletter_data = NewsletterCreate( newsletter_data = NewsletterCreate(
name="Test Newsletter", name="Test Newsletter", sender_emails=["test@example.com"]
sender_emails=["test@example.com"],
) )
newsletter = create_newsletter(db_session, newsletter_data) mock_mail, newsletter, settings = _setup_test_email_processing(
db_session, newsletter_data, settings_data
# Mock IMAP mail object )
mock_mail = MagicMock(spec=imaplib.IMAP4_SSL) sender_map = {newsletter.senders[0].email: newsletter}
# Mock email message
msg = Message()
msg["From"] = "test@example.com"
msg["Subject"] = "Test Email"
msg["Message-ID"] = "<test-message-id-2>"
# Mock mail.fetch to return the message
mock_mail.fetch.return_value = ("OK", [(b"1 (RFC822)", msg.as_bytes())])
sender_map = {"test@example.com": newsletter}
# 2. ACT # 2. ACT
_process_single_email("1", mock_mail, db_session, sender_map, settings) _process_single_email("1", mock_mail, db_session, sender_map, settings)
# 3. ASSERT # 3. ASSERT
# Check that the email was moved to the global folder
mock_mail.copy.assert_called_once_with("1", "GlobalArchive") mock_mail.copy.assert_called_once_with("1", "GlobalArchive")
mock_mail.store.assert_any_call("1", "+FLAGS", "\\Deleted") mock_mail.store.assert_any_call("1", "+FLAGS", "\\Deleted")
@patch("app.services.email_processor.trafilatura.extract")
def test_process_single_email_with_content_extraction(
mock_trafilatura, db_session: Session
):
"""Test that trafilatura is called when extract_content is True."""
# 1. ARRANGE
mock_trafilatura.return_value = "Extracted Body"
settings_data = SettingsCreate(
imap_server="test.com", imap_username="test", imap_password="password"
)
newsletter_data = NewsletterCreate(
name="Test Newsletter",
sender_emails=["test@example.com"],
extract_content=True,
)
mock_mail, newsletter, settings = _setup_test_email_processing(
db_session, newsletter_data, settings_data
)
sender_map = {newsletter.senders[0].email: newsletter}
# 2. ACT
with patch("app.services.email_processor.create_entry") as mock_create_entry:
_process_single_email("1", mock_mail, db_session, sender_map, settings)
# 3. ASSERT
mock_trafilatura.assert_called_once()
# Check that create_entry was called with the extracted body
mock_create_entry.assert_called_once()
entry_create_arg = mock_create_entry.call_args[0][1]
assert entry_create_arg.body == "Extracted Body"