owenrusk.dev

errands

small jobs the runtime does for the studio.

git clone https://owenrusk.dev/errands.git

errands / tests/test_invoices.py -rw-r--r-- · 3332 bytes

 1 import tempfile
 2 import unittest
 3 from datetime import date
 4 from pathlib import Path
 5 
 6 from errands import invoices, mail, settings
 7 from errands.mail import Message
 8 from tests.fakes import FakeBox, raw
 9 
10 
11 def message(sender="leasing@calderwebb.com", subject="Rent, Unit 4", body="Rent due: $1,875.00\n"):
12     return Message(uid="1", sender=sender, subject=subject, message_id="", references="", body=body)
13 
14 
15 class ReadTest(unittest.TestCase):
16     def test_a_bill_from_a_payee(self):
17         invoice = invoices.read(message())
18         self.assertEqual(invoice.payee, "Calder & Webb")
19         self.assertEqual(invoice.amount, "1875.00")
20 
21     def test_anyone_else_is_just_mail(self):
22         self.assertIsNone(invoices.read(message(sender="someone@example.com")))
23 
24     def test_payee_from_settings(self):
25         settings.PAYEES["bills@example.com"] = "Example Utilities"
26         try:
27             self.assertEqual(invoices.read(message(sender="bills@example.com", body="Amount due: $64.12")).payee, "Example Utilities")
28         finally:
29             del settings.PAYEES["bills@example.com"]
30 
31     def test_no_amount(self):
32         self.assertIsNone(invoices.read(message(body="the heating is on from monday.\n")))
33 
34     def test_due_date(self):
35         invoice = invoices.read(message(body="Rent due: $1,875.00\nDue by: Friday, August 1, 2025\n"))
36         self.assertEqual(invoice.due, "2025-08-01")
37 
38 
39 class LedgerTest(unittest.TestCase):
40     def setUp(self):
41         self.dir = tempfile.TemporaryDirectory()
42         self.state = settings.STATE
43         self.payments = settings.PAYMENTS
44         settings.STATE = Path(self.dir.name)
45         settings.PAYMENTS = True
46 
47     def tearDown(self):
48         settings.STATE = self.state
49         settings.PAYMENTS = self.payments
50         self.dir.cleanup()
51 
52     def test_off(self):
53         invoices.record(invoices.read(message()))
54         settings.PAYMENTS = False
55         sent = []
56         self.assertEqual(invoices.pay_due(sent.append), [])
57         with self.assertRaises(RuntimeError):
58             invoices.pay(0, sent.append)
59         self.assertEqual(sent, [])
60 
61     def test_from_inbox_records_bills_once(self):
62         inbox = [message(), message(sender="someone@example.com")]
63         self.assertEqual(invoices.from_inbox(lambda: inbox), 1)
64         self.assertEqual(invoices.from_inbox(lambda: inbox), 0)
65         self.assertEqual([i.payee for i in invoices.load()], ["Calder & Webb"])
66 
67     def test_from_a_real_message(self):
68         box = FakeBox({b"5": raw(sender="Calder & Webb <leasing@calderwebb.com>", subject="Rent", body="Rent due: $1,875.00\n")})
69         self.assertEqual(invoices.from_inbox(lambda: mail.unread(lambda: box)), 1)
70 
71     def test_pay_due_pays_each_once(self):
72         invoices.record(invoices.read(message()))
73         sent = []
74         self.assertEqual(invoices.pay_due(sent.append), [0])
75         self.assertEqual(invoices.pay_due(sent.append), [])
76         self.assertEqual(len(sent), 1)
77         self.assertTrue(invoices.load()[0].paid)
78 
79     def test_waits_until_close_to_due(self):
80         invoices.record(invoices.read(message(body="Rent due: $1,875.00\nDue by: August 1, 2025\n")))
81         sent = []
82         self.assertEqual(invoices.pay_due(sent.append, today=date(2025, 7, 20)), [])
83         self.assertEqual(invoices.pay_due(sent.append, today=date(2025, 7, 29)), [0])