BlogEcommerce

Rellenos silenciosos

Un 200 confirmaba un payload incompleto y tres campos faltantes se llenaban con defaults inventados

Nicolás Biondi

Bandeja con casilleros vacíos que una niebla cian va rellenando, bajo un sello de bronce suspendido con luz ámbar.

Mandé un payload incompleto y el proveedor me devolvió un 200 cada vez. Lo descubrí leyendo la salida impresa: un campo mostraba un valor genérico que yo nunca había elegido. El proveedor nunca lo mencionó. Se portó como el de la ventanilla que te sella el formulario incompleto porque él mismo rellenó los casilleros vacíos con lo primero que se le ocurrió. Uno se va contento con su sello hasta que lee lo que escribió.

Un payload es el paquete de datos que un sistema le envía a otro. Al mío le faltaban tres campos, y el código HTTP no distingue entre "tus datos están bien" y "te completé lo que faltaba a mi criterio".

Lo armé probando combinaciones

Cuando construí la integración no tenía ejemplo oficial, así que armé el cuerpo de la petición probando combinaciones y me quedé con la que no fallaba. Mi único criterio de éxito era que la respuesta no explotara, y ese criterio aguanta mucho tiempo sin avisar que es malo.

Meme UNO Draw 25 Cards: Avísame cuando falte un campo Avísame cuando falte un campo

El texto no venía de la plantilla

Al ver ese valor asumí que el proveedor imprimía un texto fijo y que tocaba pedirle un cambio de formato. La teoría duró poco: ese texto salía de un campo que yo nunca mandé y que el proveedor completaba con su propio default.

Tres destinos para un campo que falta

El diff contra el ejemplo oficial fue incómodo: faltaban tres campos y ninguno había dado error nunca. El proveedor trata un campo incompleto de tres maneras: lo ignora, lo rellena con un default o lo rechaza. Las dos primeras terminan en 200.

Payload incompleto

API del proveedor

Ignorado o con default

Campo rechazado

200 OK

Error visible

Ignorar o rellenar con un default terminan en 200; el rechazo es el único camino visible

El único campo que sí me rechazaban es uno de texto libre con formato estricto, que escribe una persona a mano. Su valor por defecto traía un espacio, y el espacio no pasa el filtro. Ese error ruidoso lo arreglé mucho antes, porque era el único que daba error.

Lo que va escrito y lo que omito a propósito

Ahora escribo ese campo en la petición en vez de dejarlo al criterio del proveedor. El identificador de catálogo va en 0 a propósito: es el valor que el ejemplo oficial usa para una línea que no sale del catálogo. El identificador del emisor se lee de la configuración y, cuando no está, lo omito en lugar de mandarlo vacío, porque para una API permisiva un campo vacío es peor que uno ausente: parece una decisión. El campo que escribe la persona se sanea antes de salir, ajustado al formato que exige el filtro.

No armé un validador de esquema propio ni un cliente generado del contrato. El proveedor no publica un esquema formal, así que inventarlo habría significado mantener mi suposición en dos lugares en vez de uno.

Archivábamos el borrador en vez del envío

Guardábamos el borrador del documento en lugar del cuerpo que salía por la red, así que durante toda la investigación leí mi intención en lugar de mi envío. Ahora una función pura reconstruye el body real y eso es lo que archivamos: recibe datos y devuelve datos, sin tocar nada más.

En el camino encontré un test unitario que afirmaba el valor equivocado, en verde desde el primer día, defendiendo el bug con una convicción admirable.

No di por bueno ningún arreglo con la respuesta HTTP: verifiqué cada uno leyendo la salida impresa, el único lugar donde el error era visible para una persona. Sigo sin un esquema formal del contrato, así que ningún payload nuevo sale sin su diff campo por campo contra el ejemplo oficial. El 200 dejó de contar como evidencia, y si el proveedor quiere convencerme de que mi paquete está completo, tendrá que hacerlo con algo más que un sello.

api-contract data-quality postmortem validation