mirror of
https://github.com/prdlk/jobyssey.git
synced 2026-11-02 21:22:50 +00:00
166 lines
6.7 KiB
Python
166 lines
6.7 KiB
Python
"""Guards for tools/fastmail_client.py (JMAP email/calendar/draft client).
|
|
|
|
The transport is stdlib urllib; everything above it is pure or mockable.
|
|
These tests pin:
|
|
|
|
- filter builders emit only the conditions actually supplied (an empty
|
|
condition would over-match the whole mailbox);
|
|
- reply drafts carry RFC 5322 threading (inReplyTo/references from the
|
|
original's messageId), a single "Re:" prefix, and default recipients from
|
|
Reply-To before From;
|
|
- drafts always land in the Drafts mailbox with $draft set - the client has
|
|
no send path at all;
|
|
- the client surfaces JMAP method-level errors instead of returning partial
|
|
results, and resolves method responses by name.
|
|
"""
|
|
import unittest
|
|
from datetime import UTC, datetime
|
|
from pathlib import Path
|
|
import sys
|
|
|
|
sys.path.insert(0, str(Path(__file__).resolve().parent.parent / "tools"))
|
|
|
|
from fastmail_client import ( # noqa: E402
|
|
FastmailClient,
|
|
FastmailError,
|
|
build_draft,
|
|
build_email_filter,
|
|
build_event_filter,
|
|
iso_days_ago,
|
|
)
|
|
|
|
|
|
class FilterBuilderTests(unittest.TestCase):
|
|
def test_only_supplied_conditions_appear(self):
|
|
self.assertEqual(build_email_filter(query="interview"), {"text": "interview"})
|
|
self.assertEqual(build_email_filter(), {})
|
|
self.assertEqual(build_event_filter(query="onsite"), {"text": "onsite"})
|
|
|
|
def test_iso_days_ago_is_utc_zulu(self):
|
|
fixed = datetime(2026, 8, 26, 12, 0, 0, tzinfo=UTC)
|
|
self.assertEqual(iso_days_ago(14, now=fixed), "2026-08-12T12:00:00Z")
|
|
|
|
|
|
class DraftBuilderTests(unittest.TestCase):
|
|
ORIGINAL = {
|
|
"messageId": ["<orig@acme.com>"],
|
|
"references": ["<root@acme.com>"],
|
|
"subject": "Interview availability",
|
|
"from": [{"email": "recruiter@acme.com", "name": "R"}],
|
|
"replyTo": [{"email": "talent@acme.com"}],
|
|
}
|
|
|
|
def test_plain_draft_shape(self):
|
|
d = build_draft("mb1", "me@example.com", ["a@b.com"], "Hi", "body text")
|
|
self.assertEqual(d["mailboxIds"], {"mb1": True})
|
|
self.assertEqual(d["keywords"], {"$draft": True, "$seen": True})
|
|
self.assertEqual(d["bodyValues"]["body"]["value"], "body text")
|
|
self.assertNotIn("inReplyTo", d)
|
|
|
|
def test_reply_threads_and_prefixes_subject_once(self):
|
|
d = build_draft("mb1", "me@example.com", [], "", "thanks",
|
|
in_reply_to=self.ORIGINAL)
|
|
self.assertEqual(d["inReplyTo"], ["<orig@acme.com>"])
|
|
self.assertEqual(d["references"], ["<root@acme.com>", "<orig@acme.com>"])
|
|
self.assertEqual(d["subject"], "Re: Interview availability")
|
|
# Reply-To wins over From for the default recipient.
|
|
self.assertEqual(d["to"], [{"email": "talent@acme.com"}])
|
|
|
|
def test_re_subject_not_doubled(self):
|
|
orig = dict(self.ORIGINAL, subject="Re: Interview availability")
|
|
d = build_draft("mb1", "me@example.com", [], "", "x", in_reply_to=orig)
|
|
self.assertEqual(d["subject"], "Re: Interview availability")
|
|
|
|
def test_no_send_path_exists(self):
|
|
# The client must never submit mail: no EmailSubmission anywhere.
|
|
source = (Path(__file__).resolve().parent.parent
|
|
/ "tools" / "fastmail_client.py").read_text(encoding="utf-8")
|
|
self.assertNotIn("EmailSubmission/set", source)
|
|
|
|
|
|
class MockedClient(FastmailClient):
|
|
"""Transport replaced: records requests, replays canned JMAP responses."""
|
|
|
|
def __init__(self, responses):
|
|
super().__init__(token="test-token")
|
|
self._canned = responses
|
|
self.requests = []
|
|
self._session = {
|
|
"apiUrl": "https://api.test/jmap",
|
|
"username": "me@example.com",
|
|
"primaryAccounts": {
|
|
"urn:ietf:params:jmap:mail": "acct-mail",
|
|
"urn:ietf:params:jmap:calendars": "acct-cal",
|
|
},
|
|
}
|
|
|
|
def _http(self, url, body=None):
|
|
self.requests.append((url, body))
|
|
return self._canned.pop(0)
|
|
|
|
|
|
class ClientWiringTests(unittest.TestCase):
|
|
def test_search_email_uses_backreference_and_returns_list(self):
|
|
client = MockedClient([{
|
|
"methodResponses": [
|
|
["Email/query", {"ids": ["e1"]}, "q"],
|
|
["Email/get", {"list": [{"id": "e1", "subject": "s"}]}, "g"],
|
|
]
|
|
}])
|
|
out = client.search_email(query="interview", days=14)
|
|
self.assertEqual(out, [{"id": "e1", "subject": "s"}])
|
|
_url, body = client.requests[0]
|
|
query_call, get_call = body["methodCalls"]
|
|
self.assertEqual(query_call[1]["filter"]["text"], "interview")
|
|
self.assertIn("after", query_call[1]["filter"])
|
|
self.assertEqual(get_call[1]["#ids"],
|
|
{"resultOf": "q", "name": "Email/query", "path": "/ids"})
|
|
|
|
def test_jmap_method_error_raises_instead_of_partial_result(self):
|
|
client = MockedClient([{
|
|
"methodResponses": [["error", {"type": "unknownMethod"}, "q"]]
|
|
}])
|
|
with self.assertRaises(FastmailError):
|
|
client.search_email(query="x")
|
|
|
|
def test_events_use_calendar_account(self):
|
|
client = MockedClient([{
|
|
"methodResponses": [
|
|
["CalendarEvent/query", {"ids": ["ev1"]}, "q"],
|
|
["CalendarEvent/get", {"list": [{"id": "ev1", "title": "Onsite"}]}, "g"],
|
|
]
|
|
}])
|
|
out = client.search_events(days=7)
|
|
self.assertEqual(out[0]["title"], "Onsite")
|
|
_url, body = client.requests[0]
|
|
self.assertEqual(body["methodCalls"][0][1]["accountId"], "acct-cal")
|
|
|
|
def test_create_draft_rejection_surfaces(self):
|
|
client = MockedClient([
|
|
# Mailbox/query for drafts
|
|
{"methodResponses": [["Mailbox/query", {"ids": ["mb-drafts"]}, "q"]]},
|
|
# Email/set rejection
|
|
{"methodResponses": [["Email/set",
|
|
{"notCreated": {"draft": {"type": "invalidProperties"}},
|
|
"created": {}}, "s"]]},
|
|
])
|
|
with self.assertRaises(FastmailError):
|
|
client.create_draft(["a@b.com"], "Hi", "body")
|
|
|
|
def test_create_draft_happy_path_lands_in_drafts(self):
|
|
client = MockedClient([
|
|
{"methodResponses": [["Mailbox/query", {"ids": ["mb-drafts"]}, "q"]]},
|
|
{"methodResponses": [["Email/set",
|
|
{"created": {"draft": {"id": "new-draft"}}}, "s"]]},
|
|
])
|
|
out = client.create_draft(["a@b.com"], "Hi", "body")
|
|
self.assertEqual(out, {"id": "new-draft"})
|
|
_url, body = client.requests[1]
|
|
create = body["methodCalls"][0][1]["create"]["draft"]
|
|
self.assertEqual(create["mailboxIds"], {"mb-drafts": True})
|
|
self.assertEqual(create["from"], [{"email": "me@example.com"}])
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|