124 lines
3.9 KiB
Python
124 lines
3.9 KiB
Python
|
|
#!/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()
|