Files
ebbe.bassandClaude Sonnet 5.5 20510a73e5 feat: add scheduler and mail outbox
SMTP adapter with TLS and a fake, outbox with retries, backoff, rate limit and restart safety, scheduler tick with job-run records started by gunicorn, and a send_test_mail command.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N3t4WL7bd9bdHHNjEsxzqC
2026-10-18 09:18:42 +02:00

62 lines
2.2 KiB
Python

from pathlib import Path
from types import SimpleNamespace
import polib
import pytest
from portal.adapters import registry
from portal.adapters.directory import FakeDirectory
from portal.adapters.idp import FakeIdp
from portal.adapters.mail import FakeMailer
@pytest.fixture
def valid_env(tmp_path):
ca_file = tmp_path / "ca.pem"
ca_file.write_text("test ca")
return {
"SECRET_KEY": "x" * 48,
"ALLOWED_HOSTS": "testserver",
"DATA_DIR": str(tmp_path),
"LDAP_SERVER": "dc.example.test",
"LDAP_USER_OU_DN": "OU=Users,DC=example,DC=test",
"LDAP_BIND_DN": "svc@example.test",
"LDAP_BIND_PASSWORD": "service-password",
"LDAP_CA_FILE": str(ca_file),
"LDAP_PUBLISHER_GROUP_DN": "CN=Publishers,OU=Users,DC=example,DC=test",
"LDAP_ADMIN_GROUP_DN": "CN=Admins,OU=Users,DC=example,DC=test",
"OIDC_ISSUER": "https://idp.example.test/application/o/portal/",
"OIDC_CLIENT_ID": "portal-client",
"OIDC_CLIENT_SECRET": "client-secret",
"PUBLIC_BASE_URL": "https://portal.example.test",
"SMTP_HOST": "mail.example.test",
"SMTP_USERNAME": "portal@example.test",
"SMTP_PASSWORD": "mail-password",
"MAIL_FROM": "Portal <portal@example.test>",
}
@pytest.fixture(autouse=True)
def adapters():
"""Fakes for every adapter, so no test can reach a real directory or provider."""
fakes = SimpleNamespace(directory=FakeDirectory(), idp=FakeIdp(), mailer=FakeMailer())
registry.install(fakes.directory, fakes.idp, fakes.mailer)
yield fakes
registry.install()
@pytest.fixture(scope="session", autouse=True)
def compiled_messages():
"""The image compiles the catalogues with gettext; tests compile their own copy."""
for po_file in (Path(__file__).resolve().parent.parent / "locale").glob("*/LC_MESSAGES/*.po"):
polib.pofile(str(po_file)).save_as_mofile(str(po_file.with_suffix(".mo")))
@pytest.fixture(autouse=True)
def plain_static_files(settings):
"""No collectstatic in tests, so use storage without a manifest."""
settings.STORAGES = {
**settings.STORAGES,
"staticfiles": {"BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage"},
}