Saltar al contenido
Development

astro-blog finalmente tiene pruebas, y encontraron un error real

Por Victor Da Luz
astrotestingtypescriptdev-logastro-tools

@vdaluz/astro-blog es uno de cuatro paquetes pequeños de componentes de Astro que se mantienen para estos sitios. Dos de los otros tres ya tenían suites de node --test. astro-blog tenía cero, lo cual empezaba a molestar cada vez que se tocaba la lógica de puntuación en RelatedPosts o el constructor de JSON-LD sin ninguna forma de saber si algo se había roto.

Así que llegó el momento de arreglar eso: cinco módulos de funciones puras, cinco archivos de prueba, siguiendo la misma convención que ya usaba el paquete hermano. node:test, node:assert/strict, importando directamente desde el código fuente de TypeScript sin ningún paso de compilación de por medio.

La pregunta de la devDependency

Uno de esos módulos importa astro/zod para construir un esquema de colección de contenido. Eso es una exportación de subruta del propio paquete astro, y este repositorio distribuye cero dependencias de runtime a propósito, es código fuente crudo, consumido por la versión de Astro que ya tenga instalada el sitio que lo llama. Correr un archivo de prueba que importa ese módulo fuera de una app real de Astro significaba que Node no podía resolver la importación en absoluto.

La solución fue agregar astro como devDependency, solo para pruebas. Nunca llega a quienes consumen el paquete, ya que npm no instala las devDependencies de un paquete cuando se depende de él. Pero valía la pena dejarlo explícito antes de tocarlo, porque “sin dependencias” es una restricción real de este repositorio, no solo una descripción de él.

La trampa del formato de fechas

El módulo de i18n tiene una función formatDate, y cualquier forma ingenua de escribir un dato de prueba para ella es una trampa. Construir la fecha de prueba a partir de una cadena ISO, interpretarla como medianoche UTC, y luego formatearla en la zona horaria en la que la prueba resulte estar corriendo, puede desviar el resultado un día entero según dónde esté ubicada la máquina respecto a UTC.

Las fechas de prueba se construyen a partir de componentes locales en lugar de cadenas ISO para el camino de configuración regional por defecto, y el camino del argumento de opciones se fija explícitamente a un timeZone específico para que no pueda desviarse. También se dejó de verificar una cadena exacta para la salida en configuración regional española, ya que el formato ICU para una etiqueta de idioma sin región ha cambiado antes entre versiones de Node. Esa prueba solo verifica las subcadenas que realmente importan.

Se corrió toda la suite bajo dos zonas horarias del sistema radicalmente distintas, UTC y UTC+14, antes de confiar en ella. Ambas limpias.

El error que las pruebas realmente atraparon

Uno de los módulos importa un módulo hermano sin extensión de archivo: from './relatedPosts' en vez de from './relatedPosts.ts'. Vite resuelve eso sin problema, así que funcionó en todos los sitios consumidores sin ningún inconveniente. La resolución de ESM de Node puro no. Lanza ERR_MODULE_NOT_FOUND, punto, sin alternativa.

Nadie había corrido este archivo fuera de un bundler antes. En el momento en que se hizo, que era todo el punto de agregar pruebas, se rompió de inmediato. Una corrección de una sola línea, pero es exactamente el tipo de cosa que “funciona en la app” tapa para siempre.

Lección

El error no estaba en la lógica que se estaba probando. Estaba en la plomería que permite que la lógica corra siquiera fuera de su bundler habitual. Ese es un buen argumento para probar código de librería con el corredor de pruebas más simple posible, en lugar de recurrir a la herramienta que la app consumidora use por casualidad. El corredor simple tiene menos opiniones propias, y menos opiniones significa menos cosas compensando en silencio por detrás.

Lecturas relacionadas