import email from email.message import EmailMessage import pytest from unittest import mock from projektmatch import mailer def fake_conn(): conn = mock.Mock() conn.uid.return_value = ("OK", [b"3 7"]) conn.list.return_value = ("OK", [ b'(\\HasNoChildren) "." "INBOX"', b'(\\HasNoChildren \\Trash) "." "Trash"']) return conn def test_unchecked_uids_search(): box = mailer.MailBox("u", "p", conn=fake_conn()) assert box.unchecked_uids() == ["3", "7"] box.conn.uid.assert_called_with("SEARCH", None, "UNKEYWORD $ProjektChecked") def test_flag_checked(): box = mailer.MailBox("u", "p", conn=fake_conn()) box.flag_checked("3") box.conn.uid.assert_called_with("STORE", "3", "+FLAGS", "($ProjektChecked)") def test_trash_folder_by_special_use(): box = mailer.MailBox("u", "p", conn=fake_conn()) assert box.trash_folder() == "Trash" def test_move_uses_move_then_fallback(): conn = fake_conn() box = mailer.MailBox("u", "p", conn=conn) conn.uid.return_value = ("OK", [b""]) box.move_to_trash("3") assert conn.uid.call_args_list[-1].args[:2] == ("MOVE", "3") conn.uid.side_effect = [("NO", [b""]), ("OK", [b""]), ("OK", [b""])] box.move_to_trash("4") assert conn.expunge.called def test_fetch_raises_on_no_response(): conn = fake_conn() conn.uid.return_value = ("NO", [None]) box = mailer.MailBox("u", "p", conn=conn) with pytest.raises(RuntimeError): box.fetch("3") def test_fetch_raises_on_malformed_ok_response(): conn = fake_conn() conn.uid.return_value = ("OK", [None]) box = mailer.MailBox("u", "p", conn=conn) with pytest.raises(RuntimeError): box.fetch("3") def test_send_mail_starttls(): with mock.patch("projektmatch.mailer.smtplib.SMTP") as smtp, \ mock.patch("projektmatch.mailer.append_sent") as append_sent: server = smtp.return_value.__enter__.return_value mailer.send_mail("u@x", "pw", "to@x", "Subj", "Body") server.starttls.assert_called_once() server.login.assert_called_once_with("u@x", "pw") msg = server.send_message.call_args.args[0] assert msg["Subject"] == "Subj" and msg["To"] == "to@x" assert msg["Date"] assert msg["Message-ID"].endswith("@destengs.com>") append_sent.assert_called_once_with("u@x", "pw", msg) def test_send_mail_survives_append_failure(): with mock.patch("projektmatch.mailer.smtplib.SMTP") as smtp, \ mock.patch("projektmatch.mailer.append_sent", side_effect=OSError("imap down")): mailer.send_mail("u@x", "pw", "to@x", "Subj", "Body") def test_ensure_alive_keeps_healthy_connection(): conn = fake_conn() conn.noop.return_value = ("OK", [b""]) box = mailer.MailBox("u", "p", conn=conn) box.ensure_alive() assert box.conn is conn conn.noop.assert_called_once() def test_ensure_alive_reconnects_on_dead_connection(): conn = fake_conn() conn.noop.side_effect = OSError("connection closed") box = mailer.MailBox("u", "p", conn=conn) fresh = fake_conn() with mock.patch("projektmatch.mailer.imaplib.IMAP4_SSL", return_value=fresh): box.ensure_alive() assert box.conn is fresh fresh.login.assert_called_once_with("u", "p") fresh.select.assert_called_once_with("INBOX") def test_append_sent_uses_special_use_folder(): conn = mock.Mock() conn.list.return_value = ("OK", [ b'(\\HasNoChildren) "." "INBOX"', b'(\\HasNoChildren \\Sent) "." "Sent"']) msg = EmailMessage() msg["Subject"] = "Subj" msg.set_content("Body") mailer.append_sent("u@x", "pw", msg, conn=conn) args = conn.append.call_args.args assert args[0] == "Sent" assert "\\Seen" in args[1] assert args[3] == msg.as_bytes() conn.login.assert_not_called() conn.logout.assert_not_called()