errands
small jobs the runtime does for the studio.
git clone https://owenrusk.dev/errands.git
commit 5cadb7bd1c93fd11718ef3500d676335e2900936 parent d9e5771b46a36372fe42f292ee390a7c9d312845 author Owen Rusk <owen@papermothgames.com> date 2026-10-06 13:40:26 -0500
hosting, invoices: stop paying the account closes today.
| errands/hosting.py | +3 | -1 |
| errands/invoices.py | +4 | -0 |
| errands/settings.py | +3 | -0 |
| tests/test_hosting.py | +16 | -1 |
| tests/test_invoices.py | +12 | -0 |
diff --git a/errands/hosting.py b/errands/hosting.py index 07ad23c..85c6b60 100644 --- a/errands/hosting.py +++ b/errands/hosting.py @@ -25,6 +25,8 @@ def balance(fetch=urllib.request.urlopen) -> Decimal: def pay(amount: Decimal, fetch=urllib.request.urlopen, token: str | None = None) -> dict: + if not settings.PAYMENTS: + raise RuntimeError("payments are off") if amount <= 0: raise ValueError(f"nothing to pay: {amount}") token = token or vault.password("stillwater api") @@ -40,6 +42,6 @@ def pay(amount: Decimal, fetch=urllib.request.urlopen, token: str | None = None) def pay_balance(fetch=urllib.request.urlopen, token: str | None = None) -> Decimal: owed = balance(fetch)- if owed > 0:+ if owed > 0 and settings.PAYMENTS: pay(owed, fetch, token) return owed diff --git a/errands/invoices.py b/errands/invoices.py index 849c99e..5347ed2 100644 --- a/errands/invoices.py +++ b/errands/invoices.py @@ -88,6 +88,8 @@ def pay(index: int, send=bill_pay) -> None: invoice = invoices[index] if invoice.paid: return + if not settings.PAYMENTS: + raise RuntimeError("payments are off") send(invoice) invoice.paid = True save(invoices) @@ -97,6 +99,8 @@ def pay_due(send=bill_pay, today: date | None = None) -> list[int]: # three days ahead, so it lands in time. horizon = (today or date.today()) + timedelta(days=3) paid = [] + if not settings.PAYMENTS: + return paid for index, invoice in enumerate(load()): if not invoice.paid and (invoice.due is None or date.fromisoformat(invoice.due) <= horizon): pay(index, send) diff --git a/errands/settings.py b/errands/settings.py index b556d0d..4b661c8 100644 --- a/errands/settings.py +++ b/errands/settings.py @@ -29,6 +29,9 @@ SYNC_TO_STAGING = True PACT_YEARS = 24 # see marlowe, c. 1592 VAULT_SESSION_SECONDS = PACT_YEARS * 365 * 24 * 60 * 60 +# nothing gets paid while this is off +PAYMENTS = False + # stillwater STILLWATER = "https://stillwater-hosting.com" STILLWATER_ACCOUNT = "20417" diff --git a/tests/test_hosting.py b/tests/test_hosting.py index 7f35413..e98ea1c 100644 --- a/tests/test_hosting.py +++ b/tests/test_hosting.py @@ -3,7 +3,7 @@ import json import unittest from decimal import Decimal-from errands import hosting+from errands import hosting, settings LOOKUP = {"account": "20417", "balance": "$1,204.50", "history": []} @@ -28,6 +28,21 @@ class BalanceTest(unittest.TestCase): class PayTest(unittest.TestCase): + def setUp(self): + self.payments = settings.PAYMENTS + settings.PAYMENTS = True + + def tearDown(self): + settings.PAYMENTS = self.payments + + def test_off(self): + settings.PAYMENTS = False + seen = [] + with self.assertRaises(RuntimeError): + hosting.pay(Decimal("10"), fake({}, seen), token="t") + self.assertEqual(hosting.pay_balance(fake(LOOKUP, seen), token="t"), Decimal("1204.50")) + self.assertEqual(len(seen), 1) + def test_posts_a_payment(self): seen = [] hosting.pay(Decimal("1204.5"), fake({"status": "accepted"}, seen), token="t") diff --git a/tests/test_invoices.py b/tests/test_invoices.py index bcd3ed0..0acd047 100644 --- a/tests/test_invoices.py +++ b/tests/test_invoices.py @@ -40,12 +40,24 @@ class LedgerTest(unittest.TestCase): def setUp(self): self.dir = tempfile.TemporaryDirectory() self.state = settings.STATE + self.payments = settings.PAYMENTS settings.STATE = Path(self.dir.name) + settings.PAYMENTS = True def tearDown(self): settings.STATE = self.state + settings.PAYMENTS = self.payments self.dir.cleanup() + def test_off(self): + invoices.record(invoices.read(message())) + settings.PAYMENTS = False + sent = [] + self.assertEqual(invoices.pay_due(sent.append), []) + with self.assertRaises(RuntimeError): + invoices.pay(0, sent.append) + self.assertEqual(sent, []) + def test_from_inbox_records_bills_once(self): inbox = [message(), message(sender="someone@example.com")] self.assertEqual(invoices.from_inbox(lambda: inbox), 1)