first commit

This commit is contained in:
debu-2 2026-10-03 21:14:44 +05:30
commit cce9a8c9ba
73 changed files with 8665 additions and 0 deletions

View file

@ -0,0 +1,87 @@
"""Tests for company shared-MX DNS onboarding."""
from unittest.mock import patch
from app.services.dns_verification import verify_company_dns
from app.services.domain_provisioning import (
build_company_dns_records,
build_shared_mx_spf_value,
register_relay_domain,
)
def test_build_company_dns_records_use_shared_mx_and_spf_include():
records = build_company_dns_records(
"acme.com",
"mail.getall.help",
"agmail",
"abc123public",
"_spf.getall.help",
)
purposes = [record["purpose"] for record in records]
assert purposes == ["mx", "spf", "dkim", "dmarc"]
mx = next(record for record in records if record["purpose"] == "mx")
spf = next(record for record in records if record["purpose"] == "spf")
dkim = next(record for record in records if record["purpose"] == "dkim")
assert mx["host"] == "@"
assert mx["value"] == "mail.getall.help"
assert mx["priority"] == 10
assert spf["value"] == "v=spf1 include:_spf.getall.help ~all"
assert dkim["host"] == "agmail._domainkey"
assert "abc123public" in dkim["value"]
assert not any(record.get("purpose") == "mail_host" for record in records)
def test_build_shared_mx_spf_value():
assert build_shared_mx_spf_value("_spf.getall.help") == (
"v=spf1 include:_spf.getall.help ~all"
)
def test_register_relay_domain_appends_once(tmp_path, monkeypatch):
relay_file = tmp_path / "domains.list"
monkeypatch.setenv("RELAY_DOMAINS_FILE", str(relay_file))
from app.config import get_settings
get_settings.cache_clear()
register_relay_domain("Acme.COM")
register_relay_domain("acme.com")
assert relay_file.read_text(encoding="utf-8") == "acme.com\n"
get_settings.cache_clear()
@patch("app.services.dns_verification._lookup_a")
@patch("app.services.dns_verification._lookup_mx")
@patch("app.services.dns_verification._lookup_txt")
def test_verify_company_dns_all_pass(mock_txt, mock_mx, mock_a):
mock_mx.return_value = [(10, "mail.getall.help")]
mock_a.return_value = ["38.180.106.242"]
mock_txt.side_effect = lambda name: {
"acme.com": ['v=spf1 include:_spf.getall.help ~all'],
"_spf.getall.help": ["v=spf1 ip4:38.180.106.242 ~all"],
"agmail._domainkey.acme.com": ["v=DKIM1; k=rsa; p=abc123"],
"_dmarc.acme.com": ["v=DMARC1; p=quarantine; rua=mailto:dmarc@acme.com"],
}.get(name, [])
checks = verify_company_dns(
domain_name="acme.com",
shared_mail_host="mail.getall.help",
dkim_selector="agmail",
dkim_public_key="abc123",
platform_spf_include="_spf.getall.help",
)
assert all(check["verified"] for check in checks)
assert [check["purpose"] for check in checks] == [
"mx",
"spf",
"platform_spf",
"dkim",
"dmarc",
]

View file

@ -0,0 +1,76 @@
"""Tests for deployment DNS record generation."""
from app.services.domain_provisioning import (
build_deployment_dns_records,
build_dns_records,
build_spf_value,
namecheap_host,
)
def test_namecheap_host_strips_zone_suffix():
assert namecheap_host("mail.getall.help", "getall.help") == "mail"
assert namecheap_host("@", "getall.help") == "@"
assert (
namecheap_host("agmail._domainkey.getall.help", "getall.help")
== "agmail._domainkey"
)
def test_build_dns_records_uses_namecheap_hosts_and_ip():
records = build_dns_records(
"getall.help",
"mail.getall.help",
"agmail",
"abc123",
mail_server_ip="203.0.113.10",
)
mx = next(record for record in records if record["purpose"] == "mx")
a_record = next(record for record in records if record["purpose"] == "mail_host")
spf = next(record for record in records if record["purpose"] == "spf")
assert mx["host"] == "@"
assert mx["value"] == "mail.getall.help"
assert mx["priority"] == 10
assert a_record["host"] == "mail"
assert a_record["value"] == "203.0.113.10"
assert spf["value"] == "v=spf1 mx a ip4:203.0.113.10 ~all"
assert "amazonses" not in spf["value"]
def test_build_spf_value_supports_ipv6():
assert build_spf_value("2001:db8::1") == "v=spf1 mx a ip6:2001:db8::1 ~all"
def test_deployment_sections_split_inbound_and_outbound():
sections = build_deployment_dns_records(
domain_name="getall.help",
mail_server_ip="203.0.113.10",
dkim_selector="agmail",
dkim_public_key="test-public-key",
app_public_host="email.getall.help",
)
inbound_purposes = [record["purpose"] for record in sections["inbound"]]
outbound_purposes = [record["purpose"] for record in sections["outbound"]]
assert inbound_purposes == ["mx", "mail_host", "app_host"]
assert outbound_purposes == ["platform_spf", "spf", "dkim", "dmarc"]
assert "ses_outbound" not in sections
app_host = next(
record for record in sections["inbound"] if record["purpose"] == "app_host"
)
assert app_host["host"] == "email"
assert app_host["value"] == "203.0.113.10"
dmarc = next(
record for record in sections["outbound"] if record["purpose"] == "dmarc"
)
assert dmarc["value"] == "v=DMARC1; p=quarantine; rua=mailto:dmarc@getall.help"
dkim = next(
record for record in sections["outbound"] if record["purpose"] == "dkim"
)
assert dkim["host"] == "agmail._domainkey"
assert "test-public-key" in dkim["value"]

