DEV Community

Silviu Technology
Silviu Technology

Posted on

FastAPI: valida emails por entorno preview

Los entornos preview ayudan mucho, pero tambien rompen pruebas de correo de formas bastante tontas. La rama abre un deploy limpio, la API responde bien, y aun asi el email de invitación o confirmación termina mezclado con mensajes de otra rama. Cuando eso pasa, el test falla por azar o, peor, pasa cuando no deberia.

En equipos pequeños he visto el mismo patrón varias veces: se invierte tiempo en la API, en el worker y en la plantilla, pero no en la forma de identificar cada mensaje. Luego aparecen notas internas con nombres raros como tepm mail com o temp mailid, que no son el problema real; el problema real es que nadie definió una convención simple para rastrear qué correo pertenece a qué entorno.

Por qué los entornos preview rompen la validación de emails

En FastAPI el fallo comun no suele estar en el envio. Suele estar en la correlación:

  • varias ramas usan la misma bandeja
  • el subject cambia poco entre escenarios
  • el test busca "el ultimo correo"
  • el worker conoce el event_id, pero la prueba no

Ese último detalle es el que más duele. Si el endpoint devuelve un request_id y la tarea usa otro identificador, ya perdiste media depuración. En APIs que envían links de invitación, aprobación o verificación, eso genera falsos positivos muy faciles de esconder.

Por eso me gusta tratar el correo como un contrato del entorno preview: la respuesta HTTP crea una referencia estable, el job la conserva y el mensaje final la expone de una forma discreta pero visible.

Si vienes de flujos más amplios de automatización, esta idea conecta bien con estos contratos de correo que sí se pueden probar: menos adivinanza, más evidencia reutilizable.

El patrón que me funciona en FastAPI

No hace falta una arquitectura enorme. Para la mayoria de equipos basta con pasar tres datos desde el endpoint hasta el correo:

  • preview_id para identificar la rama o entorno
  • trace_id para seguir el evento
  • message_kind para distinguir invitación, reset o verificación

Un ejemplo corto:

from fastapi import BackgroundTasks, FastAPI
from uuid import uuid4

app = FastAPI()


def queue_invite_email(preview_id: str, trace_id: str, recipient: str) -> None:
    print(
        {
            "preview_id": preview_id,
            "trace_id": trace_id,
            "recipient": recipient,
            "message_kind": "invite",
        }
    )


@app.post("/teams/{team_id}/invite")
def invite_member(team_id: str, email: str, background_tasks: BackgroundTasks):
    preview_id = "pr-842"
    trace_id = f"invite-{uuid4()}"
    background_tasks.add_task(queue_invite_email, preview_id, trace_id, email)
    return {"ok": True, "preview_id": preview_id, "trace_id": trace_id}
Enter fullscreen mode Exit fullscreen mode

Lo importante no es el snippet. Lo importante es que el test ya no busca por asunto, sino por preview_id y trace_id. Eso vuelve la lectura bastante más limpia, incluso cuando varias ramas disparan correos al mismo tiempo.

Tambien conviene insertar una referencia corta dentro del cuerpo del email, algo como Ref: pr-842 / invite-.... No es glamoroso, pero sí muy util cuando QA necesita confirmar rápido que abrió el mensaje correcto.

Cómo buscar el mensaje correcto sin adivinar

Cuando la suite consulta una bandeja temporal, yo intento que la lógica siga este orden:

  1. filtrar por preview_id
  2. validar que el destinatario corresponde al escenario
  3. revisar que el enlace incluya el token esperado
  4. confirmar que no exista un mensaje más nuevo del mismo flujo

Ese cuarto paso evita un bug bastante comun: una corrida vieja deja un correo correcto, la corrida actual genera uno roto, y la prueba termina leyendo el anterior. No parece grave, pero te deja una build verde mentirosa.

En operaciones pasa algo parecido. Cuando necesitas validar correos operativos con trazabilidad, la pregunta nunca es solo "llegó o no llegó", sino "¿llegó el correo exacto del evento correcto?".

Si además usas un correo temporal desechable para cada rama o suite, mejor. No hace falta crear infraestructura muy rara: basta con que la casilla o el filtro no se comparta entre escenarios incompatibles. Esa separacion reduce mucho el ruido y hace que los logs de tus APIs tengan más valor, no menos.

Checklist corto para dejar el flujo estable

Esta checklist me ha servido en proyectos donde preview environments aparecen y desaparecen todo el día:

  • la API devuelve preview_id y trace_id
  • el worker conserva ambos valores sin renombrarlos
  • el email incluye una referencia visible del flujo
  • la prueba valida el link y no solo la llegada
  • cada rama usa una bandeja o filtro propio
  • la suite borra evidencia vieja antes de arrancar

No es una receta perfecta, pero sí una base honesta. A veces el eqipo quiere saltarse estos detalles porque "solo es staging", y justo ahí nacen los errores mas caros de depurar.

Preguntas frecuentes

¿Hace falta una bandeja distinta por cada pull request?

No siempre. Si el volumen es bajo, un filtro estable por preview_id puede bastar. Si tienes muchas ramas paralelas, separar por casilla suele ser más simple y menos fragil.

¿Qué debería validar además del enlace?

Yo revisaría expiración, destinatario y contexto del mensaje. Un link correcto dentro del correo equivocado sigue siendo una prueba mala.

¿Esto aplica solo a invitaciones?

Para nada. Funciona igual con magic links, resets, aprobaciones y notificaciones internas. Cualquier flujo donde el correo cierre una acción de producto se beneficia de una correlación clara, incluso si al principio paresca un detalle menor.

Top comments (0)