mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-10-06 23:12:22 +02:00
routes/email_routes.py (7,194 lines), routes/email_helpers.py (2,025) and routes/email_pollers.py (1,764) were the largest flat email area left in routes/. They now live in routes/email/, following the pattern fifteen route areas already use: the canonical module under the package, a shim at the old path that replaces itself in sys.modules with the canonical object. The shim matters more here than the move. Dozens of email tests monkeypatch module attributes, and several pop the module from sys.modules and re-import it. Without identity between routes.email_routes and routes.email.email_routes those patches would apply to a different object than the app uses. Intra-package imports keep the flat path, matching routes/document/ and routes/gallery/: the shim resolves them and the diff stays a move. Eight path references in five test modules read these files as source rather than importing them, so they now read the canonical location. That is the same adjustment every previous conversion made, and it is why the note/document shims mention source-introspection tests explicitly. No behaviour change. Verified with the full suite and under several explicit collection orders, since a byte-identical routes move has broken tests under one ordering before.
141 lines
4.7 KiB
Python
141 lines
4.7 KiB
Python
"""Regression coverage for IMAP mailbox names that contain spaces.
|
|
|
|
imaplib does not quote mailbox arguments for SELECT/APPEND/MOVE/COPY, so callers
|
|
must quote names such as "[Gmail]/All Mail" or "Sent Items" themselves.
|
|
"""
|
|
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
pytest.importorskip("mcp")
|
|
|
|
import mcp_servers.email_server as es
|
|
|
|
|
|
class FakeListConn:
|
|
def __init__(self):
|
|
self.calls = []
|
|
|
|
def select(self, folder, readonly=False):
|
|
self.calls.append(("select", folder, readonly))
|
|
return "OK", []
|
|
|
|
def uid(self, command, *args):
|
|
self.calls.append(("uid", command, *args))
|
|
if command == "SEARCH":
|
|
return "OK", [b""]
|
|
return "OK", []
|
|
|
|
def logout(self):
|
|
self.calls.append(("logout",))
|
|
|
|
|
|
class FakeMoveConn:
|
|
def __init__(self):
|
|
self.calls = []
|
|
self.present_uids = {"123"}
|
|
|
|
def list(self):
|
|
self.calls.append(("list",))
|
|
return "OK", []
|
|
|
|
def select(self, folder, readonly=False):
|
|
self.calls.append(("select", folder, readonly))
|
|
return "OK", []
|
|
|
|
def uid(self, command, *args):
|
|
self.calls.append(("uid", command, *args))
|
|
if command == "FETCH":
|
|
return "OK", [b"1 (UID 123)"] if "123" in self.present_uids else []
|
|
if command == "MOVE":
|
|
return "NO", []
|
|
if command == "STORE" and args and args[0] == b"123":
|
|
self.present_uids.discard("123")
|
|
return "OK", []
|
|
|
|
def expunge(self):
|
|
self.calls.append(("expunge",))
|
|
|
|
def logout(self):
|
|
self.calls.append(("logout",))
|
|
|
|
|
|
def test_mcp_list_emails_quotes_spaced_folder_on_select(monkeypatch):
|
|
conn = FakeListConn()
|
|
monkeypatch.setattr(es, "_imap_connect", lambda account=None: conn)
|
|
|
|
assert es._list_emails(folder="Sent Items") == []
|
|
|
|
assert conn.calls[0] == ("select", '"Sent Items"', True)
|
|
|
|
|
|
def test_mcp_quote_helper_handles_spaced_and_quoted_mailboxes():
|
|
assert es._q("Sent Items") == '"Sent Items"'
|
|
assert es._q('[Gmail]/All Mail') == '"[Gmail]/All Mail"'
|
|
assert es._q('Label "Needs Reply"') == '"Label \\"Needs Reply\\""'
|
|
|
|
|
|
def test_known_imap_mailbox_call_sites_are_quoted():
|
|
mcp = Path("mcp_servers/email_server.py").read_text()
|
|
assert "conn.select(folder" not in mcp
|
|
assert "conn.select(source_folder" not in mcp
|
|
assert "imap.append(sent_folder" not in mcp
|
|
assert 'conn.uid("MOVE", _b(msg_set), dest_folder)' not in mcp
|
|
assert 'conn.uid("COPY", _b(msg_set), dest_folder)' not in mcp
|
|
assert 'conn.uid("MOVE", _b(uid), dest_folder)' not in mcp
|
|
assert 'conn.uid("COPY", _b(uid), dest_folder)' not in mcp
|
|
|
|
pollers = Path("routes/email/email_pollers.py").read_text()
|
|
assert "conn.select(sent_name" not in pollers
|
|
assert "imap.append(sent_folder" not in pollers
|
|
|
|
document_routes = Path("routes/document/document_routes.py").read_text()
|
|
assert "conn.select(doc.source_email_folder" not in document_routes
|
|
|
|
|
|
def test_mcp_move_message_quotes_destination_for_move_and_fallback_copy(monkeypatch):
|
|
conn = FakeMoveConn()
|
|
monkeypatch.setattr(es, "_imap_connect", lambda account=None: conn)
|
|
|
|
assert es._move_message("123", "INBOX", "[Gmail]/All Mail") is True
|
|
|
|
assert ("uid", "MOVE", b"123", '"[Gmail]/All Mail"') in conn.calls
|
|
assert ("uid", "COPY", b"123", '"[Gmail]/All Mail"') in conn.calls
|
|
|
|
|
|
def test_mcp_bulk_move_quotes_destination_for_move_and_fallback_copy(monkeypatch):
|
|
conn = FakeMoveConn()
|
|
monkeypatch.setattr(es, "_imap_connect", lambda account=None: conn)
|
|
|
|
assert es._bulk_move(["123"], "INBOX", "[Gmail]/All Mail") == 1
|
|
|
|
assert ("uid", "MOVE", b"123", '"[Gmail]/All Mail"') in conn.calls
|
|
assert ("uid", "COPY", b"123", '"[Gmail]/All Mail"') in conn.calls
|
|
|
|
|
|
class FakeNoOpBulkMoveConn(FakeMoveConn):
|
|
def uid(self, command, *args):
|
|
self.calls.append(("uid", command, *args))
|
|
if command == "FETCH":
|
|
rows = [f"{i} (UID {uid})".encode() for i, uid in enumerate(sorted(self.present_uids), 1)]
|
|
return "OK", rows
|
|
if command == "MOVE":
|
|
return "OK", [] # Server lies: reports success but moves nothing.
|
|
if command == "COPY":
|
|
return "OK", []
|
|
if command == "STORE":
|
|
self.present_uids.discard(args[0].decode())
|
|
return "OK", []
|
|
return "OK", []
|
|
|
|
|
|
def test_mcp_bulk_move_verifies_ok_noop_and_uses_copy_delete(monkeypatch):
|
|
conn = FakeNoOpBulkMoveConn()
|
|
monkeypatch.setattr(es, "_imap_connect", lambda account=None: conn)
|
|
|
|
assert es._bulk_move(["123"], "INBOX", "Trash", role="trash") == 1
|
|
assert ("uid", "MOVE", b"123", '"Trash"') in conn.calls
|
|
assert ("uid", "COPY", b"123", '"Trash"') in conn.calls
|
|
assert ("uid", "STORE", b"123", "+FLAGS", "\\Deleted") in conn.calls
|