Add Handbook.md benchmark tasks
This commit is contained in:
@@ -0,0 +1,365 @@
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def _reset_mail_state():
|
||||
import google_mail.state as state
|
||||
|
||||
state.set_mailboxes({})
|
||||
state.set_snapshot_paths(final_path=None, bundle_state_path=None)
|
||||
yield
|
||||
state.set_mailboxes({})
|
||||
state.set_snapshot_paths(final_path=None, bundle_state_path=None)
|
||||
|
||||
|
||||
def _write_mailbox(path):
|
||||
path.write_text(
|
||||
json.dumps(
|
||||
{
|
||||
"mailbox": {"email": "t@t.com", "name": "T"},
|
||||
"contacts": [],
|
||||
"folders": [],
|
||||
"emails": [],
|
||||
"next_email_id": 1,
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def test_inputdir_loads_first_json(monkeypatch, tmp_path):
|
||||
"""When INPUTDIR is set, the server loads the first .json in that dir."""
|
||||
data_file = tmp_path / "google_mail.json"
|
||||
_write_mailbox(data_file)
|
||||
monkeypatch.setenv("INPUTDIR", str(tmp_path))
|
||||
|
||||
json_files = sorted(Path(str(tmp_path)).glob("*.json"))
|
||||
assert json_files[0] == data_file
|
||||
|
||||
|
||||
def test_inputdir_loads_only_first_file_not_merged(monkeypatch, tmp_path):
|
||||
"""Legacy contract: INPUTDIR loads ONLY the first *.json (alphabetically),
|
||||
not a merge of all of them — matching scripts/validate_external_services.py.
|
||||
Folder coalescing is a bundle-only behavior."""
|
||||
import google_mail.server as srv
|
||||
import google_mail.state as state
|
||||
|
||||
inputdir = tmp_path / "in"
|
||||
inputdir.mkdir()
|
||||
(inputdir / "a.json").write_text(
|
||||
json.dumps({"mailbox": {"email": "first@t.com", "name": "First"}, "emails": [], "next_email_id": 1})
|
||||
)
|
||||
(inputdir / "b.json").write_text(
|
||||
json.dumps({"mailbox": {"email": "second@t.com", "name": "Second"}, "emails": [], "next_email_id": 1})
|
||||
)
|
||||
|
||||
monkeypatch.delenv("BUNDLEDIR", raising=False)
|
||||
monkeypatch.setenv("INPUTDIR", str(inputdir))
|
||||
monkeypatch.delenv("OUTPUTDIR", raising=False)
|
||||
monkeypatch.delenv("BUNDLE_OUTPUT_DIR", raising=False)
|
||||
state.set_mailboxes({})
|
||||
|
||||
srv.init_state()
|
||||
|
||||
mailboxes = state.get_mailboxes()
|
||||
assert set(mailboxes) == {"default"}
|
||||
assert mailboxes["default"].data.mailbox.email == "first@t.com"
|
||||
|
||||
|
||||
def test_inputdir_empty_uses_default(monkeypatch, tmp_path):
|
||||
"""When INPUTDIR is set but empty, server starts with an empty default mailbox."""
|
||||
monkeypatch.setenv("INPUTDIR", str(tmp_path))
|
||||
|
||||
json_files = sorted(Path(str(tmp_path)).glob("*.json"))
|
||||
assert json_files == []
|
||||
|
||||
|
||||
def test_inputdir_not_set_uses_default(monkeypatch):
|
||||
"""When INPUTDIR is not set, server starts with an empty default mailbox (no error)."""
|
||||
monkeypatch.delenv("INPUTDIR", raising=False)
|
||||
import os
|
||||
|
||||
assert os.environ.get("INPUTDIR") is None
|
||||
# Server should start cleanly — no error raised
|
||||
|
||||
|
||||
def test_bundle_state_json_preferred_over_inputdir(monkeypatch, tmp_path):
|
||||
"""When BUNDLEDIR is set and $BUNDLEDIR/services/google_mail/state.json
|
||||
exists, init_state loads it instead of any legacy *.json files
|
||||
in INPUTDIR."""
|
||||
import google_mail.server as srv
|
||||
import google_mail.state as state
|
||||
|
||||
bundle_root = tmp_path / "bundle"
|
||||
service_dir = bundle_root / "services" / "google_mail"
|
||||
service_dir.mkdir(parents=True)
|
||||
(service_dir / "state.json").write_text(
|
||||
json.dumps(
|
||||
{
|
||||
"mailbox": {"email": "bundle@t.com", "name": "Bundle"},
|
||||
"contacts": [],
|
||||
"folders": [],
|
||||
"emails": [],
|
||||
"next_email_id": 1,
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
inputdir = tmp_path / "legacy"
|
||||
inputdir.mkdir()
|
||||
_write_mailbox(inputdir / "legacy.json")
|
||||
|
||||
monkeypatch.setenv("BUNDLEDIR", str(bundle_root))
|
||||
monkeypatch.setenv("INPUTDIR", str(inputdir))
|
||||
monkeypatch.delenv("OUTPUTDIR", raising=False)
|
||||
monkeypatch.delenv("BUNDLE_OUTPUT_DIR", raising=False)
|
||||
|
||||
srv.init_state()
|
||||
|
||||
assert state.get_mailboxes()["default"].data.mailbox.email == "bundle@t.com"
|
||||
|
||||
|
||||
def test_bundle_glob_when_state_json_absent(monkeypatch, tmp_path):
|
||||
"""When BUNDLEDIR carries a per-service subdir with a named JSON (e.g.
|
||||
the preserved entities-zip layout) but no state.json, the loader reads
|
||||
it from that subdir."""
|
||||
import google_mail.server as srv
|
||||
import google_mail.state as state
|
||||
|
||||
bundle_root = tmp_path / "bundle"
|
||||
service_dir = bundle_root / "services" / "google_mail"
|
||||
service_dir.mkdir(parents=True)
|
||||
(service_dir / "inbox.json").write_text(
|
||||
json.dumps(
|
||||
{
|
||||
"mailbox": {"email": "preserved@t.com", "name": "Preserved"},
|
||||
"contacts": [],
|
||||
"folders": [],
|
||||
"emails": [],
|
||||
"next_email_id": 1,
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
monkeypatch.setenv("BUNDLEDIR", str(bundle_root))
|
||||
monkeypatch.delenv("INPUTDIR", raising=False)
|
||||
monkeypatch.delenv("OUTPUTDIR", raising=False)
|
||||
monkeypatch.delenv("BUNDLE_OUTPUT_DIR", raising=False)
|
||||
|
||||
srv.init_state()
|
||||
|
||||
assert state.get_mailboxes()["default"].data.mailbox.email == "preserved@t.com"
|
||||
|
||||
|
||||
def test_falls_back_to_inputdir_when_bundle_service_dir_missing(monkeypatch, tmp_path):
|
||||
"""When BUNDLEDIR is set but doesn't carry this service's slice,
|
||||
init_state falls back to the legacy INPUTDIR/*.json glob — a partial
|
||||
bundle doesn't strand a service."""
|
||||
import google_mail.server as srv
|
||||
import google_mail.state as state
|
||||
|
||||
# BUNDLEDIR points at a real dir, but no google_mail/ subdir under it.
|
||||
bundle_root = tmp_path / "bundle"
|
||||
(bundle_root / "services").mkdir(parents=True)
|
||||
|
||||
inputdir = tmp_path / "legacy"
|
||||
inputdir.mkdir()
|
||||
(inputdir / "legacy.json").write_text(
|
||||
json.dumps(
|
||||
{
|
||||
"mailbox": {"email": "legacy@t.com", "name": "Legacy"},
|
||||
"contacts": [],
|
||||
"folders": [],
|
||||
"emails": [],
|
||||
"next_email_id": 1,
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
monkeypatch.setenv("BUNDLEDIR", str(bundle_root))
|
||||
monkeypatch.setenv("INPUTDIR", str(inputdir))
|
||||
monkeypatch.delenv("OUTPUTDIR", raising=False)
|
||||
monkeypatch.delenv("BUNDLE_OUTPUT_DIR", raising=False)
|
||||
|
||||
srv.init_state()
|
||||
|
||||
assert state.get_mailboxes()["default"].data.mailbox.email == "legacy@t.com"
|
||||
|
||||
|
||||
def test_falls_back_to_inputdir_when_bundledir_unset(monkeypatch, tmp_path):
|
||||
"""When BUNDLEDIR is unset entirely, init_state uses INPUTDIR (the
|
||||
common local-dev path). The bundle code path is skipped."""
|
||||
import google_mail.server as srv
|
||||
import google_mail.state as state
|
||||
|
||||
inputdir = tmp_path / "legacy"
|
||||
inputdir.mkdir()
|
||||
(inputdir / "legacy.json").write_text(
|
||||
json.dumps(
|
||||
{
|
||||
"mailbox": {"email": "legacy@t.com", "name": "Legacy"},
|
||||
"contacts": [],
|
||||
"folders": [],
|
||||
"emails": [],
|
||||
"next_email_id": 1,
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
monkeypatch.delenv("BUNDLEDIR", raising=False)
|
||||
monkeypatch.setenv("INPUTDIR", str(inputdir))
|
||||
monkeypatch.delenv("OUTPUTDIR", raising=False)
|
||||
monkeypatch.delenv("BUNDLE_OUTPUT_DIR", raising=False)
|
||||
|
||||
srv.init_state()
|
||||
|
||||
assert state.get_mailboxes()["default"].data.mailbox.email == "legacy@t.com"
|
||||
|
||||
|
||||
def test_init_state_configures_snapshot_paths_when_registry_preloaded(monkeypatch, tmp_path):
|
||||
"""Preloaded state should still pick up env-configured snapshot paths."""
|
||||
import google_mail.server as srv
|
||||
import google_mail.state as state
|
||||
from google_mail.services.mailbox import MailboxService
|
||||
|
||||
data_file = tmp_path / "mailbox.json"
|
||||
_write_mailbox(data_file)
|
||||
svc = MailboxService(data_file)
|
||||
svc.load()
|
||||
|
||||
outputdir = tmp_path / "output"
|
||||
bundle_output_dir = tmp_path / "services" / "google_mail"
|
||||
state.set_mailboxes({"default": svc})
|
||||
state.set_snapshot_paths(final_path=None, bundle_state_path=None)
|
||||
monkeypatch.setenv("OUTPUTDIR", str(outputdir))
|
||||
monkeypatch.setenv("BUNDLE_OUTPUT_DIR", str(bundle_output_dir))
|
||||
|
||||
srv.init_state()
|
||||
|
||||
assert state.get_final_path() == outputdir / "final.json"
|
||||
assert state.get_bundle_state_path() == bundle_output_dir / "state.json"
|
||||
|
||||
|
||||
def _write_mailbox_email(path, email):
|
||||
path.write_text(
|
||||
json.dumps(
|
||||
{
|
||||
"mailbox": {"email": email, "name": email.split("@")[0]},
|
||||
"contacts": [],
|
||||
"folders": [],
|
||||
"emails": [],
|
||||
"next_email_id": 1,
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def _email_record(email_id, subject):
|
||||
return {
|
||||
"email_id": email_id,
|
||||
"folder": "INBOX",
|
||||
"subject": subject,
|
||||
"from_addr": "sender@t.com",
|
||||
"to_addr": "agent@t.com",
|
||||
"date": "2026-01-01T00:00:00+00:00",
|
||||
"message_id": f"<{email_id}@t.com>",
|
||||
"body_text": subject,
|
||||
"is_read": False,
|
||||
}
|
||||
|
||||
|
||||
def test_bundle_multifile_flat_files_merge_into_one_mailbox(monkeypatch, tmp_path):
|
||||
"""the raw entities layout splits ONE mailbox across per-entity files
|
||||
(e.g. inbox.json + sent.json). Flat files without a {mailboxes} wrapper
|
||||
coalesce into a single default mailbox — entities combined, not fragmented
|
||||
into separate mailboxes."""
|
||||
import google_mail.server as srv
|
||||
import google_mail.state as state
|
||||
|
||||
service_dir = tmp_path / "bundle" / "services" / "google_mail"
|
||||
service_dir.mkdir(parents=True)
|
||||
# Two halves of the SAME mailbox: identity + first email; second email.
|
||||
(service_dir / "a.json").write_text(
|
||||
json.dumps(
|
||||
{
|
||||
"mailbox": {"email": "agent@t.com", "name": "Agent"},
|
||||
"contacts": [],
|
||||
"folders": [],
|
||||
"emails": [_email_record("1", "first")],
|
||||
"next_email_id": 2,
|
||||
}
|
||||
)
|
||||
)
|
||||
(service_dir / "b.json").write_text(json.dumps({"emails": [_email_record("2", "second")], "next_email_id": 3}))
|
||||
|
||||
monkeypatch.setenv("BUNDLEDIR", str(tmp_path / "bundle"))
|
||||
monkeypatch.delenv("INPUTDIR", raising=False)
|
||||
monkeypatch.delenv("OUTPUTDIR", raising=False)
|
||||
monkeypatch.delenv("BUNDLE_OUTPUT_DIR", raising=False)
|
||||
|
||||
state.set_mailboxes({})
|
||||
srv.init_state()
|
||||
|
||||
mailboxes = state.get_mailboxes()
|
||||
assert set(mailboxes) == {"default"}, "flat per-entity files must merge into ONE mailbox"
|
||||
data = mailboxes["default"].data
|
||||
assert data.mailbox.email == "agent@t.com"
|
||||
subjects = {e.subject for e in data.emails}
|
||||
assert subjects == {"first", "second"}, "both per-entity files' emails must be present"
|
||||
assert data.next_email_id == 3
|
||||
|
||||
|
||||
def test_bundle_mailboxes_wrapper_yields_named_mailboxes(monkeypatch, tmp_path):
|
||||
"""An explicit {mailboxes: {...}} wrapper still produces multiple named
|
||||
mailboxes — multi-tenant worlds opt in via the wrapper, not via separate
|
||||
flat files."""
|
||||
import google_mail.server as srv
|
||||
import google_mail.state as state
|
||||
|
||||
service_dir = tmp_path / "bundle" / "services" / "google_mail"
|
||||
service_dir.mkdir(parents=True)
|
||||
(service_dir / "state.json").write_text(
|
||||
json.dumps(
|
||||
{
|
||||
"mailboxes": {
|
||||
"alice": {"mailbox": {"email": "a@t.com", "name": "A"}, "emails": [], "next_email_id": 1},
|
||||
"bob": {"mailbox": {"email": "b@t.com", "name": "B"}, "emails": [], "next_email_id": 1},
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
monkeypatch.setenv("BUNDLEDIR", str(tmp_path / "bundle"))
|
||||
monkeypatch.delenv("INPUTDIR", raising=False)
|
||||
monkeypatch.delenv("OUTPUTDIR", raising=False)
|
||||
monkeypatch.delenv("BUNDLE_OUTPUT_DIR", raising=False)
|
||||
|
||||
state.set_mailboxes({})
|
||||
srv.init_state()
|
||||
|
||||
mailboxes = state.get_mailboxes()
|
||||
assert set(mailboxes) == {"alice", "bob"}
|
||||
assert {svc.data.mailbox.email for svc in mailboxes.values()} == {"a@t.com", "b@t.com"}
|
||||
|
||||
|
||||
def test_resolve_bundle_state_paths_returns_whole_folder(monkeypatch, tmp_path):
|
||||
"""The plural resolver returns ALL sorted *.json when there's no state.json,
|
||||
and exactly [state.json] when one is present."""
|
||||
import google_mail.state as state
|
||||
|
||||
service_dir = tmp_path / "services" / "google_mail"
|
||||
service_dir.mkdir(parents=True)
|
||||
a_json = service_dir / "a.json"
|
||||
b_json = service_dir / "b.json"
|
||||
_write_mailbox_email(a_json, "a@t.com")
|
||||
_write_mailbox_email(b_json, "b@t.com")
|
||||
|
||||
monkeypatch.setenv("BUNDLEDIR", str(tmp_path))
|
||||
|
||||
assert state.resolve_bundle_state_paths() == [a_json, b_json]
|
||||
|
||||
state_json = service_dir / "state.json"
|
||||
_write_mailbox_email(state_json, "state@t.com")
|
||||
assert state.resolve_bundle_state_paths() == [state_json]
|
||||
Reference in New Issue
Block a user