agentic.email/backend/tests/test_inbound_smtp.py
2026-10-04 14:40:06 +05:30

143 lines
3.7 KiB
Python

from types import SimpleNamespace
import pytest
from bson import ObjectId
from app.services import inbound_smtp
from app.services.raw_mime_storage import StoredMime
RAW = (
b"From: Sender <sender@example.com>\r\n"
b"To: user@gentic.email\r\n"
b"Subject: Hello\r\n\r\n"
b"Retry safe body\r\n"
)
class FakeMessages:
def __init__(self):
self.id = ObjectId()
self.updates = 0
async def update_one(self, *args, **kwargs):
self.updates += 1
async def find_one(self, *args, **kwargs):
return {"_id": self.id}
class FakeEntries:
def __init__(self):
self.updates = 0
async def update_one(self, *args, **kwargs):
self.updates += 1
class FakeStorage:
async def put(self, raw, delivery_key):
return StoredMime("mail", f"raw/{delivery_key}.eml", 7, len(raw), "crc")
class FakeSession:
def __init__(self, account):
self.account = account
async def __aenter__(self):
return self
async def __aexit__(self, *args):
pass
async def scalar(self, query):
return self.account
def envelope(raw=RAW):
return SimpleNamespace(
original_content=raw,
rcpt_tos=["user@gentic.email"],
mail_from="sender@example.com",
account_ids={"user@gentic.email": "account-1"},
)
@pytest.mark.asyncio
async def test_rcpt_rejects_unknown_account(monkeypatch):
monkeypatch.setattr(inbound_smtp, "SessionFactory", lambda: FakeSession(None))
target = SimpleNamespace(rcpt_tos=[], rcpt_options=[])
response = await inbound_smtp.InboundHandler().handle_RCPT(
None,
None,
target,
"missing@gentic.email",
[],
)
assert response.startswith("550 5.1.1")
assert target.rcpt_tos == []
@pytest.mark.asyncio
async def test_rcpt_accepts_account_and_tracks_owner(monkeypatch):
account = SimpleNamespace(id="account-1")
monkeypatch.setattr(inbound_smtp, "SessionFactory", lambda: FakeSession(account))
target = SimpleNamespace(rcpt_tos=[], rcpt_options=[])
response = await inbound_smtp.InboundHandler().handle_RCPT(
None,
None,
target,
"User@GetAll.Help",
[],
)
assert response.startswith("250 2.1.5")
assert target.rcpt_tos == ["user@gentic.email"]
assert target.account_ids == {"user@gentic.email": "account-1"}
@pytest.mark.asyncio
async def test_data_returns_success_only_after_idempotent_records(monkeypatch):
messages = FakeMessages()
entries = FakeEntries()
monkeypatch.setattr(
inbound_smtp,
"mongo_db",
SimpleNamespace(messages=messages, mailbox_entries=entries),
)
monkeypatch.setattr(inbound_smtp, "get_raw_mime_storage", lambda: FakeStorage())
response = await inbound_smtp.InboundHandler().handle_DATA(None, None, envelope())
assert response.startswith("250 2.0.0")
assert messages.updates == 1
assert entries.updates == 1
@pytest.mark.asyncio
async def test_data_returns_retryable_status_on_storage_failure(monkeypatch):
class BrokenStorage:
async def put(self, raw, delivery_key):
raise TimeoutError("GCS unavailable")
monkeypatch.setattr(inbound_smtp, "get_raw_mime_storage", lambda: BrokenStorage())
response = await inbound_smtp.InboundHandler().handle_DATA(None, None, envelope())
assert response.startswith("451 4.3.0")
@pytest.mark.asyncio
async def test_data_rejects_oversized_message(monkeypatch):
monkeypatch.setattr(
inbound_smtp,
"get_settings",
lambda: SimpleNamespace(inbound_max_bytes=10),
)
response = await inbound_smtp.InboundHandler().handle_DATA(None, None, envelope())
assert response.startswith("552 5.3.4")