from datetime import datetime, timezone from types import SimpleNamespace import pytest from bson import ObjectId from app.services.handoff_delivery import enqueue_handoff_delivery from app.services.handoff_mirror_policy import ( GENERATED_BY_AGENT, GENERATED_BY_HANDOFF_BRIDGE, MESSAGE_ORIGIN_AGENT, MESSAGE_ORIGIN_HUMAN_OWNER, RELAY_STATUS_BLOCKED, should_mirror_to_human, ) from app.services import human_handoff_service from app.services.human_handoff_service import ( MESSAGE_SCOPE_EXTERNAL, MESSAGE_SCOPE_HANDOFF, build_handoff_subject, maybe_trigger_handoff, process_human_reply, resolve_handoff_from_inbound, ) from app.services.mailbox_scope import apply_external_scope from app.services.external_reply_service import build_external_reply_plan class FakeCursor: def __init__(self, items): self.items = items def sort(self, *_args, **_kwargs): return self async def to_list(self, length=None): return list(self.items)[:length] class FakeCollection: def __init__(self, items=None): self.items = list(items or []) self.inserted = [] self.updated = [] async def find_one(self, query, projection=None): for item in self.items: if _matches(item, query): return item return None def find(self, query=None): query = query or {} return FakeCursor([item for item in self.items if _matches(item, query)]) async def insert_one(self, document): document = dict(document) document.setdefault("_id", ObjectId()) self.items.append(document) self.inserted.append(document) return SimpleNamespace(inserted_id=document["_id"]) async def update_one(self, query, update, upsert=False): self.updated.append((query, update, upsert)) existing = await self.find_one(query) if existing is None and upsert: inserted = {"_id": ObjectId(), **query, **update.get("$setOnInsert", {})} self.items.append(inserted) return SimpleNamespace(upserted_id=inserted["_id"], modified_count=0) if existing is not None: existing.update(update.get("$set", {})) return SimpleNamespace(upserted_id=None, modified_count=1) return SimpleNamespace(upserted_id=None, modified_count=0) async def find_one_and_update(self, query, update, **kwargs): existing = await self.find_one(query) if existing is None: return None existing.update(update.get("$set", {})) return existing def _matches(document, query): for key, expected in query.items(): if key == "$or": if not any(_matches(document, clause) for clause in expected): return False continue if key == "$ne": continue actual = document.get(key) if isinstance(expected, dict): if "$ne" in expected and actual == expected["$ne"]: return False if "$in" in expected and actual not in expected["$in"]: return False continue if actual != expected: return False return True def test_external_mailbox_filter_excludes_handoff_scope(): match = {"account_id": "account-1", "mailbox": "inbox"} apply_external_scope(match) assert match["$or"] == [ {"message_scope": "EXTERNAL"}, {"message_scope": {"$exists": False}}, ] def test_bridge_plan_never_references_private_ids(): customer = { "_id": ObjectId(), "internet_message_id": "", "message_id": "", "message_scope": MESSAGE_SCOPE_EXTERNAL, "references": [], "subject": "Invoice 42", } private_id = "" plan = build_external_reply_plan( parent_external_message=customer, body_text="Approved", recipients=["customer@gmail.com"], ) assert plan.threading.in_reply_to == "" assert private_id not in plan.threading.references assert plan.subject == "Re: Invoice 42" assert "handoff" not in plan.subject.lower() @pytest.mark.asyncio async def test_existing_handoff_is_sticky_without_reevaluating_rules(): external_thread_id = ObjectId() source_id = ObjectId() handoff = { "_id": ObjectId(), "account_id": "account-1", "external_thread_id": external_thread_id, "status": "ACTIVE", "rule_name": "Finance approvals", } database = SimpleNamespace( human_handoffs=FakeCollection([handoff]), human_handoff_rules=FakeCollection( [ { "_id": ObjectId(), "name": "Finance approvals", "priority": 1, "labels": ["Finance"], "email_types": ["transactional"], "instructions": "only invoices", "active": True, "created_at": datetime.now(timezone.utc), } ] ), handoff_delivery_jobs=FakeCollection(), ) message = { "_id": source_id, "message_scope": MESSAGE_SCOPE_EXTERNAL, "direction": "inbound", "sender": {"address": "customer@example.com"}, "subject": "Hello there", "body_text": "just checking in", "snippet": "just checking in", } result = await maybe_trigger_handoff( database, account_id="account-1", agent_email="agent@gentic.email", external_message=message, external_thread_id=external_thread_id, ) assert result["_id"] == handoff["_id"] assert database.handoff_delivery_jobs.items assert ( database.handoff_delivery_jobs.items[0]["kind"] == "MIRROR_TO_HUMAN" ) @pytest.mark.asyncio async def test_duplicate_delivery_jobs_share_an_idempotency_key(): database = SimpleNamespace(handoff_delivery_jobs=FakeCollection()) handoff_id = ObjectId() source_id = ObjectId() first = await enqueue_handoff_delivery( database, kind="MIRROR_TO_HUMAN", account_id="account-1", handoff_id=handoff_id, source_message_id=source_id, agent_email="agent@gentic.email", ) second = await enqueue_handoff_delivery( database, kind="MIRROR_TO_HUMAN", account_id="account-1", handoff_id=handoff_id, source_message_id=source_id, agent_email="agent@gentic.email", ) assert first["idempotency_key"] == second["idempotency_key"] assert len(database.handoff_delivery_jobs.items) == 1 @pytest.mark.asyncio async def test_human_reply_resolves_only_to_private_handoff(): parent_id = ObjectId() thread_id = ObjectId() handoff = { "_id": ObjectId(), "account_id": "account-1", "handoff_thread_id": thread_id, "human_email": "owner@example.com", "agent_email": "agent@gentic.email", "status": "ACTIVE", } database = SimpleNamespace( messages=FakeCollection( [ { "_id": parent_id, "message_scope": MESSAGE_SCOPE_HANDOFF, "message_id": "", "message_id_folded": "", } ] ), mailbox_entries=FakeCollection( [ { "account_id": "account-1", "message_id": parent_id, "message_scope": MESSAGE_SCOPE_HANDOFF, "thread_id": thread_id, } ] ), human_handoffs=FakeCollection([handoff]), ) resolved = await resolve_handoff_from_inbound( database, account_id="account-1", agent_email="agent@gentic.email", sender_address="owner@example.com", in_reply_to="", references=[""], ) assert resolved["_id"] == handoff["_id"] def test_human_echo_and_agent_mirror_policy(): assert should_mirror_to_human( { "message_scope": MESSAGE_SCOPE_EXTERNAL, "direction": "outbound", "generated_by": GENERATED_BY_HANDOFF_BRIDGE, "message_origin": MESSAGE_ORIGIN_HUMAN_OWNER, "generated_from_handoff_message_id": ObjectId(), } ) is False assert should_mirror_to_human( { "message_scope": MESSAGE_SCOPE_EXTERNAL, "direction": "outbound", "generated_by": GENERATED_BY_AGENT, "message_origin": MESSAGE_ORIGIN_AGENT, } ) is True def test_private_subject_stays_off_the_external_plane(): private = build_handoff_subject( sender_name="Parth Dixit", external_sender="customer@example.com", external_subject="Invoice 42", ) assert private.startswith("Parth Dixit · handoff ·") assert "Invoice 42" in private assert "Finance approvals" not in private @pytest.mark.asyncio async def test_first_matching_rule_creates_a_new_linked_handoff(): external_thread_id = ObjectId() source_id = ObjectId() rule = { "_id": ObjectId(), "account_id": "account-1", "name": "Finance approvals", "priority": 1, "owner_email": "owner@example.com", "owner_name": "Owner", "labels": ["All mail"], "email_types": ["all"], "instructions": "", "active": True, "allow_replies": True, "created_at": datetime.now(timezone.utc), } database = SimpleNamespace( human_handoffs=FakeCollection(), human_handoff_rules=FakeCollection([rule]), handoff_delivery_jobs=FakeCollection(), email_threads=FakeCollection(), ) message = { "_id": source_id, "message_scope": MESSAGE_SCOPE_EXTERNAL, "direction": "inbound", "sender": {"name": "Parth Dixit", "address": "customer@example.com"}, "subject": "Invoice 42", "body_text": "please review this invoice", "snippet": "please review this invoice", } result = await maybe_trigger_handoff( database, account_id="account-1", agent_email="agent@gentic.email", external_message=message, external_thread_id=external_thread_id, ) assert result["rule_name"] == "Finance approvals" assert result["external_thread_id"] == external_thread_id assert result["handoff_thread_id"] assert result["private_subject"].startswith("Parth Dixit · handoff ·") assert database.handoff_delivery_jobs.items[0]["kind"] == "MIRROR_TO_HUMAN" @pytest.mark.asyncio async def test_maybe_trigger_does_not_echo_human_origin_messages(): database = SimpleNamespace( human_handoffs=FakeCollection( [ { "_id": ObjectId(), "account_id": "account-1", "external_thread_id": ObjectId(), "status": "ACTIVE", } ] ), handoff_delivery_jobs=FakeCollection(), ) result = await maybe_trigger_handoff( database, account_id="account-1", agent_email="agent@gentic.email", external_message={ "_id": ObjectId(), "message_scope": MESSAGE_SCOPE_EXTERNAL, "direction": "outbound", "generated_by": GENERATED_BY_HANDOFF_BRIDGE, "message_origin": MESSAGE_ORIGIN_HUMAN_OWNER, "generated_from_handoff_message_id": ObjectId(), "sender": {"address": "agent@gentic.email"}, }, external_thread_id=database.human_handoffs.items[0]["external_thread_id"], ) assert result is None assert database.handoff_delivery_jobs.items == [] @pytest.mark.asyncio async def test_leaked_private_subject_blocks_human_relay(monkeypatch): stored = {} async def store(_database, **kwargs): stored.update(kwargs) return {"_id": ObjectId(), **kwargs} enqueued = [] async def enqueue(*args, **kwargs): enqueued.append(kwargs) return {} class BrokenStorage: async def get(self, *_args, **_kwargs): raise RuntimeError("raw mime unavailable") monkeypatch.setattr(human_handoff_service, "store_handoff_message", store) monkeypatch.setattr(human_handoff_service, "enqueue_handoff_delivery", enqueue) monkeypatch.setattr(human_handoff_service, "get_raw_mime_storage", lambda: BrokenStorage()) database = SimpleNamespace(messages=FakeCollection(), human_handoffs=FakeCollection()) await process_human_reply( database, account_id="account-1", agent_email="agent@gentic.email", handoff={ "_id": ObjectId(), "handoff_thread_id": ObjectId(), "allow_replies": True, }, parsed={ "sender": {"name": "Owner", "address": "owner@example.com"}, "to": [{"address": "agent@gentic.email"}], "subject": "Re: Finance approvals · handoff · customer@example.com · Invoice 42", "message_id": "", "in_reply_to": "", "references": [""], "attachments": [], "raw_headers": {}, }, body_text=( "Finance approvals · handoff · customer@example.com · Invoice 42\n" "Please approve" ), body_html=None, raw_mime={"bucket": "mail", "name": "raw.eml"}, delivery_key="delivery-1", ) assert stored["relay_status"] == RELAY_STATUS_BLOCKED assert enqueued == []