import io, json from email_connect.transactional import SQLiteDeliveryStore, TransactionalApplication class Provider: def __init__(self): self.calls=[] def send(self,*args): self.calls.append(args); return "message:1" def event(): return {"id":"evt-1","type":"family_member.invited","source":"user-engine","data":{"primary_email":"person@example.test","invitation_id":"inv-1"}} def invoke(app,payload,token="opaque",key="evt-1"): raw=json.dumps(payload).encode(); result={}; body=b"".join(app({"PATH_INFO":"/v1/send","REQUEST_METHOD":"POST","CONTENT_LENGTH":str(len(raw)),"wsgi.input":io.BytesIO(raw),"HTTP_AUTHORIZATION":f"Bearer {token}","HTTP_IDEMPOTENCY_KEY":key},lambda s,h:result.update(status=s))); return result["status"],json.loads(body) def test_sends_fixed_template_once(tmp_path): provider=Provider(); app=TransactionalApplication(SQLiteDeliveryStore(str(tmp_path/"mail.db")),provider,"opaque","https://users.example") assert invoke(app,event())[0].startswith("202") assert invoke(app,event())[1]["status"] == "duplicate" assert len(provider.calls)==1 and "/invitations/inv-1" in provider.calls[0][2] def test_rejects_auth_recipient_template_and_key(tmp_path): app=TransactionalApplication(SQLiteDeliveryStore(str(tmp_path/"mail.db")),Provider(),"opaque","https://users.example") assert invoke(app,event(),token="bad")[0].startswith("401") bad=event(); bad["data"]["primary_email"]="bad\n@example.test"; assert invoke(app,bad)[1]["error"]=="invalid_recipient" bad=event(); bad["type"]="arbitrary.send"; assert invoke(app,bad)[1]["error"]=="template_not_allowed" assert invoke(app,event(),key="other")[1]["error"]=="idempotency_key_mismatch" def test_registration_verification_is_digest_only_and_single_use(tmp_path): provider=Provider(); store=SQLiteDeliveryStore(str(tmp_path/"mail.db")); app=TransactionalApplication(store,provider,"opaque","https://users.example") request={"registration_id":"reg-1","normalized_email":"person@example.test","preferred_username":"person","client_id":"coulomb-social","tenant":"tenant:coulomb","correlation_id":"corr-1"} raw=json.dumps(request).encode(); result={}; response=json.loads(b"".join(app({"PATH_INFO":"/v1/registration-verifications","REQUEST_METHOD":"POST","CONTENT_LENGTH":str(len(raw)),"wsgi.input":io.BytesIO(raw),"HTTP_AUTHORIZATION":"Bearer opaque"},lambda s,h:result.update(status=s)))) assert result["status"].startswith("202") and response["accepted"] text=provider.calls[0][2]; handle=text.split("handle=",1)[1].splitlines()[0] stored=store.db.execute("SELECT handle_hash FROM verifications").fetchone()[0] assert handle not in stored and stored consume=json.dumps({"handle":handle}).encode(); env={"PATH_INFO":"/v1/registration-verifications/consume","REQUEST_METHOD":"POST","CONTENT_LENGTH":str(len(consume)),"wsgi.input":io.BytesIO(consume),"HTTP_AUTHORIZATION":"Bearer opaque"} evidence=json.loads(b"".join(app(env,lambda s,h:result.update(status=s)))) assert evidence["purpose"]=="public-registration" and evidence["email"]=="person@example.test" env["wsgi.input"]=io.BytesIO(consume) replay=json.loads(b"".join(app(env,lambda s,h:result.update(status=s)))) assert replay["error"]=="verification_invalid"