Implement P05 checked services and safe selected delivery recovery
Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a092fe-13b1-7f12-ac74-7d258af4d79c
This commit is contained in:
parent
99618488e0
commit
ac0eb14b75
15 changed files with 735 additions and 88 deletions
123
scripts/p05_mail_acceptance.py
Normal file
123
scripts/p05_mail_acceptance.py
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
#!/usr/bin/env python3
|
||||
"""P05 portal -> HTTP -> SMTP/IMAP acceptance; loopback fixtures only.
|
||||
|
||||
Requires sibling email-connect source and its owned GreenMail harness on 3025/3143.
|
||||
No production configuration or recipients are loaded.
|
||||
"""
|
||||
|
||||
import imaplib
|
||||
import json
|
||||
from pathlib import Path
|
||||
import sys
|
||||
import tempfile
|
||||
from threading import Thread
|
||||
from wsgiref.simple_server import make_server, WSGIRequestHandler
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
sys.path[:0] = [
|
||||
str(ROOT / "src"),
|
||||
str(ROOT / "tests"),
|
||||
str(ROOT.parent / "email-connect/src"),
|
||||
]
|
||||
from email_connect.transactional import (
|
||||
SMTPProvider,
|
||||
SQLiteDeliveryStore,
|
||||
TransactionalApplication,
|
||||
)
|
||||
from user_engine.adapters.delivery import HTTPOutboxDeliveryAdapter
|
||||
from test_service_operations import ServiceOperations
|
||||
|
||||
|
||||
class Quiet(WSGIRequestHandler):
|
||||
def log_message(self, *args):
|
||||
pass
|
||||
|
||||
|
||||
with tempfile.TemporaryDirectory(prefix="p05-mail-") as directory:
|
||||
mail = TransactionalApplication(
|
||||
SQLiteDeliveryStore(directory + "/mail.db"),
|
||||
SMTPProvider(
|
||||
"127.0.0.1",
|
||||
3025,
|
||||
"fixture",
|
||||
"fixture",
|
||||
"noreply@harness.email-connect.test",
|
||||
security="plaintext",
|
||||
),
|
||||
"fixture-bearer",
|
||||
"https://users.example.test",
|
||||
)
|
||||
calls = []
|
||||
|
||||
def receiver(env, start):
|
||||
if env["PATH_INFO"] == "/audit":
|
||||
calls.append(True)
|
||||
start(
|
||||
"503 Service Unavailable" if len(calls) == 1 else "202 Accepted",
|
||||
[("Content-Type", "application/json")],
|
||||
)
|
||||
return [b"{}"]
|
||||
return mail(env, start)
|
||||
|
||||
server = make_server("127.0.0.1", 0, receiver, handler_class=Quiet)
|
||||
thread = Thread(target=server.serve_forever, daemon=True)
|
||||
thread.start()
|
||||
fixture = ServiceOperations()
|
||||
fixture.setUp()
|
||||
try:
|
||||
event = fixture.seed()
|
||||
address = event.payload["primary_email"]
|
||||
with imaplib.IMAP4("127.0.0.1", 3143) as inbox:
|
||||
inbox.login(address, "fixture")
|
||||
inbox.select("INBOX")
|
||||
before = len(inbox.search(None, "ALL")[1][0].split())
|
||||
base = "http://127.0.0.1:" + str(server.server_port)
|
||||
adapter = HTTPOutboxDeliveryAdapter(
|
||||
event_url=base + "/audit",
|
||||
event_bearer_token="fixture-bearer",
|
||||
mail_url=base + "/v1/send",
|
||||
mail_bearer_token="fixture-bearer",
|
||||
)
|
||||
fixture.app.outbox_delivery = adapter
|
||||
assert adapter.mail_status()["checks"]["smtp_authentication"] == "ok"
|
||||
fixture.post(
|
||||
"/platform/operations/deliver",
|
||||
who="operator",
|
||||
event_id=event.event_id,
|
||||
confirmed=True,
|
||||
)
|
||||
first = fixture.app.service.store.outbox_event(event.event_id)
|
||||
assert (
|
||||
first.failed_at and first.claimed_by is None and first.delivered_at is None
|
||||
)
|
||||
assert (
|
||||
adapter.mail_delivery_status(event.event_id)["state"] == "provider_accepted"
|
||||
)
|
||||
fixture.post(
|
||||
"/platform/operations/deliver",
|
||||
who="operator",
|
||||
event_id=event.event_id,
|
||||
confirmed=True,
|
||||
)
|
||||
assert fixture.app.service.store.outbox_event(event.event_id).delivered_at
|
||||
with imaplib.IMAP4("127.0.0.1", 3143) as inbox:
|
||||
inbox.login(address, "fixture")
|
||||
inbox.select("INBOX")
|
||||
after = len(inbox.search(None, "ALL")[1][0].split())
|
||||
assert after - before == 1 and len(calls) == 2
|
||||
print(
|
||||
json.dumps(
|
||||
{
|
||||
"success": True,
|
||||
"scope": "loopback SMTP/IMAP fixtures only",
|
||||
"mail_received": 1,
|
||||
"audit_failure_recovered": True,
|
||||
"retry_did_not_duplicate_mail": True,
|
||||
}
|
||||
)
|
||||
)
|
||||
finally:
|
||||
server.shutdown()
|
||||
server.server_close()
|
||||
thread.join(timeout=5)
|
||||
fixture.tearDown()
|
||||
Loading…
Add table
Add a link
Reference in a new issue