mirror of
https://github.com/khoaliber/LetterFeed.git
synced 2026-03-02 21:19:13 +00:00
v0.1.0
This commit is contained in:
1
backend/app/tests/__init__.py
Normal file
1
backend/app/tests/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
"""Unit tests for the application."""
|
||||
65
backend/app/tests/conftest.py
Normal file
65
backend/app/tests/conftest.py
Normal file
@@ -0,0 +1,65 @@
|
||||
import os
|
||||
|
||||
os.environ["LETTERFEED_DATABASE_URL"] = "sqlite:///./test.db"
|
||||
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
|
||||
from app.core.database import Base, engine, get_db
|
||||
from app.main import app
|
||||
|
||||
TestingSessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
||||
|
||||
|
||||
@pytest.fixture(scope="function", autouse=True)
|
||||
def setup_and_teardown_db():
|
||||
"""Set up and tear down the database for each test.
|
||||
|
||||
This fixture is automatically used for all tests. It creates all tables
|
||||
before a test and drops them afterwards. This ensures a clean database for
|
||||
every test.
|
||||
"""
|
||||
Base.metadata.create_all(bind=engine)
|
||||
yield
|
||||
Base.metadata.drop_all(bind=engine)
|
||||
|
||||
|
||||
@pytest.fixture(name="db_session")
|
||||
def db_session_fixture():
|
||||
"""Yield a database session for a test."""
|
||||
db = TestingSessionLocal()
|
||||
try:
|
||||
yield db
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
@pytest.fixture(name="client")
|
||||
def client_fixture(db_session):
|
||||
"""Yield a TestClient for a test."""
|
||||
|
||||
def override_get_db():
|
||||
yield db_session
|
||||
|
||||
app.dependency_overrides[get_db] = override_get_db
|
||||
with TestClient(app) as client:
|
||||
yield client
|
||||
app.dependency_overrides.clear()
|
||||
|
||||
|
||||
@pytest.fixture(scope="session", autouse=True)
|
||||
def cleanup_test_db(request):
|
||||
"""Clean up the test database after the test session.
|
||||
|
||||
This fixture is automatically used once per test session. It registers a
|
||||
finalizer to remove the test database file after all tests have run.
|
||||
"""
|
||||
|
||||
def remove_test_db():
|
||||
# The path is relative to the backend directory where pytest is run
|
||||
db_file = "test.db"
|
||||
if os.path.exists(db_file):
|
||||
os.remove(db_file)
|
||||
|
||||
request.addfinalizer(remove_test_db)
|
||||
201
backend/app/tests/test_core.py
Normal file
201
backend/app/tests/test_core.py
Normal file
@@ -0,0 +1,201 @@
|
||||
from unittest.mock import ANY, MagicMock, patch
|
||||
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.core.imap import _test_imap_connection, get_folders
|
||||
from app.crud.newsletters import create_newsletter
|
||||
from app.crud.settings import create_or_update_settings
|
||||
from app.schemas.newsletters import NewsletterCreate
|
||||
from app.schemas.settings import SettingsCreate
|
||||
from app.services.email_processor import process_emails
|
||||
|
||||
|
||||
@patch("app.core.imap.imaplib.IMAP4_SSL")
|
||||
def test_test_imap_connection_success(mock_imap):
|
||||
"""Test IMAP connection success."""
|
||||
mock_imap.return_value.login.return_value = (None, None)
|
||||
mock_imap.return_value.logout.return_value = (None, None)
|
||||
success, message = _test_imap_connection("imap.test.com", "user", "pass")
|
||||
assert success
|
||||
assert message == "Connection successful"
|
||||
|
||||
|
||||
@patch("app.core.imap.imaplib.IMAP4_SSL")
|
||||
def test_test_imap_connection_failure(mock_imap):
|
||||
"""Test IMAP connection failure."""
|
||||
mock_imap.return_value.login.side_effect = Exception("Auth failed")
|
||||
success, message = _test_imap_connection("imap.test.com", "user", "pass")
|
||||
assert not success
|
||||
assert message == "Auth failed"
|
||||
|
||||
|
||||
@patch("app.core.imap.imaplib.IMAP4_SSL")
|
||||
def test_get_folders(mock_imap):
|
||||
"""Test fetching IMAP folders."""
|
||||
mock_imap.return_value.login.return_value = (None, None)
|
||||
mock_imap.return_value.logout.return_value = (None, None)
|
||||
mock_imap.return_value.list.return_value = (
|
||||
"OK",
|
||||
[b'(NOCONNECT NOSELECT) "/" "INBOX"', b'(NOCONNECT NOSELECT) "/" "Processed"'],
|
||||
)
|
||||
folders = get_folders("imap.test.com", "user", "pass")
|
||||
assert folders == ["INBOX", "Processed"]
|
||||
|
||||
|
||||
@patch("app.services.email_processor.imaplib.IMAP4_SSL")
|
||||
def test_process_emails(mock_imap, db_session: Session):
|
||||
"""Test processing emails."""
|
||||
# Setup settings
|
||||
settings_data = SettingsCreate(
|
||||
imap_server="imap.test.com",
|
||||
imap_username="test@test.com",
|
||||
imap_password="password",
|
||||
search_folder="INBOX",
|
||||
move_to_folder="Processed",
|
||||
mark_as_read=True,
|
||||
)
|
||||
create_or_update_settings(db_session, settings_data)
|
||||
|
||||
# Setup newsletter
|
||||
newsletter_data = NewsletterCreate(
|
||||
name="Test Newsletter", sender_emails=["newsletter@example.com"]
|
||||
)
|
||||
create_newsletter(db_session, newsletter_data)
|
||||
|
||||
# Mock IMAP connection and email fetching
|
||||
mock_mail = MagicMock()
|
||||
mock_imap.return_value = mock_mail
|
||||
mock_mail.login.return_value = (None, None)
|
||||
mock_mail.select.return_value = (None, None)
|
||||
mock_mail.search.return_value = ("OK", [b"1"])
|
||||
|
||||
# Mock email content
|
||||
mock_msg_bytes = b"From: newsletter@example.com\nSubject: Test Subject\n\nTest Body"
|
||||
mock_mail.fetch.return_value = ("OK", [(None, mock_msg_bytes)])
|
||||
|
||||
process_emails(db_session)
|
||||
|
||||
# Assertions
|
||||
mock_mail.login.assert_called_once_with("test@test.com", "password")
|
||||
mock_mail.select.assert_called_once_with("INBOX")
|
||||
mock_mail.search.assert_called_once_with(None, "(UNSEEN)")
|
||||
mock_mail.fetch.assert_called_once_with(b"1", "(RFC822)")
|
||||
mock_mail.store.assert_any_call(b"1", "+FLAGS", "\\Seen")
|
||||
mock_mail.copy.assert_called_once_with(b"1", "Processed")
|
||||
mock_mail.store.assert_any_call(b"1", "+FLAGS", "\\Deleted")
|
||||
mock_mail.expunge.assert_called_once()
|
||||
mock_mail.logout.assert_called_once()
|
||||
|
||||
# Verify entry in DB
|
||||
from app.crud.entries import get_entries_by_newsletter
|
||||
from app.crud.newsletters import get_newsletters
|
||||
|
||||
newsletters = get_newsletters(db_session)
|
||||
assert len(newsletters) == 1
|
||||
entries = get_entries_by_newsletter(db_session, newsletters[0].id)
|
||||
assert len(entries) == 1
|
||||
assert entries[0].subject == "Test Subject"
|
||||
assert entries[0].body == "Test Body"
|
||||
|
||||
|
||||
@patch("app.core.scheduler.job")
|
||||
@patch("app.core.scheduler.SessionLocal")
|
||||
@patch("app.core.scheduler.scheduler")
|
||||
def test_start_scheduler_with_interval(
|
||||
mock_scheduler, mock_session_local, mock_job, db_session: Session
|
||||
):
|
||||
"""Test starting the scheduler with an interval."""
|
||||
mock_session_local.return_value = db_session
|
||||
mock_scheduler.running = False
|
||||
settings_data = SettingsCreate(
|
||||
imap_server="imap.test.com",
|
||||
imap_username="test@test.com",
|
||||
imap_password="password",
|
||||
email_check_interval=30,
|
||||
)
|
||||
create_or_update_settings(db_session, settings_data)
|
||||
|
||||
from app.core.scheduler import start_scheduler_with_interval
|
||||
|
||||
start_scheduler_with_interval()
|
||||
|
||||
mock_scheduler.add_job.assert_called_with(
|
||||
ANY, "interval", minutes=30, id="email_check_job", replace_existing=True
|
||||
)
|
||||
mock_scheduler.start.assert_called_once()
|
||||
mock_job.assert_called_once()
|
||||
|
||||
|
||||
@patch("app.core.scheduler.SessionLocal")
|
||||
@patch("app.core.scheduler.process_emails")
|
||||
def test_scheduler_job(mock_process_emails, mock_session_local, db_session: Session):
|
||||
"""Test the scheduler job."""
|
||||
mock_session_local.return_value = db_session
|
||||
from app.core.scheduler import job
|
||||
|
||||
job()
|
||||
mock_process_emails.assert_called_once_with(db_session)
|
||||
|
||||
|
||||
@patch("app.services.email_processor.imaplib.IMAP4_SSL")
|
||||
def test_process_emails_auto_add_sender(mock_imap, db_session: Session):
|
||||
"""Test processing emails with auto add sender enabled."""
|
||||
settings_data = SettingsCreate(
|
||||
imap_server="imap.test.com",
|
||||
imap_username="test@test.com",
|
||||
imap_password="password",
|
||||
auto_add_new_senders=True,
|
||||
)
|
||||
create_or_update_settings(db_session, settings_data)
|
||||
|
||||
mock_mail = MagicMock()
|
||||
mock_imap.return_value = mock_mail
|
||||
mock_mail.search.return_value = ("OK", [b"1"])
|
||||
mock_msg_bytes = b"From: New Sender <new@example.com>\nSubject: New Email\n\nHello"
|
||||
mock_mail.fetch.return_value = ("OK", [(None, mock_msg_bytes)])
|
||||
|
||||
process_emails(db_session)
|
||||
|
||||
from app.crud.newsletters import get_newsletters
|
||||
|
||||
newsletters = get_newsletters(db_session)
|
||||
assert len(newsletters) == 1
|
||||
assert len(newsletters[0].senders) == 1
|
||||
assert newsletters[0].senders[0].email == "new@example.com"
|
||||
assert newsletters[0].name == "New Sender"
|
||||
|
||||
|
||||
@patch("app.services.email_processor.imaplib.IMAP4_SSL")
|
||||
def test_process_emails_no_settings(mock_imap, db_session: Session):
|
||||
"""Test processing emails with no settings in the database."""
|
||||
# No settings in the DB
|
||||
process_emails(db_session)
|
||||
mock_imap.assert_not_called()
|
||||
|
||||
|
||||
@patch("app.services.email_processor.imaplib.IMAP4_SSL")
|
||||
def test_process_emails_no_move_or_read(mock_imap, db_session: Session):
|
||||
"""Test processing emails with no move or read."""
|
||||
settings_data = SettingsCreate(
|
||||
imap_server="imap.test.com",
|
||||
imap_username="test@test.com",
|
||||
imap_password="password",
|
||||
mark_as_read=False,
|
||||
move_to_folder=None,
|
||||
)
|
||||
create_or_update_settings(db_session, settings_data)
|
||||
create_newsletter(
|
||||
db_session,
|
||||
NewsletterCreate(name="Test", sender_emails=["newsletter@example.com"]),
|
||||
)
|
||||
|
||||
mock_mail = MagicMock()
|
||||
mock_imap.return_value = mock_mail
|
||||
mock_mail.search.return_value = ("OK", [b"1"])
|
||||
mock_msg_bytes = b"From: newsletter@example.com\nSubject: Test Subject\n\nTest Body"
|
||||
mock_mail.fetch.return_value = ("OK", [(None, mock_msg_bytes)])
|
||||
|
||||
process_emails(db_session)
|
||||
|
||||
mock_mail.store.assert_not_called()
|
||||
mock_mail.copy.assert_not_called()
|
||||
172
backend/app/tests/test_crud.py
Normal file
172
backend/app/tests/test_crud.py
Normal file
@@ -0,0 +1,172 @@
|
||||
import uuid
|
||||
from unittest.mock import patch
|
||||
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.crud.entries import create_entry, get_entries_by_newsletter
|
||||
from app.crud.newsletters import create_newsletter, get_newsletter, get_newsletters
|
||||
from app.crud.settings import create_or_update_settings, get_settings
|
||||
from app.schemas.entries import EntryCreate
|
||||
from app.schemas.newsletters import NewsletterCreate
|
||||
from app.schemas.settings import SettingsCreate
|
||||
|
||||
|
||||
def test_create_or_update_settings(db_session: Session):
|
||||
"""Test creating or updating settings."""
|
||||
settings_data = SettingsCreate(
|
||||
imap_server="imap.test.com",
|
||||
imap_username="test@test.com",
|
||||
imap_password="password",
|
||||
search_folder="INBOX",
|
||||
move_to_folder="Archive",
|
||||
mark_as_read=True,
|
||||
)
|
||||
settings = create_or_update_settings(db_session, settings_data)
|
||||
assert settings.imap_server == "imap.test.com"
|
||||
assert settings.mark_as_read
|
||||
|
||||
updated_settings_data = SettingsCreate(
|
||||
imap_server="imap.updated.com",
|
||||
imap_username="updated@test.com",
|
||||
imap_password="new_password",
|
||||
search_folder="Inbox",
|
||||
move_to_folder=None,
|
||||
mark_as_read=False,
|
||||
)
|
||||
updated_settings = create_or_update_settings(db_session, updated_settings_data)
|
||||
assert updated_settings.imap_server == "imap.updated.com"
|
||||
assert not updated_settings.mark_as_read
|
||||
assert updated_settings.move_to_folder is None
|
||||
|
||||
|
||||
def test_get_settings(db_session: Session):
|
||||
"""Test getting settings."""
|
||||
settings_data = SettingsCreate(
|
||||
imap_server="imap.get.com",
|
||||
imap_username="get@test.com",
|
||||
imap_password="get_password",
|
||||
search_folder="INBOX",
|
||||
move_to_folder=None,
|
||||
mark_as_read=False,
|
||||
)
|
||||
create_or_update_settings(db_session, settings_data)
|
||||
settings = get_settings(db_session)
|
||||
assert settings.imap_server == "imap.get.com"
|
||||
|
||||
|
||||
def test_get_settings_with_env_override(db_session: Session):
|
||||
"""Test getting settings with environment variable override."""
|
||||
# 1. Create initial settings in the database
|
||||
db_settings_data = SettingsCreate(
|
||||
imap_server="db.imap.com",
|
||||
imap_username="db_user",
|
||||
imap_password="db_pass",
|
||||
email_check_interval=15,
|
||||
)
|
||||
create_or_update_settings(db_session, db_settings_data)
|
||||
|
||||
# 2. Patch the env_settings to simulate environment variables
|
||||
with patch("app.crud.settings.env_settings") as mock_env_settings:
|
||||
mock_env_settings.model_dump.return_value = {
|
||||
"imap_server": "env.imap.com",
|
||||
"imap_username": "env_user",
|
||||
"imap_password": "env_pass",
|
||||
"email_check_interval": 30,
|
||||
}
|
||||
mock_env_settings.imap_password = "env_pass"
|
||||
|
||||
# 3. Call get_settings and assert the override
|
||||
settings = get_settings(db_session, with_password=True)
|
||||
assert settings.imap_server == "env.imap.com"
|
||||
assert settings.imap_username == "env_user"
|
||||
assert settings.imap_password == "env_pass"
|
||||
assert settings.email_check_interval == 30
|
||||
assert "imap_server" in settings.locked_fields
|
||||
assert "imap_username" in settings.locked_fields
|
||||
|
||||
# 4. Call create_or_update_settings and assert that locked fields are not updated
|
||||
update_data = SettingsCreate(
|
||||
imap_server="new.imap.com",
|
||||
imap_username="new_user",
|
||||
imap_password="new_pass",
|
||||
email_check_interval=45,
|
||||
)
|
||||
updated_settings = create_or_update_settings(db_session, update_data)
|
||||
assert updated_settings.imap_server == "env.imap.com" # Should not change
|
||||
assert updated_settings.imap_username == "env_user" # Should not change
|
||||
assert updated_settings.email_check_interval == 30 # Should not change
|
||||
|
||||
|
||||
def test_create_newsletter(db_session: Session):
|
||||
"""Test creating a newsletter."""
|
||||
unique_email = f"sender_{uuid.uuid4()}@test.com"
|
||||
newsletter_data = NewsletterCreate(
|
||||
name="Test Newsletter 1", sender_emails=[unique_email]
|
||||
)
|
||||
newsletter = create_newsletter(db_session, newsletter_data)
|
||||
assert newsletter.name == "Test Newsletter 1"
|
||||
assert newsletter.is_active
|
||||
assert len(newsletter.senders) == 1
|
||||
assert newsletter.senders[0].email == unique_email
|
||||
|
||||
|
||||
def test_get_newsletter(db_session: Session):
|
||||
"""Test getting a single newsletter."""
|
||||
unique_email = f"sender_{uuid.uuid4()}@test.com"
|
||||
newsletter_data = NewsletterCreate(
|
||||
name="Test Newsletter 2", sender_emails=[unique_email]
|
||||
)
|
||||
created_newsletter = create_newsletter(db_session, newsletter_data)
|
||||
newsletter = get_newsletter(db_session, created_newsletter.id)
|
||||
assert newsletter.name == "Test Newsletter 2"
|
||||
assert len(newsletter.senders) == 1
|
||||
assert newsletter.senders[0].email == unique_email
|
||||
|
||||
|
||||
def test_get_newsletters(db_session: Session):
|
||||
"""Test getting multiple newsletters."""
|
||||
create_newsletter(
|
||||
db_session,
|
||||
NewsletterCreate(
|
||||
name="Test Newsletter 3", sender_emails=[f"sender_{uuid.uuid4()}@test.com"]
|
||||
),
|
||||
)
|
||||
create_newsletter(
|
||||
db_session,
|
||||
NewsletterCreate(
|
||||
name="Test Newsletter 4", sender_emails=[f"sender_{uuid.uuid4()}@test.com"]
|
||||
),
|
||||
)
|
||||
newsletters = get_newsletters(db_session)
|
||||
assert len(newsletters) >= 2
|
||||
|
||||
|
||||
def test_create_entry(db_session: Session):
|
||||
"""Test creating a newsletter entry."""
|
||||
unique_email = f"sender_{uuid.uuid4()}@test.com"
|
||||
newsletter_data = NewsletterCreate(
|
||||
name="Test Newsletter 5", sender_emails=[unique_email]
|
||||
)
|
||||
newsletter = create_newsletter(db_session, newsletter_data)
|
||||
entry_data = EntryCreate(subject="Test Subject", body="Test Body")
|
||||
entry = create_entry(db_session, entry_data, newsletter.id)
|
||||
assert entry.subject == "Test Subject"
|
||||
assert entry.newsletter_id == newsletter.id
|
||||
|
||||
|
||||
def test_get_entries_by_newsletter(db_session: Session):
|
||||
"""Test getting entries for a newsletter."""
|
||||
unique_email = f"sender_{uuid.uuid4()}@test.com"
|
||||
newsletter_data = NewsletterCreate(
|
||||
name="Test Newsletter 6", sender_emails=[unique_email]
|
||||
)
|
||||
newsletter = create_newsletter(db_session, newsletter_data)
|
||||
create_entry(
|
||||
db_session, EntryCreate(subject="Entry 1", body="Body 1"), newsletter.id
|
||||
)
|
||||
create_entry(
|
||||
db_session, EntryCreate(subject="Entry 2", body="Body 2"), newsletter.id
|
||||
)
|
||||
entries = get_entries_by_newsletter(db_session, newsletter.id)
|
||||
assert len(entries) == 2
|
||||
assert entries[0].subject == "Entry 1"
|
||||
147
backend/app/tests/test_routers.py
Normal file
147
backend/app/tests/test_routers.py
Normal file
@@ -0,0 +1,147 @@
|
||||
import uuid
|
||||
from unittest.mock import patch
|
||||
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
|
||||
def test_health_check(client: TestClient):
|
||||
"""Test the health check endpoint."""
|
||||
response = client.get("/health")
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {"status": "ok"}
|
||||
|
||||
|
||||
@patch("app.core.imap.imaplib.IMAP4_SSL")
|
||||
def test_update_imap_settings(mock_imap, client: TestClient):
|
||||
"""Test updating IMAP settings."""
|
||||
mock_imap.return_value.login.return_value = (None, None)
|
||||
mock_imap.return_value.logout.return_value = (None, None)
|
||||
|
||||
settings_data = {
|
||||
"imap_server": "imap.example.com",
|
||||
"imap_username": "test@example.com",
|
||||
"imap_password": "password",
|
||||
"search_folder": "INBOX",
|
||||
"move_to_folder": "Processed",
|
||||
"mark_as_read": True,
|
||||
}
|
||||
response = client.post("/imap/settings", json=settings_data)
|
||||
assert response.status_code == 200
|
||||
assert response.json()["imap_server"] == "imap.example.com"
|
||||
assert response.json()["imap_username"] == "test@example.com"
|
||||
assert response.json()["search_folder"] == "INBOX"
|
||||
assert response.json()["move_to_folder"] == "Processed"
|
||||
assert response.json()["mark_as_read"]
|
||||
|
||||
|
||||
@patch("app.core.imap.imaplib.IMAP4_SSL")
|
||||
def test_get_imap_settings(mock_imap, client: TestClient):
|
||||
"""Test getting IMAP settings."""
|
||||
mock_imap.return_value.login.return_value = (None, None)
|
||||
mock_imap.return_value.logout.return_value = (None, None)
|
||||
|
||||
settings_data = {
|
||||
"imap_server": "imap.example.com",
|
||||
"imap_username": "test@example.com",
|
||||
"imap_password": "password",
|
||||
"search_folder": "INBOX",
|
||||
"move_to_folder": "Processed",
|
||||
"mark_as_read": True,
|
||||
}
|
||||
client.post("/imap/settings", json=settings_data)
|
||||
|
||||
response = client.get("/imap/settings")
|
||||
assert response.status_code == 200
|
||||
assert response.json()["imap_server"] == "imap.example.com"
|
||||
assert response.json()["imap_username"] == "test@example.com"
|
||||
|
||||
|
||||
@patch("app.core.imap.imaplib.IMAP4_SSL")
|
||||
def test_test_imap_connection(mock_imap, client: TestClient):
|
||||
"""Test the IMAP connection."""
|
||||
mock_imap.return_value.login.return_value = (None, None)
|
||||
mock_imap.return_value.logout.return_value = (None, None)
|
||||
|
||||
settings_data = {
|
||||
"imap_server": "imap.example.com",
|
||||
"imap_username": "test@example.com",
|
||||
"imap_password": "password",
|
||||
"search_folder": "INBOX",
|
||||
"move_to_folder": "Processed",
|
||||
"mark_as_read": True,
|
||||
}
|
||||
client.post("/imap/settings", json=settings_data)
|
||||
|
||||
response = client.post("/imap/test")
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {"message": "Connection successful"}
|
||||
|
||||
|
||||
@patch("app.core.imap.imaplib.IMAP4_SSL")
|
||||
def test_get_imap_folders(mock_imap, client: TestClient):
|
||||
"""Test getting IMAP folders."""
|
||||
mock_imap.return_value.login.return_value = (None, None)
|
||||
mock_imap.return_value.logout.return_value = (None, None)
|
||||
mock_imap.return_value.list.return_value = (
|
||||
"OK",
|
||||
[b'(NOCONNECT NOSELECT) "/" "INBOX"', b'(NOCONNECT NOSELECT) "/" "Processed"'],
|
||||
)
|
||||
|
||||
settings_data = {
|
||||
"imap_server": "imap.example.com",
|
||||
"imap_username": "test@example.com",
|
||||
"imap_password": "password",
|
||||
"search_folder": "INBOX",
|
||||
"move_to_folder": "Processed",
|
||||
"mark_as_read": True,
|
||||
}
|
||||
client.post("/imap/settings", json=settings_data)
|
||||
|
||||
response = client.get("/imap/folders")
|
||||
assert response.status_code == 200
|
||||
assert response.json() == ["INBOX", "Processed"]
|
||||
|
||||
|
||||
def test_create_newsletter(client: TestClient):
|
||||
"""Test creating a newsletter."""
|
||||
unique_email = f"newsletter_{uuid.uuid4()}@example.com"
|
||||
newsletter_data = {"name": "Example Newsletter", "sender_emails": [unique_email]}
|
||||
response = client.post("/newsletters", json=newsletter_data)
|
||||
assert response.status_code == 200
|
||||
assert response.json()["name"] == "Example Newsletter"
|
||||
assert response.json()["is_active"]
|
||||
assert len(response.json()["senders"]) == 1
|
||||
assert response.json()["senders"][0]["email"] == unique_email
|
||||
|
||||
|
||||
def test_get_newsletters(client: TestClient):
|
||||
"""Test getting all newsletters."""
|
||||
unique_email = f"newsletter_{uuid.uuid4()}@example.com"
|
||||
newsletter_data = {"name": "Another Newsletter", "sender_emails": [unique_email]}
|
||||
client.post("/newsletters", json=newsletter_data)
|
||||
|
||||
response = client.get("/newsletters")
|
||||
assert response.status_code == 200
|
||||
assert len(response.json()) >= 1
|
||||
assert any(
|
||||
unique_email in [s["email"] for s in nl["senders"]] for nl in response.json()
|
||||
)
|
||||
|
||||
|
||||
def test_get_single_newsletter(client: TestClient):
|
||||
"""Test getting a single newsletter."""
|
||||
unique_email = f"newsletter_{uuid.uuid4()}@example.com"
|
||||
newsletter_data = {"name": "Third Newsletter", "sender_emails": [unique_email]}
|
||||
create_response = client.post("/newsletters/", json=newsletter_data)
|
||||
newsletter_id = create_response.json()["id"]
|
||||
|
||||
response = client.get(f"/newsletters/{newsletter_id}")
|
||||
assert response.status_code == 200
|
||||
assert response.json()["senders"][0]["email"] == unique_email
|
||||
|
||||
|
||||
def test_get_nonexistent_newsletter(client: TestClient):
|
||||
"""Test getting a nonexistent newsletter."""
|
||||
response = client.get("/newsletters/999")
|
||||
assert response.status_code == 404
|
||||
assert response.json() == {"detail": "Newsletter not found"}
|
||||
48
backend/app/tests/test_services.py
Normal file
48
backend/app/tests/test_services.py
Normal file
@@ -0,0 +1,48 @@
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.crud.entries import create_entry
|
||||
from app.crud.newsletters import create_newsletter
|
||||
from app.schemas.entries import EntryCreate
|
||||
from app.schemas.newsletters import NewsletterCreate
|
||||
from app.services.feed_generator import generate_feed
|
||||
|
||||
|
||||
def test_generate_feed(db_session: Session):
|
||||
"""Test the feed generation for a newsletter with entries."""
|
||||
# Create a newsletter
|
||||
newsletter_data = NewsletterCreate(
|
||||
name="Feed Test Newsletter", sender_emails=["feed@example.com"]
|
||||
)
|
||||
newsletter = create_newsletter(db_session, newsletter_data)
|
||||
|
||||
# Create entries for the newsletter
|
||||
entry1_data = EntryCreate(
|
||||
subject="First Entry", body="<p>This is the first entry.</p>"
|
||||
)
|
||||
create_entry(db_session, entry1_data, newsletter.id)
|
||||
|
||||
entry2_data = EntryCreate(
|
||||
subject="Second Entry", body="<p>This is the second entry.</p>"
|
||||
)
|
||||
create_entry(db_session, entry2_data, newsletter.id)
|
||||
|
||||
# Generate the feed
|
||||
feed_xml = generate_feed(db_session, newsletter.id)
|
||||
assert feed_xml is not None
|
||||
|
||||
# Parse the feed XML to verify content (simplified check)
|
||||
# In a real scenario, you'd use an XML parser to validate structure and content more thoroughly
|
||||
assert f"<title>{newsletter.name}</title>" in feed_xml.decode()
|
||||
assert f"<id>urn:letterfeed:newsletter:{newsletter.id}</id>" in feed_xml.decode()
|
||||
assert "<title>First Entry</title>" in feed_xml.decode()
|
||||
assert "<title>Second Entry</title>" in feed_xml.decode()
|
||||
assert (
|
||||
'<content type="html"><p>This is the first entry.</p></content>'
|
||||
in feed_xml.decode()
|
||||
)
|
||||
|
||||
|
||||
def test_generate_feed_nonexistent_newsletter(db_session: Session):
|
||||
"""Test feed generation for a non-existent newsletter."""
|
||||
feed_xml = generate_feed(db_session, 999) # Non-existent newsletter ID
|
||||
assert feed_xml is None
|
||||
Reference in New Issue
Block a user