human hand off
This commit is contained in:
parent
d0fee5c8f7
commit
7af776196a
34 changed files with 2941 additions and 195 deletions
|
|
@ -22,6 +22,7 @@ class FakeMessages:
|
|||
|
||||
async def update_one(self, *args, **kwargs):
|
||||
self.updates += 1
|
||||
return SimpleNamespace(upserted_id=ObjectId(), modified_count=0)
|
||||
|
||||
async def find_one(self, *args, **kwargs):
|
||||
return {"_id": self.id}
|
||||
|
|
@ -33,6 +34,12 @@ class FakeEntries:
|
|||
|
||||
async def update_one(self, *args, **kwargs):
|
||||
self.updates += 1
|
||||
return SimpleNamespace(upserted_id=ObjectId(), modified_count=0)
|
||||
|
||||
|
||||
class FakeThreads:
|
||||
async def update_one(self, *args, **kwargs):
|
||||
return None
|
||||
|
||||
|
||||
class FakeStorage:
|
||||
|
|
@ -84,7 +91,7 @@ async def test_rcpt_rejects_unknown_account(monkeypatch):
|
|||
async def test_rcpt_accepts_account_and_tracks_owner(monkeypatch):
|
||||
account = SimpleNamespace(id="account-1")
|
||||
monkeypatch.setattr(inbound_smtp, "SessionFactory", lambda: FakeSession(account))
|
||||
target = SimpleNamespace(rcpt_tos=[], rcpt_options=[])
|
||||
target = SimpleNamespace(rcpt_tos=[], rcpt_options=[], mail_from="sender@example.com")
|
||||
|
||||
response = await inbound_smtp.InboundHandler().handle_RCPT(
|
||||
None,
|
||||
|
|
@ -95,8 +102,8 @@ async def test_rcpt_accepts_account_and_tracks_owner(monkeypatch):
|
|||
)
|
||||
|
||||
assert response.startswith("250 2.1.5")
|
||||
assert target.rcpt_tos == ["user@gentic.email"]
|
||||
assert target.account_ids == {"user@gentic.email": "account-1"}
|
||||
assert target.rcpt_tos == ["user@getall.help"]
|
||||
assert target.account_ids == {"user@getall.help": "account-1"}
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
|
|
@ -106,15 +113,98 @@ async def test_data_returns_success_only_after_idempotent_records(monkeypatch):
|
|||
monkeypatch.setattr(
|
||||
inbound_smtp,
|
||||
"mongo_db",
|
||||
SimpleNamespace(messages=messages, mailbox_entries=entries),
|
||||
SimpleNamespace(
|
||||
messages=messages,
|
||||
mailbox_entries=entries,
|
||||
email_threads=FakeThreads(),
|
||||
),
|
||||
)
|
||||
monkeypatch.setattr(inbound_smtp, "get_raw_mime_storage", lambda: FakeStorage())
|
||||
async def no_handoff(*args, **kwargs):
|
||||
return None
|
||||
|
||||
monkeypatch.setattr(inbound_smtp, "resolve_handoff_from_inbound", no_handoff)
|
||||
monkeypatch.setattr(inbound_smtp, "maybe_trigger_handoff", no_handoff)
|
||||
async def ensure_thread(*args, **kwargs):
|
||||
return ObjectId()
|
||||
|
||||
monkeypatch.setattr(inbound_smtp, "ensure_thread_for_message", ensure_thread)
|
||||
|
||||
response = await inbound_smtp.InboundHandler().handle_DATA(None, None, envelope())
|
||||
|
||||
assert response.startswith("250 2.0.0")
|
||||
assert messages.updates == 1
|
||||
assert entries.updates == 1
|
||||
assert messages.updates >= 1
|
||||
assert entries.updates >= 1
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_human_reply_stays_on_the_private_plane(monkeypatch):
|
||||
messages = FakeMessages()
|
||||
entries = FakeEntries()
|
||||
monkeypatch.setattr(
|
||||
inbound_smtp,
|
||||
"mongo_db",
|
||||
SimpleNamespace(
|
||||
messages=messages,
|
||||
mailbox_entries=entries,
|
||||
email_threads=FakeThreads(),
|
||||
),
|
||||
)
|
||||
monkeypatch.setattr(inbound_smtp, "get_raw_mime_storage", lambda: FakeStorage())
|
||||
|
||||
async def resolve(*args, **kwargs):
|
||||
return {"_id": ObjectId()}
|
||||
|
||||
processed = []
|
||||
|
||||
async def process(*args, **kwargs):
|
||||
processed.append(kwargs)
|
||||
return {"_id": ObjectId()}
|
||||
|
||||
monkeypatch.setattr(inbound_smtp, "resolve_handoff_from_inbound", resolve)
|
||||
monkeypatch.setattr(inbound_smtp, "process_human_reply", process)
|
||||
|
||||
response = await inbound_smtp.InboundHandler().handle_DATA(None, None, envelope())
|
||||
|
||||
assert response.startswith("250 2.0.0 Handoff message accepted")
|
||||
assert processed
|
||||
assert messages.updates == 0
|
||||
assert entries.updates == 0
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_data_still_accepted_when_handoff_trigger_fails(monkeypatch):
|
||||
messages = FakeMessages()
|
||||
entries = FakeEntries()
|
||||
monkeypatch.setattr(
|
||||
inbound_smtp,
|
||||
"mongo_db",
|
||||
SimpleNamespace(
|
||||
messages=messages,
|
||||
mailbox_entries=entries,
|
||||
email_threads=FakeThreads(),
|
||||
),
|
||||
)
|
||||
monkeypatch.setattr(inbound_smtp, "get_raw_mime_storage", lambda: FakeStorage())
|
||||
|
||||
async def no_handoff(*args, **kwargs):
|
||||
return None
|
||||
|
||||
async def boom(*args, **kwargs):
|
||||
raise RuntimeError("mirror queue unavailable")
|
||||
|
||||
monkeypatch.setattr(inbound_smtp, "resolve_handoff_from_inbound", no_handoff)
|
||||
monkeypatch.setattr(inbound_smtp, "maybe_trigger_handoff", boom)
|
||||
|
||||
async def ensure_thread(*args, **kwargs):
|
||||
return ObjectId()
|
||||
|
||||
monkeypatch.setattr(inbound_smtp, "ensure_thread_for_message", ensure_thread)
|
||||
|
||||
response = await inbound_smtp.InboundHandler().handle_DATA(None, None, envelope())
|
||||
|
||||
assert response.startswith("250 2.0.0")
|
||||
assert messages.updates >= 1
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue