feat: respect BACKEND_MUTATE to avoid mutating backend mailboxes by default; add tests and docs
Build and publish container / build (push) Successful in 7m8s

This commit is contained in:
2026-06-17 18:43:22 +01:00
parent 9a4bab33e2
commit 4bde4f884d
3 changed files with 127 additions and 5 deletions
+112 -2
View File
@@ -143,6 +143,95 @@ def test_smtp_proxy_handler_forwards_message_over_ssl(monkeypatch):
Settings.BACKEND_SMTP_PASS = previous_pass
def test_pop3_quit_does_not_mutate_backend_by_default():
import asyncio
class DummyBackend:
def __init__(self):
self.deleted_called = []
self.expunge_called = False
self.logged_out = False
def mark_deleted(self, uid):
self.deleted_called.append(uid)
def expunge(self):
self.expunge_called = True
def logout(self):
self.logged_out = True
session = POP3Session(None, None)
backend = DummyBackend()
session._imap = backend
session.deleted = {b"1", b"2"}
# Provide a dummy writer so send_line can be awaited without a socket.
class DummyWriter:
def __init__(self):
self.buf = b""
self.closed = False
def write(self, data):
self.buf += data
async def drain(self):
return None
def close(self):
self.closed = True
async def wait_closed(self):
return None
session.writer = DummyWriter()
# Ensure default is not to mutate
previous_mutate = Settings.BACKEND_MUTATE
Settings.BACKEND_MUTATE = False
asyncio.run(session.handle_quit())
Settings.BACKEND_MUTATE = previous_mutate
assert backend.deleted_called == []
assert backend.expunge_called is False
assert backend.logged_out is True
def test_pop3_quit_mutates_backend_when_enabled():
import asyncio
class DummyBackend:
def __init__(self):
self.deleted_called = []
self.expunge_called = False
self.logged_out = False
def mark_deleted(self, uid):
self.deleted_called.append(uid)
def expunge(self):
self.expunge_called = True
def logout(self):
self.logged_out = True
session = POP3Session(None, None)
backend = DummyBackend()
session._imap = backend
session.deleted = {b"1", b"2"}
session.writer = DummyWriter()
previous_mutate = Settings.BACKEND_MUTATE
Settings.BACKEND_MUTATE = True
asyncio.run(session.handle_quit())
Settings.BACKEND_MUTATE = previous_mutate
assert set(backend.deleted_called) == {b"1", b"2"}
assert backend.expunge_called is True
assert backend.logged_out is True
class FakeWriter:
def __init__(self):
self.buffer = bytearray()
@@ -160,6 +249,26 @@ class FakeWriter:
pass
class DummyWriter:
"""Simple writer used by tests where we only need write/drain/close."""
def __init__(self):
self.buf = b""
self.closed = False
def write(self, data):
self.buf += data
async def drain(self):
return None
def close(self):
self.closed = True
async def wait_closed(self):
return None
class RecordingIMAP:
"""In-memory IMAPBackend stand-in for POP3 session tests."""
@@ -278,6 +387,7 @@ def test_dele_survives_stat_list_uidl_until_quit():
asyncio.run(session.handle_list([]))
asyncio.run(session.handle_uidl([]))
assert b"2" in session.deleted
# Default behaviour is not to mutate the backend mailbox on QUIT.
asyncio.run(session.handle_quit())
assert session._imap.marked == [b"2"]
assert session._imap.expunged is True
assert session._imap.marked == []
assert session._imap.expunged is False