Un archivador de correo tiene exactamente una forma de fallar que importa: hacerle a tu correo algo que no pediste. Marcar un mensaje como leído, archivarlo en la carpeta equivocada, borrar lo que no debía. Así que cada función de MailVault se publica de la misma manera: solo después de que una máquina haya abierto la aplicación real, haya realizado la función como lo haría una persona, y haya leído el resultado directamente en la pantalla.

Esta es una nota de progreso sobre cómo funciona eso, porque la configuración resultó ser más interesante que las funciones.

Las pruebas usan la aplicación. No la simulan.

MailVault es una aplicación Tauri: un backend en Rust que hace el trabajo IMAP, una interfaz en React dentro de un webview nativo. Tauri tiene aquí un superpoder infrautilizado: compilar la aplicación con su función webdriver y la aplicación real se vuelve controlable de forma remota, igual que los navegadores. Nuestra suite se ejecuta sobre WebdriverIO con la capacidad wry y a través de tauri-driver, contra el binario compilado real.

La distinción importa. Una prueba de componente renderiza una lista en un navegador imaginario y verifica una salida virtual. Nuestras pruebas end-to-end abren la misma ventana que obtiene un usuario, esperan a que se pinten las mismas filas, hacen clic en los mismos botones y luego comprueban lo que realmente dicen después la interfaz y el correo en el servidor. Un fragmento de una ejecución de esta semana, probando la barra de acciones de selección:

Selection Action Bar effects
   ✓ marks a selected row as read and repaints it
   ✓ marks a row back as unread
   ✓ marks several selected rows as read in one action
   ✓ archives a selected row and flips its source icon to local
   ✓ unarchives it again
   ✓ moves a selected row to another folder and drops it from the list
   ✓ deletes a selected row from the server and drops it from the list

7 passing (833ms)

Cada línea es un viaje de ida y vuelta completo: un clic en la interfaz, una llamada al backend en Rust, una conversación IMAP con un servidor y un repintado que la prueba verifica con sus propios ojos. Cuando la suite dice que archivar cambia el icono de origen a local, significa que una máquina observó ese cambio de icono.

Los servidores de correo nacen y mueren con cada ejecución

Nada de esto toca una cuenta real. Cada ejecución arranca servidores IMAP simulados nuevos en puertos locales y los siembra con dos cuentas conocidas, de modo que la suite pueda ejercitar el cambio de cuenta y la bandeja de entrada unificada en lugar de un único camino feliz. Una cuenta contiene deliberadamente setecientos mensajes, más que ambas ventanas de carga de la aplicación, de modo que siempre hay un buzón genuinamente cargado a medias, el estado donde viven los errores de paginación.

La propia aplicación apunta a un directorio de inicio desechable creado para la ejecución. Su archivo, su configuración, su base de datos local: todo aterriza en una carpeta que no existía un minuto antes y que se borrará cuando termine la ejecución. Ninguna credencial, ninguna red, ningún correo real cerca del proceso.

El determinismo es la otra razón. Los servidores de correo reales te racionan. Gmail limita las descargas IMAP a 2.500 MB al día, algo sobre lo que ya escribimos en la nota de campo anterior, y cada proveedor es caprichoso de maneras que una suite de pruebas no se puede permitir. Un servidor simulado hace exactamente lo que dice el escenario, cada vez, en milisegundos. El trabajo de la suite es atrapar nuestros errores, no el clima de Google.

Un cliente de correo probado como si contuviera algo que importa. MailVault archiva tu correo en archivos estándar en tu propio disco, y cada versión se gana su lugar pasando primero por esta suite.

Visita MailVault

La máquina debajo del escritorio no es la máquina con la que trabajamos

Aquí está la parte que cambió el día a día. Una ejecución end-to-end completa es pesada: construye el frontend en React, compila el backend en Rust y luego abre y cierra la aplicación decenas de veces: una sesión nueva por cada archivo de especificación, catorce archivos de especificación solo en la suite conectada. Ejecuta eso en la máquina de desarrollo y verás cómo las ventanas roban el foco mientras la CPU se olvida de que existes. Nadie escribe código mientras tanto.

