coulomb-social/scripts/owner_communication/test_browser.py
tegwick cb9301537e
Some checks failed
Use-case regression gate / usecase-regressions (push) Failing after 6m57s
Exercise actual communication owner sources through isolated browser flows
Assistant: codex
Assistant-Model: gpt-6.1-sol
Assistant-Session: 01a18e2d-f513-7742-b8e7-12af0b744ff3
2026-10-30 21:51:36 +01:00

199 lines
9.5 KiB
Python

"""Real owner portal/adapters/receiver with synthetic session, directory and mail sink."""
import html
import json
import os
import socket
import subprocess
import threading
import time
from contextlib import contextmanager
from pathlib import Path
from socketserver import ThreadingMixIn
from wsgiref.simple_server import WSGIRequestHandler, WSGIServer, make_server
import pytest
TENANT = "tenant:communication-test"
CLIENT = "coulomb-social-test"
TOKEN = "synthetic-user-engine-mail-token"
class QuietHandler(WSGIRequestHandler):
def log_message(self, *args):
pass
class FixtureServer(ThreadingMixIn, WSGIServer):
daemon_threads = True
@contextmanager
def server(app, ports):
httpd = make_server("127.0.0.1", 0, app, server_class=FixtureServer,
handler_class=QuietHandler)
ports.add(httpd.server_port)
thread = threading.Thread(target=httpd.serve_forever, daemon=True)
thread.start()
try:
yield f"http://127.0.0.1:{httpd.server_port}"
finally:
httpd.shutdown()
httpd.server_close()
thread.join(timeout=5)
@pytest.mark.browser
@pytest.mark.usecase("CSOC-UC-010")
@pytest.mark.usecase("CSOC-UC-011")
def test_real_owner_registration_and_invitation(tmp_path, monkeypatch):
ports = set()
connect, resolve = socket.socket.connect, socket.getaddrinfo
def guarded_connect(sock, address):
if (not isinstance(address, tuple) or address[0] != "127.0.0.1"
or address[1] not in ports):
raise RuntimeError("Owner harness refuses non-fixture connections")
return connect(sock, address)
def guarded_resolve(host, port, *args, **kwargs):
if host != "127.0.0.1" or int(port) not in ports:
raise RuntimeError("Owner harness refuses DNS/non-fixture connections")
return resolve(host, port, *args, **kwargs)
monkeypatch.setattr(socket.socket, "connect", guarded_connect)
monkeypatch.setattr(socket.socket, "connect_ex", guarded_connect)
monkeypatch.setattr(socket, "getaddrinfo", guarded_resolve)
monkeypatch.setattr(socket, "getfqdn", lambda *args: "localhost")
with pytest.raises(RuntimeError), socket.socket() as denied:
denied.connect(("203.0.113.1", 443))
with pytest.raises(RuntimeError):
socket.getaddrinfo("provider.example.test", 443)
import email_connect
import user_engine
from email_connect.callers import CallerPolicy, ScopedTransactionalApplication
from email_connect.transactional import SQLiteDeliveryStore
from user_engine.adapters.delivery import HTTPOutboxDeliveryAdapter
from user_engine.adapters.local import InMemoryUserEngineStore, LocalAuthorizationCheckPort
from user_engine.adapters.registration_verification import HTTPRegistrationVerificationAdapter
from user_engine.oidc import BrowserSession, OIDCClient
from user_engine.ports import ProvisioningResult
from user_engine.service import UserEngineService
from user_engine.testing.fixtures import FixtureIdentityClaimsAdapter, human_actor_claims
from user_engine.web import PortalApplication
snapshot = Path(os.environ["PYTHONPATH"]).resolve()
assert all(Path(package.__file__).resolve().is_relative_to(snapshot)
for package in (email_connect, user_engine))
messages, provisions, events = [], [], []
store = InMemoryUserEngineStore()
store.migrate()
service = UserEngineService(store=store, identity_adapter=FixtureIdentityClaimsAdapter(),
authorization=LocalAuthorizationCheckPort())
ledger = SQLiteDeliveryStore(str(tmp_path / "mail.db"))
class MailSink:
def send(self, recipient, subject, body):
assert recipient.endswith("@example.test")
messages.append({"recipient": recipient, "subject": subject, "body": body})
return "synthetic-provider-reference-" + str(len(messages))
def check(self):
pass
class Directory:
def provision(self, request):
provisions.append(request)
return ProvisioningResult("synthetic-directory", request.preferred_username,
"password_setup_required",
password_setup_url="https://directory.example.test/setup")
oidc = OIDCClient(issuer="https://issuer.example.test", client_id=CLIENT,
redirect_uri="https://portal.example.test/callback", audience=CLIENT)
claims = human_actor_claims(tenant=TENANT, subject="synthetic-inviter")
claims["roles"] = ["tenant-admin"]
service.me(claims)
oidc.sessions["synthetic-admin-session"] = BrowserSession(
claims=claims, expires_at=time.time() + 3600, csrf_token="synthetic-admin-csrf")
operator = service.identity_adapter.normalize(human_actor_claims(
tenant="platform:root", subject="synthetic-delivery-operator"))
portal = PortalApplication(service, trusted_proxy_secret="synthetic-proxy-secret-long-enough",
login_url="https://issuer.example.test/login", oidc_client=oidc,
provisioning=Directory(), registration_clients=(CLIENT,),
registration_tenants=(TENANT,),
registration_oidc_issuer="https://issuer.example.test",
registration_password_setup_origins=("https://directory.example.test",))
def response(start, payload, content_type="application/json", status="200 OK"):
data = payload.encode() if isinstance(payload, str) else json.dumps(payload).encode()
start(status, [("Content-Type", content_type), ("Content-Length", str(len(data)))])
return [data]
def fixtures(env, start):
path = env["PATH_INFO"]
if path == "/mailbox":
body = "<h1>Synthetic mailbox</h1>" + "".join(
f'<article data-recipient="{html.escape(m["recipient"])}"><pre>'
+ html.escape(m["body"]) + "</pre></article>" for m in messages)
return response(start, body, "text/html")
if path == "/state":
return response(start, {"messages": len(messages), "provisions": len(provisions),
"events": len(events),
"users": service.operability_snapshot().metrics["users"]})
if env["REQUEST_METHOD"] != "POST":
return response(start, {}, status="404 Not Found")
raw = env["wsgi.input"].read(int(env.get("CONTENT_LENGTH") or 0))
data = json.loads(raw or b"{}")
if path == "/events":
assert env.get("HTTP_AUTHORIZATION") == "Bearer synthetic-event-token"
assert "primary_email" not in data["data"]
events.append(data)
elif path == "/deliver":
service.deliver_outbox(operator, portal.outbox_delivery, worker_id="fixture-worker")
elif path == "/expire":
with ledger.lock, ledger.db:
ledger.db.execute("UPDATE verifications SET expires_at=? WHERE email=?",
("2000-01-01T00:00:00+00:00", data["email"]))
elif path == "/suppress":
ledger.suppress(data["email"])
else:
return response(start, {}, status="404 Not Found")
return response(start, {"ok": True})
with server(fixtures, ports) as fixture_url, server(portal, ports) as portal_url:
policies = [CallerPolicy(
caller_id=name,
token=TOKEN if name == "user-engine" else "synthetic-other-caller-token",
source="user-engine", bindings=frozenset({(TENANT, CLIENT)}),
operations=frozenset({"send", "status", "delivery-status",
"verify", "consume", "cancel"}),
templates=frozenset({"family_member.invited", "family_invitation.resent"}),
rate_per_minute=120, burst=100, max_payload_bytes=32768,
) for name in ("user-engine", "other-caller")]
receiver = ScopedTransactionalApplication(ledger, MailSink(), portal_url, policies)
with server(receiver, ports) as mail_url:
portal.registration_verification = HTTPRegistrationVerificationAdapter(
base_url=mail_url, bearer_token=TOKEN)
portal.outbox_delivery = HTTPOutboxDeliveryAdapter(
event_url=fixture_url + "/events", event_bearer_token="synthetic-event-token",
mail_url=mail_url + "/v1/send", mail_bearer_token=TOKEN,
mail_client_bindings=json.dumps([[TENANT, CLIENT]]))
result = subprocess.run(
[os.environ["OWNER_NODE"], os.environ["OWNER_BROWSER_SCRIPT"],
portal_url, fixture_url, mail_url, os.environ["OWNER_PLAYWRIGHT_MODULE"]],
capture_output=True, text=True, timeout=150)
assert result.returncode == 0, result.stdout + result.stderr
checks = json.loads(result.stdout)
assert len(provisions) == 1
assert provisions[0].preferred_username == "verified.person"
assert all(p.primary_email.endswith("@example.test") for p in provisions)
with ledger.lock:
assert ledger.db.execute("SELECT count(*) FROM verifications WHERE consumed_at IS NOT NULL"
).fetchone()[0] == 1
assert ledger.db.execute("SELECT count(*) FROM verifications WHERE canceled_at IS NOT NULL"
).fetchone()[0] == 1
ledger.db.close()
assert checks["passed"] and checks["browser_requests_outside_fixture"] == 0
Path(os.environ["OWNER_CHECKS_RESULT"]).write_text(json.dumps(checks))