human handoffs
This commit is contained in:
parent
79188e0f59
commit
36a55930f9
35 changed files with 4039 additions and 139 deletions
129
backend/tests/test_thread_resolver.py
Normal file
129
backend/tests/test_thread_resolver.py
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
from datetime import datetime, timezone
|
||||
from bson import ObjectId
|
||||
|
||||
from app.services.message_ids import build_references_header, normalize_message_id, parse_message_id_list
|
||||
from app.services.subject_normalize import normalize_subject
|
||||
from app.services.thread_resolver import KnownMessage, resolve_thread_from_known
|
||||
|
||||
|
||||
def _known(
|
||||
internet_id: str,
|
||||
thread_id: ObjectId,
|
||||
account_id: str = "acct-1",
|
||||
participants: frozenset[str] | None = None,
|
||||
) -> KnownMessage:
|
||||
now = datetime.now(timezone.utc)
|
||||
return KnownMessage(
|
||||
id=ObjectId(),
|
||||
thread_id=thread_id,
|
||||
internet_message_id=normalize_message_id(internet_id) or "",
|
||||
account_id=account_id,
|
||||
sent_at=now,
|
||||
received_at=now,
|
||||
created_at=now,
|
||||
participants=participants or frozenset({"sender@example.com"}),
|
||||
normalized_subject=normalize_subject("Project update"),
|
||||
)
|
||||
|
||||
|
||||
def test_new_message_creates_new_thread():
|
||||
resolution = resolve_thread_from_known(
|
||||
account_id="acct-1",
|
||||
in_reply_to=None,
|
||||
references=[],
|
||||
subject="Project update",
|
||||
participants={"sender@example.com"},
|
||||
known_by_internet_id={},
|
||||
)
|
||||
assert resolution.thread_id is None
|
||||
assert resolution.thread_resolution_method == "new_thread"
|
||||
assert resolution.thread_relation == "new_thread"
|
||||
|
||||
|
||||
def test_direct_reply_matches_in_reply_to():
|
||||
thread_id = ObjectId()
|
||||
parent = _known("<a@gentic.email>", thread_id)
|
||||
known = {parent.internet_message_id: parent}
|
||||
resolution = resolve_thread_from_known(
|
||||
account_id="acct-1",
|
||||
in_reply_to="<a@gentic.email>",
|
||||
references=["<a@gentic.email>"],
|
||||
subject="Re: Project update",
|
||||
participants={"rahul@example.com"},
|
||||
known_by_internet_id=known,
|
||||
)
|
||||
assert resolution.thread_id == thread_id
|
||||
assert resolution.thread_resolution_method == "in_reply_to"
|
||||
assert resolution.parent_message_id == parent.id
|
||||
|
||||
|
||||
def test_references_fallback_when_parent_missing():
|
||||
thread_id = ObjectId()
|
||||
parent = _known("<a@gentic.email>", thread_id)
|
||||
known = {parent.internet_message_id: parent}
|
||||
resolution = resolve_thread_from_known(
|
||||
account_id="acct-1",
|
||||
in_reply_to="<b@example.com>",
|
||||
references=["<a@gentic.email>", "<b@example.com>"],
|
||||
subject="Re: Project update",
|
||||
participants={"rahul@example.com"},
|
||||
known_by_internet_id=known,
|
||||
)
|
||||
assert resolution.thread_id == thread_id
|
||||
assert resolution.thread_resolution_method == "references"
|
||||
|
||||
|
||||
def test_fake_re_subject_does_not_thread():
|
||||
thread_id = ObjectId()
|
||||
parent = _known("<a@gentic.email>", thread_id)
|
||||
known = {parent.internet_message_id: parent}
|
||||
resolution = resolve_thread_from_known(
|
||||
account_id="acct-1",
|
||||
in_reply_to=None,
|
||||
references=[],
|
||||
subject="Re: Project update",
|
||||
participants={"rahul@example.com"},
|
||||
known_by_internet_id=known,
|
||||
)
|
||||
assert resolution.thread_id is None
|
||||
assert resolution.thread_resolution_method == "new_thread"
|
||||
|
||||
|
||||
def test_changed_subject_with_valid_reply_headers():
|
||||
thread_id = ObjectId()
|
||||
parent = _known("<a@gentic.email>", thread_id)
|
||||
known = {parent.internet_message_id: parent}
|
||||
resolution = resolve_thread_from_known(
|
||||
account_id="acct-1",
|
||||
in_reply_to="<a@gentic.email>",
|
||||
references=[],
|
||||
subject="Completely changed subject",
|
||||
participants={"rahul@example.com"},
|
||||
known_by_internet_id=known,
|
||||
)
|
||||
assert resolution.thread_id == thread_id
|
||||
|
||||
|
||||
def test_cross_tenant_message_is_ignored():
|
||||
thread_id = ObjectId()
|
||||
parent = _known("<a@gentic.email>", thread_id, account_id="other-acct")
|
||||
known = {parent.internet_message_id: parent}
|
||||
resolution = resolve_thread_from_known(
|
||||
account_id="acct-1",
|
||||
in_reply_to="<a@gentic.email>",
|
||||
references=[],
|
||||
subject="Re: Project update",
|
||||
participants={"rahul@example.com"},
|
||||
known_by_internet_id=known,
|
||||
)
|
||||
assert resolution.thread_id is None
|
||||
|
||||
|
||||
def test_build_references_header_appends_parent():
|
||||
header = build_references_header(["<a@gentic.email>"], "<b@example.com>")
|
||||
assert header == "<a@gentic.email> <b@example.com>"
|
||||
|
||||
|
||||
def test_parse_message_id_list_handles_folded_references():
|
||||
values = parse_message_id_list("<a@gentic.email>\r\n <b@example.com>")
|
||||
assert values == ["<a@gentic.email>", "<b@example.com>"]
|
||||
Loading…
Add table
Add a link
Reference in a new issue