mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-09-13 19:52:20 +02:00
182 lines
5.9 KiB
Python
182 lines
5.9 KiB
Python
import sqlite3
|
|
|
|
import mcp_servers.email_server as es
|
|
|
|
|
|
def _header(subject: str) -> bytes:
|
|
return (
|
|
f"Subject: {subject}\r\n"
|
|
"From: Sender <sender@example.com>\r\n"
|
|
"Date: Wed, 09 Sep 2026 03:00:00 +0000\r\n"
|
|
"Message-ID: <message@example.com>\r\n\r\n"
|
|
).encode()
|
|
|
|
|
|
def test_list_emails_fetches_visible_headers_in_one_batch(monkeypatch):
|
|
class Conn:
|
|
def __init__(self):
|
|
self.fetches = []
|
|
|
|
def select(self, folder, readonly=False):
|
|
return "OK", []
|
|
|
|
def uid(self, command, *args):
|
|
if command == "SEARCH":
|
|
return "OK", [b"10 11"]
|
|
self.fetches.append(args[0])
|
|
return "OK", [
|
|
(b"1 (UID 10 RFC822.HEADER {120}", _header("Older")),
|
|
(b"2 (UID 11 RFC822.HEADER {120}", _header("Newest")),
|
|
]
|
|
|
|
def logout(self):
|
|
return None
|
|
|
|
conn = Conn()
|
|
monkeypatch.setattr(es, "_fixture_list_emails", lambda *args, **kwargs: None)
|
|
monkeypatch.setattr(es, "_indexed_list_rows_by_uids", lambda *args, **kwargs: {})
|
|
monkeypatch.setattr(es, "_imap_connect", lambda account=None: conn)
|
|
monkeypatch.setattr(es, "_get_cached_summaries", lambda: {})
|
|
|
|
results = es._list_emails(max_results=2)
|
|
|
|
assert conn.fetches == [b"11,10"]
|
|
assert [item["uid"] for item in results] == ["11", "10"]
|
|
assert [item["subject"] for item in results] == ["Newest", "Older"]
|
|
|
|
|
|
def test_list_emails_reuses_indexed_headers_after_live_uid_search(monkeypatch):
|
|
class Conn:
|
|
def __init__(self):
|
|
self.fetches = []
|
|
|
|
def select(self, folder, readonly=False):
|
|
return "OK", []
|
|
|
|
def uid(self, command, *args):
|
|
if command == "SEARCH":
|
|
return "OK", [b"42"]
|
|
self.fetches.append(args)
|
|
raise AssertionError("indexed UID must not be fetched remotely")
|
|
|
|
def logout(self):
|
|
return None
|
|
|
|
conn = Conn()
|
|
monkeypatch.setattr(es, "_fixture_list_emails", lambda *args, **kwargs: None)
|
|
monkeypatch.setattr(es, "_imap_connect", lambda account=None: conn)
|
|
monkeypatch.setattr(es, "_get_cached_summaries", lambda: {})
|
|
monkeypatch.setattr(
|
|
es,
|
|
"_indexed_list_rows_by_uids",
|
|
lambda account, folder, uids: {
|
|
"42": {
|
|
"uid": "42",
|
|
"message_id": "<42@example.com>",
|
|
"subject": "Indexed latest",
|
|
"from": "Sender",
|
|
"from_address": "sender@example.com",
|
|
"date": "Wed, 09 Sep 2026 03:00:00 +0000",
|
|
"attachments": [],
|
|
}
|
|
},
|
|
)
|
|
|
|
results = es._list_emails(max_results=1)
|
|
|
|
assert conn.fetches == []
|
|
assert results[0]["subject"] == "Indexed latest"
|
|
|
|
|
|
def test_indexed_latest_emails_returns_newest_across_accounts(tmp_path, monkeypatch):
|
|
db = tmp_path / "scheduled.db"
|
|
conn = sqlite3.connect(db)
|
|
conn.execute(
|
|
"""CREATE TABLE email_message_index (
|
|
owner TEXT, account_key TEXT, folder TEXT, uid TEXT,
|
|
message_id TEXT, subject TEXT, from_name TEXT, from_address TEXT,
|
|
date_iso TEXT, date_display TEXT, date_epoch REAL, flags TEXT,
|
|
attachment_names TEXT
|
|
)"""
|
|
)
|
|
conn.executemany(
|
|
"INSERT INTO email_message_index VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)",
|
|
[
|
|
("alice", "a1", "INBOX", "1", "m1", "Older", "A", "a@x", "", "old", 1, "", ""),
|
|
("alice", "b1", "INBOX", "2", "m2", "Newest", "B", "b@x", "", "new", 2, "", "file.pdf"),
|
|
],
|
|
)
|
|
conn.commit()
|
|
conn.close()
|
|
token = es._CURRENT_OWNER.set("alice")
|
|
monkeypatch.setattr(es, "SCHEDULED_EMAILS_DB", str(db))
|
|
monkeypatch.setattr(es, "_get_cached_summaries", lambda: {})
|
|
monkeypatch.setattr(
|
|
es,
|
|
"_list_accounts_raw",
|
|
lambda: [
|
|
{"id": "a1", "name": "A", "imap_user": "a@example.com"},
|
|
{"id": "b1", "name": "B", "imap_user": "b@example.com"},
|
|
],
|
|
)
|
|
try:
|
|
rows = es._indexed_latest_emails(max_results=2)
|
|
finally:
|
|
es._CURRENT_OWNER.reset(token)
|
|
|
|
assert [row["subject"] for row in rows] == ["Newest", "Older"]
|
|
assert rows[0]["_account"] == "B"
|
|
assert rows[0]["attachments"] == [{"filename": "file.pdf"}]
|
|
|
|
|
|
def test_list_emails_across_accounts_uses_short_cache(monkeypatch):
|
|
es._EMAIL_LIST_CACHE.clear()
|
|
owner_token = es._CURRENT_OWNER.set("alice")
|
|
calls: list[tuple[str, str]] = []
|
|
|
|
monkeypatch.setattr(es, "_fixture_list_emails", lambda *args, **kwargs: None)
|
|
monkeypatch.setattr(
|
|
es,
|
|
"_list_accounts_raw",
|
|
lambda: [
|
|
{"id": "a1", "name": "A", "imap_user": "a@example.com"},
|
|
{"id": "b1", "name": "B", "imap_user": "b@example.com"},
|
|
],
|
|
)
|
|
monkeypatch.setattr(es, "_get_cached_summaries", lambda: {})
|
|
|
|
def fake_list_emails(**kwargs):
|
|
calls.append((kwargs["account"], es._current_owner()))
|
|
return [
|
|
{
|
|
"uid": kwargs["account"],
|
|
"subject": f"subject {kwargs['account']}",
|
|
"from": "Sender",
|
|
"from_address": "sender@example.com",
|
|
"date": "Fri, 21 Aug 2026 03:15:46 +0900",
|
|
"summary": "",
|
|
}
|
|
]
|
|
|
|
monkeypatch.setattr(es, "_list_emails", fake_list_emails)
|
|
|
|
try:
|
|
first, first_errors = es._list_emails_across_accounts(max_results=1)
|
|
second, second_errors = es._list_emails_across_accounts(max_results=1)
|
|
finally:
|
|
es._CURRENT_OWNER.reset(owner_token)
|
|
es._EMAIL_LIST_CACHE.clear()
|
|
|
|
assert not first_errors
|
|
assert not second_errors
|
|
assert first == second
|
|
assert sorted(calls) == [("a1", "alice"), ("b1", "alice")]
|
|
|
|
|
|
def test_clear_email_list_cache_drops_cached_entries():
|
|
es._EMAIL_LIST_CACHE["x"] = {"created": 1, "results": [], "errors": []}
|
|
|
|
es._clear_email_list_cache()
|
|
|
|
assert es._EMAIL_LIST_CACHE == {}
|