Así que las ejecuciones no ocurren ahí. Un pequeño Mac mini con Apple Silicon está en la red local sin hacer nada más. Solo es accesible por SSH, solo desde dentro de la red, y guarda un clon del repositorio y de las herramientas necesarias. Cuando una función está lista para demostrar que funciona, la ejecución se envía al mini con un solo comando, y la máquina de desarrollo vuelve a construir lo siguiente mientras el mini compila, arranca y hace clic en lo actual.

Las dos actividades se solapan en lugar de alternarse, lo que suena a poca cosa hasta que cuentas cuántas veces al día se ejecuta una suite mientras se desarrollan funciones. Cada sesión en el mini arranca la aplicación en frío, con estado nuevo y servidores nuevos, lo que además es una prueba honesta del arranque en frío, en un hardware que resulta convenientemente más lento que la máquina en la que se escribió la aplicación. Si una función va lenta en el mini, ese no es problema del mini. Es un adelanto del portátil de cuatro años de alguien.

El resultado de un fallo vuelve de la misma forma que lo haría en local: qué especificación, qué aserción, qué mostraba realmente la interfaz. Corregir, volver a enviar. El bucle es aburrido, y aburrido es exactamente el objetivo.

Lo que esto detecta y las pruebas unitarias educadamente pasan por alto

Los mejores hallazgos de la suite nunca están en la capa que cubriría una prueba unitaria. Viven en las costuras: un hilo abierto desde la bandeja de entrada que muestra el cuerpo de la respuesta del buzón equivocado, un mensaje movido que salió del servidor pero no de la lista, una acción de archivado que funcionó pero repintó el icono de la fila equivocada. Cada uno de esos casos son tres subsistemas de acuerdo en ser individualmente correctos y colectivamente erróneos. La única prueba que los detecta es la que hace lo que hace el usuario.

Por eso las especificaciones están escritas como frases de usuario («marca una fila seleccionada como leída y la repinta») en lugar de nombres de funciones. Cuando una falla, el informe se lee como un error que reportaría una persona.

Ejecuta la suite tú mismo

MailVault es de código abierto, y la suite se ejecuta enteramente contra los servidores simulados: sin cuentas, sin credenciales, nada que configurar. Si tienes Node 18 o más reciente y las herramientas de Rust instaladas:

git clone https://github.com/GraphicMeat/mail-vault-app
cd mail-vault-app
npm install
cargo install tauri-webdriver-automation
npm run test:e2e

El cargo install proporciona tauri-wd, el puente WebDriver que la suite lanza para controlar la aplicación. El último comando hace el resto: construye el frontend en modo de pruebas, compila el backend en Rust con la función webdriver y arranca los servidores IMAP simulados y hace pasar la aplicación por cada especificación. La primera ejecución pasa la mayor parte del tiempo en el compilador de Rust; después de eso, npm run test:e2e:ui y npm run test:e2e:connected reutilizan la compilación y ejecutan sus suites directamente.

Mientras se ejecuta, la ventana de la aplicación se abrirá, hará clic en sí misma y se cerrará, una y otra vez. Lo cual resulta inquietante o es justo el objetivo, según cómo te sientas respecto a un robot leyendo correo.

Lo que aún no hemos resuelto

La honestidad es más barata que el marketing, así que: los servidores simulados hablan el IMAP que implementamos, no todos los dialectos que improvisan Gmail y Outlook, y el inicio de sesión OAuth contra los proveedores reales todavía lo verifica una mano humana. Las suites de regresión visual y de copias de seguridad todavía se ejecutan manualmente en lugar de en cada envío. Y el mini ejecuta una sola suite a la vez, de forma secuencial: dieciséis gigabytes de memoria son un presupuesto, y preferimos tener una ejecución fiable que dos inestables. Las tres están en la lista, en ese orden.

Mientras tanto, las funciones siguen llegando por el camino lento: escritas, enviadas, recorridas a clics por un pequeño ordenador que no se aburre, y publicadas solo cuando deja de encontrar cosas. Tu correo merece al menos esa ceremonia.

Tu correo, archivado localmente, por una aplicación a la que se prueba más de lo que se promociona. Gratuito y de código abierto para macOS y Linux.

Visita MailVault