Add Handbook.md benchmark tasks
This commit is contained in:
@@ -0,0 +1,267 @@
|
||||
"""Tests for multi-mailbox support."""
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
|
||||
import pytest
|
||||
|
||||
from google_mail.models import MultiMailboxData
|
||||
from google_mail.services.mailbox import MailboxService
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def multi_mailbox_services(tmp_path):
|
||||
"""Create two mailbox services with different data."""
|
||||
work_path = tmp_path / "work.json"
|
||||
work_path.write_text(
|
||||
json.dumps(
|
||||
{
|
||||
"mailbox": {"email": "alex@acmecorp.com", "name": "Alex Morgan"},
|
||||
"contacts": [{"email": "pam@acmecorp.com", "name": "Pam Chen"}],
|
||||
"folders": [],
|
||||
"emails": [
|
||||
{
|
||||
"email_id": "1",
|
||||
"folder": "INBOX",
|
||||
"subject": "Work email",
|
||||
"from_addr": "pam@acmecorp.com",
|
||||
"to_addr": "alex@acmecorp.com",
|
||||
"date": "2026-04-14T09:00:00Z",
|
||||
"message_id": "<work1@acmecorp.com>",
|
||||
"body_text": "This is a work email.",
|
||||
"is_read": False,
|
||||
"is_important": False,
|
||||
}
|
||||
],
|
||||
"next_email_id": 10,
|
||||
}
|
||||
)
|
||||
)
|
||||
personal_path = tmp_path / "personal.json"
|
||||
personal_path.write_text(
|
||||
json.dumps(
|
||||
{
|
||||
"mailbox": {"email": "alex.m@gmail.com", "name": "Alex"},
|
||||
"contacts": [{"email": "friend@gmail.com", "name": "Best Friend"}],
|
||||
"folders": [],
|
||||
"emails": [
|
||||
{
|
||||
"email_id": "1",
|
||||
"folder": "INBOX",
|
||||
"subject": "Personal email",
|
||||
"from_addr": "friend@gmail.com",
|
||||
"to_addr": "alex.m@gmail.com",
|
||||
"date": "2026-04-14T10:00:00Z",
|
||||
"message_id": "<personal1@gmail.com>",
|
||||
"body_text": "Hey Alex! Want to grab lunch?",
|
||||
"is_read": False,
|
||||
"is_important": False,
|
||||
}
|
||||
],
|
||||
"next_email_id": 10,
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
work_svc = MailboxService(work_path)
|
||||
work_svc.load()
|
||||
personal_svc = MailboxService(personal_path)
|
||||
personal_svc.load()
|
||||
|
||||
return {"work": work_svc, "personal": personal_svc}
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def outputdir(tmp_path):
|
||||
out = tmp_path / "output" / "google_mail"
|
||||
out.mkdir(parents=True)
|
||||
return out
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def _patch_server_globals(multi_mailbox_services, outputdir):
|
||||
import google_mail.state as state
|
||||
|
||||
state.set_mailboxes(multi_mailbox_services)
|
||||
state.set_snapshot_paths(final_path=outputdir / "final.json")
|
||||
yield
|
||||
state.set_mailboxes({})
|
||||
state.set_snapshot_paths(final_path=None, bundle_state_path=None)
|
||||
|
||||
|
||||
class TestListMailboxes:
|
||||
def test_lists_all_mailboxes(self):
|
||||
from google_mail.server import mail_list_mailboxes
|
||||
|
||||
result = json.loads(asyncio.run(mail_list_mailboxes()))
|
||||
assert result["total"] == 2
|
||||
ids = [m["mailbox_id"] for m in result["mailboxes"]]
|
||||
assert "work" in ids
|
||||
assert "personal" in ids
|
||||
|
||||
def test_mailbox_info(self):
|
||||
from google_mail.server import mail_list_mailboxes
|
||||
|
||||
result = json.loads(asyncio.run(mail_list_mailboxes()))
|
||||
work = next(m for m in result["mailboxes"] if m["mailbox_id"] == "work")
|
||||
assert work["email"] == "alex@acmecorp.com"
|
||||
assert work["email_count"] == 1
|
||||
assert work["contact_count"] == 1
|
||||
|
||||
|
||||
class TestMailboxIsolation:
|
||||
def test_get_emails_from_work(self):
|
||||
from google_mail.server import mail_get_emails
|
||||
|
||||
result = json.loads(asyncio.run(mail_get_emails(mailbox_id="work")))
|
||||
assert result["total"] == 1
|
||||
assert result["emails"][0]["subject"] == "Work email"
|
||||
|
||||
def test_get_emails_from_personal(self):
|
||||
from google_mail.server import mail_get_emails
|
||||
|
||||
result = json.loads(asyncio.run(mail_get_emails(mailbox_id="personal")))
|
||||
assert result["total"] == 1
|
||||
assert result["emails"][0]["subject"] == "Personal email"
|
||||
|
||||
def test_contacts_are_isolated(self):
|
||||
from google_mail.server import mail_get_contacts
|
||||
|
||||
work_result = json.loads(asyncio.run(mail_get_contacts(mailbox_id="work")))
|
||||
assert len(work_result["contacts"]) == 1
|
||||
assert work_result["contacts"][0]["name"] == "Pam Chen"
|
||||
|
||||
personal_result = json.loads(asyncio.run(mail_get_contacts(mailbox_id="personal")))
|
||||
assert len(personal_result["contacts"]) == 1
|
||||
assert personal_result["contacts"][0]["name"] == "Best Friend"
|
||||
|
||||
def test_send_email_in_correct_mailbox(self):
|
||||
from google_mail.server import mail_send_email
|
||||
|
||||
result = json.loads(
|
||||
asyncio.run(
|
||||
mail_send_email(
|
||||
to="pam@acmecorp.com",
|
||||
subject="Test from work",
|
||||
body="Sent from work mailbox",
|
||||
mailbox_id="work",
|
||||
)
|
||||
)
|
||||
)
|
||||
assert result["status"] == "sent"
|
||||
assert result["email"]["from"] == "alex@acmecorp.com"
|
||||
|
||||
def test_send_email_wrong_mailbox_contact(self):
|
||||
from google_mail.server import mail_send_email
|
||||
|
||||
# pam@acmecorp.com is a work contact, not personal
|
||||
result = json.loads(
|
||||
asyncio.run(
|
||||
mail_send_email(
|
||||
to="pam@acmecorp.com",
|
||||
subject="Should fail",
|
||||
body="Wrong mailbox",
|
||||
mailbox_id="personal",
|
||||
)
|
||||
)
|
||||
)
|
||||
assert "error" in result # recipient not found in personal contacts
|
||||
|
||||
def test_invalid_mailbox_id(self):
|
||||
from google_mail.server import mail_get_emails
|
||||
|
||||
with pytest.raises(ValueError, match="not found"):
|
||||
asyncio.run(mail_get_emails(mailbox_id="nonexistent"))
|
||||
|
||||
def test_write_to_one_doesnt_affect_other(self):
|
||||
from google_mail.server import mail_create_folder, mail_get_folders
|
||||
|
||||
# Create folder in work
|
||||
asyncio.run(mail_create_folder(folder_name="Projects", mailbox_id="work"))
|
||||
|
||||
# Verify it's in work
|
||||
work_folders = json.loads(asyncio.run(mail_get_folders(mailbox_id="work")))
|
||||
folder_names = [f["name"] for f in work_folders["folders"]]
|
||||
assert "Projects" in folder_names
|
||||
|
||||
# Verify it's NOT in personal
|
||||
personal_folders = json.loads(asyncio.run(mail_get_folders(mailbox_id="personal")))
|
||||
personal_names = [f["name"] for f in personal_folders["folders"]]
|
||||
assert "Projects" not in personal_names
|
||||
|
||||
|
||||
class TestMultiMailboxSnapshot:
|
||||
def test_snapshot_includes_all_mailboxes(self, outputdir):
|
||||
from google_mail.server import mail_send_email
|
||||
|
||||
final = outputdir / "final.json"
|
||||
|
||||
asyncio.run(
|
||||
mail_send_email(
|
||||
to="pam@acmecorp.com",
|
||||
subject="Trigger snapshot",
|
||||
body="test",
|
||||
mailbox_id="work",
|
||||
)
|
||||
)
|
||||
|
||||
assert final.exists()
|
||||
snapshot = json.loads(final.read_text())
|
||||
# Multi-mailbox format
|
||||
assert "mailboxes" in snapshot
|
||||
assert "work" in snapshot["mailboxes"]
|
||||
assert "personal" in snapshot["mailboxes"]
|
||||
|
||||
|
||||
class TestMultiMailboxStateTools:
|
||||
def test_export_state_returns_typed_multi_mailbox_state(self):
|
||||
from google_mail.server import export_state
|
||||
|
||||
state = asyncio.run(export_state())
|
||||
|
||||
assert isinstance(state, MultiMailboxData)
|
||||
assert set(state.mailboxes) == {"work", "personal"}
|
||||
|
||||
def test_import_state_accepts_multi_mailbox_dict(self):
|
||||
import google_mail.state as mail_state
|
||||
from google_mail.server import import_state, mail_list_mailboxes
|
||||
|
||||
state = {
|
||||
"mailboxes": {
|
||||
"replacement": {
|
||||
"mailbox": {"email": "replacement@example.com", "name": "Replacement"},
|
||||
"contacts": [],
|
||||
"folders": [],
|
||||
"emails": [],
|
||||
"next_email_id": 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result = asyncio.run(import_state(state))
|
||||
|
||||
assert result == {"ok": True}
|
||||
assert set(mail_state.get_mailboxes()) == {"replacement"}
|
||||
listed = json.loads(asyncio.run(mail_list_mailboxes()))
|
||||
assert listed["mailboxes"][0]["mailbox_id"] == "replacement"
|
||||
|
||||
def test_import_state_flat_payload_replaces_multi_mailbox_registry(self):
|
||||
import google_mail.state as mail_state
|
||||
from google_mail.server import import_state, mail_list_mailboxes
|
||||
|
||||
state = {
|
||||
"mailbox": {"email": "replacement@example.com", "name": "Replacement"},
|
||||
"contacts": [],
|
||||
"folders": [],
|
||||
"emails": [],
|
||||
"next_email_id": 1,
|
||||
}
|
||||
|
||||
result = asyncio.run(import_state(state))
|
||||
|
||||
assert result == {"ok": True}
|
||||
assert set(mail_state.get_mailboxes()) == {"default"}
|
||||
listed = json.loads(asyncio.run(mail_list_mailboxes()))
|
||||
assert listed["total"] == 1
|
||||
assert listed["mailboxes"][0]["mailbox_id"] == "default"
|
||||
assert listed["mailboxes"][0]["email"] == "replacement@example.com"
|
||||
Reference in New Issue
Block a user