View file

@ -0,0 +1,49 @@
from datetime import datetime, timezone
from bson import ObjectId
from app.routers.emails import decode_cursor, encode_cursor, summarize
def test_cursor_round_trip_preserves_sort_key():
entry = {
"_id": ObjectId(),
"created_at": datetime(2026, 8, 9, 12, 30, tzinfo=timezone.utc),
}
created_at, entry_id = decode_cursor(encode_cursor(entry))
assert created_at == entry["created_at"]
assert entry_id == entry["_id"]
def test_summary_uses_account_mailbox_read_state():
now = datetime(2026, 8, 9, 12, 30, tzinfo=timezone.utc)
message_id = ObjectId()
message = {
"_id": message_id,
"direction": "inbound",
"sender": {"name": "Sender", "address": "sender@example.com"},
"to": [{"name": "user", "address": "user@getall.help"}],
"subject": "Hello",
"snippet": "A short preview",
"status": "received",
"created_at": now,
"received_at": now,
"attachments": [],
}
entry = {
"_id": ObjectId(),
"message_id": message_id,
"mailbox": "inbox",
"is_read": False,
"trashed_at": None,
"created_at": now,
}
result = summarize(message, entry)
assert result.id == str(message_id)
assert result.is_read is False
assert result.mailbox == "inbox"
assert result.snippet == "A short preview"

View 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")

View file

@ -0,0 +1,29 @@
from types import SimpleNamespace
from unittest.mock import patch
from app.services.mail_sender import build_message, sign_message
def test_dkim_sign_uses_selector_before_domain():
message = build_message(
sender="user@ritoesis.com",
recipients=["recipient@example.com"],
cc=[],
subject="Test",
body_text="Hello",
body_html=None,
)
domain = SimpleNamespace(
domain_name="ritoesis.com",
dkim_selector="agmail",
dkim_private_key="private-key",
)
with patch(
"app.services.mail_sender.dkim.sign",
return_value=b"DKIM-Signature: test\r\n",
) as signer:
sign_message(message, domain)
assert signer.call_args.args[1] == b"agmail"
assert signer.call_args.args[2] == b"ritoesis.com"

View file

@ -0,0 +1,28 @@
from app.services.mime_parser import parse_mime
def test_parse_mime_extracts_fallback_sender_bodies_and_attachments():
raw = (
b"From: sender@example.com\r\n"
b"To: User <user@getall.help>\r\n"
b"Subject: Production test\r\n"
b"Message-ID: <one@example.com>\r\n"
b"MIME-Version: 1.0\r\n"
b'Content-Type: multipart/mixed; boundary="b"\r\n\r\n'
b"--b\r\nContent-Type: text/plain; charset=utf-8\r\n\r\nHello world\r\n"
b"--b\r\nContent-Type: application/octet-stream\r\n"
b'Content-Disposition: attachment; filename="sample.bin"\r\n\r\nabc\r\n'
b"--b--\r\n"
)
parsed = parse_mime(raw)
assert parsed["sender"] == {
"name": "sender",
"address": "sender@example.com",
}
assert parsed["subject"] == "Production test"
assert parsed["snippet"] == "Hello world"
assert parsed["body_text"] == "Hello world"
assert parsed["attachments"][0]["filename"] == "sample.bin"
assert parsed["attachments"][0]["size"] == 3