first commit
This commit is contained in:
commit
cce9a8c9ba
73 changed files with 8665 additions and 0 deletions
143
backend/tests/test_inbound_smtp.py
Normal file
143
backend/tests/test_inbound_smtp.py
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
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@getall.help\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@getall.help"],
|
||||
mail_from="sender@example.com",
|
||||
account_ids={"user@getall.help": "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@getall.help",
|
||||
[],
|
||||
)
|
||||
|
||||
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@getall.help"]
|
||||
assert target.account_ids == {"user@getall.help": "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")
|
||||
Loading…
Add table
Add a link
Reference in a new issue