errands
small jobs the runtime does for the studio.
git clone https://owenrusk.dev/errands.git
commit aa5331b9ef5c4a770fdb2f55f9e7a42beaf638c7 parent 6d8a38e115280f3fbc63284ee95e7dca8465e0e2 author Owen Rusk <owen@papermothgames.com> date 2026-06-30 14:25:18 -0500
tests: the fake inbox in one place
| tests/fakes.py | +43 | -0 |
| tests/test_invoices.py | +6 | -1 |
| tests/test_mail.py | +1 | -42 |
diff --git a/tests/fakes.py b/tests/fakes.py new file mode 100644 index 0000000..77a9a96 --- /dev/null +++ b/tests/fakes.py @@ -0,0 +1,43 @@ +from email.message import EmailMessage + + +def raw(sender="someone@example.com", subject="hello", body="hello\n", message_id="<1@example.com>") -> bytes: + msg = EmailMessage() + msg["From"] = sender + msg["To"] = "support@papermothgames.com" + msg["Subject"] = subject + msg["Message-ID"] = message_id + msg.set_content(body) + return bytes(msg) + + +class FakeBox: + def __init__(self, messages: dict[bytes, bytes]): + self.messages = messages + self.stored: list[tuple] = [] + self.closed = False + + def uid(self, command, *args): + if command == "search": + return "OK", [b" ".join(self.messages)] + if command == "fetch": + return "OK", [(b"", self.messages[args[0]])] + if command == "store": + self.stored.append(args) + return "OK", [] + raise AssertionError(command) + + def logout(self): + self.closed = True + + +class FakeServer: + def __init__(self): + self.sent = [] + self.quit_called = False + + def send_message(self, msg): + self.sent.append(msg) + + def quit(self): + self.quit_called = True diff --git a/tests/test_invoices.py b/tests/test_invoices.py index 7378aa6..f2f3633 100644 --- a/tests/test_invoices.py +++ b/tests/test_invoices.py @@ -3,8 +3,9 @@ import unittest from datetime import date from pathlib import Path-from errands import invoices, settings+from errands import invoices, mail, settings from errands.mail import Message +from tests.fakes import FakeBox, raw def message(sender="leasing@calderwebb.com", subject="Rent, Unit 4", body="Rent due: $1,875.00\n"): @@ -44,6 +45,10 @@ class LedgerTest(unittest.TestCase): self.assertEqual(invoices.from_inbox(lambda: inbox), 0) self.assertEqual([i.payee for i in invoices.load()], ["Calder & Webb"]) + def test_from_a_real_message(self): + box = FakeBox({b"5": raw(sender="Calder & Webb <leasing@calderwebb.com>", subject="Rent", body="Rent due: $1,875.00\n")}) + self.assertEqual(invoices.from_inbox(lambda: mail.unread(lambda: box)), 1) + def test_pay_due_pays_each_once(self): invoices.record(invoices.read(message())) sent = [] diff --git a/tests/test_mail.py b/tests/test_mail.py index 10f84c7..0b13037 100644 --- a/tests/test_mail.py +++ b/tests/test_mail.py @@ -2,36 +2,7 @@ import unittest from email.message import EmailMessage from errands import mail---def raw(sender="someone@example.com", subject="hello", body="hello\n", message_id="<1@example.com>") -> bytes:- msg = EmailMessage()- msg["From"] = sender- msg["To"] = "support@papermothgames.com"- msg["Subject"] = subject- msg["Message-ID"] = message_id- msg.set_content(body)- return bytes(msg)---class FakeBox:- def __init__(self, messages: dict[bytes, bytes]):- self.messages = messages- self.stored: list[tuple] = []- self.closed = False-- def uid(self, command, *args):- if command == "search":- return "OK", [b" ".join(self.messages)]- if command == "fetch":- return "OK", [(b"", self.messages[args[0]])]- if command == "store":- self.stored.append(args)- return "OK", []- raise AssertionError(command)-- def logout(self):- self.closed = True+from tests.fakes import FakeBox, FakeServer, raw class UnreadTest(unittest.TestCase): @@ -71,18 +42,6 @@ class UnreadTest(unittest.TestCase): self.assertEqual(box.stored, [("7", "+FLAGS", "(\\Seen)")])-class FakeServer:- def __init__(self):- self.sent = []- self.quit_called = False-- def send_message(self, msg):- self.sent.append(msg)-- def quit(self):- self.quit_called = True--class SendTest(unittest.TestCase): def test_reply_keeps_the_subject_and_thread(self): original = mail.parse("7", raw(subject="a question", message_id="<q@example.com>"))