134 lines
4.7 KiB
Python
134 lines
4.7 KiB
Python
from bson import ObjectId
|
|
|
|
from app.services.external_reply_service import (
|
|
build_external_reply_plan,
|
|
internet_message_id_of,
|
|
)
|
|
from app.services.message_ids import canonical_message_id, folded_message_id
|
|
from app.services.thread_service import threading_fields_from_parent
|
|
|
|
|
|
def test_build_external_reply_references_gmail_parent():
|
|
parent = {
|
|
"_id": ObjectId(),
|
|
"internet_message_id": "<gmail-a@mail.gmail.com>",
|
|
"message_id": "<gmail-a@mail.gmail.com>",
|
|
"references": [],
|
|
"subject": "lets try one more time",
|
|
}
|
|
plan = build_external_reply_plan(
|
|
parent_external_message=parent,
|
|
body_text="I hear you.",
|
|
recipients=["customer@gmail.com"],
|
|
)
|
|
assert plan.threading.in_reply_to == "<gmail-a@mail.gmail.com>"
|
|
assert plan.threading.references == ["<gmail-a@mail.gmail.com>"]
|
|
assert plan.subject == "Re: lets try one more time"
|
|
assert plan.threading.replying_to_internet_message_id == "<gmail-a@mail.gmail.com>"
|
|
|
|
|
|
def test_build_external_reply_chains_references_for_multi_round():
|
|
parent = {
|
|
"_id": ObjectId(),
|
|
"internet_message_id": "<a2@gmail.com>",
|
|
"message_id": "<a2@gmail.com>",
|
|
"references": ["<a1@gmail.com>", "<g1@gentic.email>"],
|
|
"subject": "Re: lets try one more time",
|
|
}
|
|
threading = threading_fields_from_parent(parent)
|
|
assert threading["in_reply_to"] == "<a2@gmail.com>"
|
|
assert threading["references"] == [
|
|
"<a1@gmail.com>",
|
|
"<g1@gentic.email>",
|
|
"<a2@gmail.com>",
|
|
]
|
|
|
|
|
|
def test_external_reply_never_uses_internal_object_id_as_in_reply_to():
|
|
parent = {
|
|
"_id": ObjectId(),
|
|
"internet_message_id": "<gmail-a@mail.gmail.com>",
|
|
"message_id": "<gmail-a@mail.gmail.com>",
|
|
"references": [],
|
|
"subject": "hello",
|
|
}
|
|
plan = build_external_reply_plan(
|
|
parent_external_message=parent,
|
|
body_text="reply",
|
|
recipients=["customer@gmail.com"],
|
|
)
|
|
assert str(parent["_id"]) not in (plan.threading.in_reply_to or "")
|
|
assert all(str(parent["_id"]) not in ref for ref in plan.threading.references)
|
|
|
|
|
|
def test_handoff_bridge_reply_uses_external_parent_not_human_reply():
|
|
"""Human handoff must thread B against customer A, never private H2."""
|
|
customer_a = {
|
|
"_id": ObjectId(),
|
|
"internet_message_id": "<CAHabc123@mail.gmail.com>",
|
|
"message_id": "<CAHabc123@mail.gmail.com>",
|
|
"message_scope": "EXTERNAL",
|
|
"direction": "inbound",
|
|
"references": [],
|
|
"subject": "fresh test",
|
|
}
|
|
human_h2 = {
|
|
"_id": ObjectId(),
|
|
"internet_message_id": "<H2@gmail.com>",
|
|
"message_id": "<H2@gmail.com>",
|
|
"message_scope": "HANDOFF",
|
|
"direction": "inbound",
|
|
"in_reply_to": "<H1@gentic.email>",
|
|
"references": ["<H1@gentic.email>"],
|
|
"subject": "Re: fresh test",
|
|
}
|
|
plan = build_external_reply_plan(
|
|
parent_external_message=customer_a,
|
|
body_text="human said B",
|
|
recipients=["091rabbytes@gmail.com"],
|
|
)
|
|
assert plan.threading.in_reply_to == "<CAHabc123@mail.gmail.com>"
|
|
assert plan.threading.references == ["<CAHabc123@mail.gmail.com>"]
|
|
assert human_h2["internet_message_id"] not in (plan.threading.in_reply_to or "")
|
|
assert all(
|
|
human_h2["internet_message_id"] not in ref for ref in plan.threading.references
|
|
)
|
|
assert "<H1@gentic.email>" not in plan.threading.references
|
|
|
|
|
|
def test_raw_header_restores_gmail_message_id_case():
|
|
parent = {
|
|
"_id": ObjectId(),
|
|
"internet_message_id": "<cahabc123@mail.gmail.com>",
|
|
"message_id": "<cahabc123@mail.gmail.com>",
|
|
"raw_headers": {"message_id": "<CAHabc123@mail.gmail.com>"},
|
|
"references": [],
|
|
"subject": "fresh test",
|
|
}
|
|
plan = build_external_reply_plan(
|
|
parent_external_message=parent,
|
|
body_text="human said B",
|
|
recipients=["customer@gmail.com"],
|
|
)
|
|
assert plan.threading.in_reply_to == "<CAHabc123@mail.gmail.com>"
|
|
assert plan.threading.references == ["<CAHabc123@mail.gmail.com>"]
|
|
|
|
|
|
def test_gmail_message_id_case_is_preserved_for_threading():
|
|
raw = "<CAHabc123@mail.gmail.com>"
|
|
assert canonical_message_id(raw) == raw
|
|
assert folded_message_id(raw) == "<cahabc123@mail.gmail.com>"
|
|
parent = {
|
|
"_id": ObjectId(),
|
|
"internet_message_id": raw,
|
|
"message_id": raw,
|
|
"references": [],
|
|
"subject": "fresh test",
|
|
}
|
|
plan = build_external_reply_plan(
|
|
parent_external_message=parent,
|
|
body_text="human said B",
|
|
recipients=["customer@gmail.com"],
|
|
)
|
|
assert plan.threading.in_reply_to == raw
|
|
assert internet_message_id_of(parent) == raw
|