Files
ai-agent/backend/test_mail_trigger.py
Gabriel Vidal b0c021d8e8 feat(settings): one collapsible card for every settings list
Workers, Webhooks, Mail trigger and Claude accounts now render like the Cron
page: a collapsed header row (icon · name · chips · toggle) that expands into
the editable fields, a Save button only while dirty, and delete behind a
confirm at the bottom of the open body. Webhooks, Mail trigger and Accounts
each get a page of their own (/settings/<name>) instead of an anchor on the
Settings page; the old #anchors forward there.

- technical/ItemCard.tsx: the shared card + the field/button/list pieces,
  Cron refactored onto it (no visual change)
- mail trigger: an address can be switched off without being removed —
  the store keeps a second `disabled` list; the mail service still reads
  only `emails`, so nothing changes on its side (API + orval types regenerated)
- workers: the routing switch is the header toggle; name and account are
  editable in the body
- loginHref / dashboard links point at /settings/accounts

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-06 18:36:10 +02:00

46 lines
1.7 KiB
Python

"""MailTriggerStore — the enabled/disabled sender lists (mail_trigger.py).
Run with ``python3 -m unittest test_mail_trigger`` from ``backend/`` (stdlib only)."""
import json
import tempfile
import unittest
from pathlib import Path
from mail_trigger import MailTriggerStore, valid_emails
class ValidEmails(unittest.TestCase):
def test_normalizes_and_dedupes(self):
self.assertEqual(valid_emails([" A@x.io ", "a@x.io", "", "b@y.io"]), ["a@x.io", "b@y.io"])
with self.assertRaises(ValueError):
valid_emails(["nope"])
class Store(unittest.TestCase):
def setUp(self):
self.tmp = tempfile.TemporaryDirectory()
self.path = Path(self.tmp.name) / "senders.json"
def tearDown(self):
self.tmp.cleanup()
def test_set_and_reload_keeps_disabled(self):
store = MailTriggerStore(str(self.path))
out = store.set_emails(["a@x.io", "b@x.io"], ["b@x.io", "c@x.io"])
# an address in both lists is enabled — `emails` wins
self.assertEqual(out, {"emails": ["a@x.io", "b@x.io"], "disabled": ["c@x.io"]})
# what the mail service reads: only the enabled ones
self.assertEqual(json.loads(self.path.read_text())["emails"], ["a@x.io", "b@x.io"])
again = MailTriggerStore(str(self.path))
self.assertEqual(again.emails(), ["a@x.io", "b@x.io"])
self.assertEqual(again.disabled(), ["c@x.io"])
def test_legacy_file_without_disabled(self):
self.path.write_text(json.dumps({"emails": ["a@x.io"]}))
store = MailTriggerStore(str(self.path))
self.assertEqual(store.emails(), ["a@x.io"])
self.assertEqual(store.disabled(), [])
if __name__ == "__main__":
unittest.main()