Pruebas de aceptación de TaskFlow#
Este documento describe el examen. El examen en sí es ejecutable y vive en acceptance.test.mjs: 20 casos que solo hablan HTTP y se lanzan sin modificación contra cualquier implementación, en cualquier lenguaje.
node scripts/run-acceptance.mjs # lista los destinos
node scripts/run-acceptance.mjs reference-node # referencia sin framework
node scripts/run-acceptance.mjs express --prepare # instala y prueba
node scripts/run-acceptance.mjs --url http://host:puertoSi una implementación necesita que se cambie una de estas pruebas para pasar, la comparación entre ecosistemas deja de significar nada. Cambiar el examen para que apruebe un candidato es la falta crítica del programa.
Los 20 casos#
| # | Caso | Petición | Respuesta esperada |
|---|---|---|---|
| 1 | Salud | GET /health |
200 y {"status":"ok"} |
| 2 | Colección | GET /tasks |
200 con items como arreglo |
| 3 | Crear | POST /tasks válido con clave |
201, cabecera Location, tarea con id, title, completed:false, createdAt |
| 4 | Recorte | título con espacios alrededor | el título se guarda recortado |
| 5 | Recuperar | GET sobre el Location devuelto |
200 con la misma tarea |
| 6 | Idempotencia | repetir cuerpo y clave | 200, mismo id, una sola tarea en la colección |
| 7 | Conflicto | misma clave, cuerpo distinto | 409 IDEMPOTENCY_KEY_REUSED |
| 8 | Sin clave | POST sin Idempotency-Key |
400 IDEMPOTENCY_KEY_REQUIRED |
| 9 | Clave en blanco | clave con solo espacios | 400 IDEMPOTENCY_KEY_REQUIRED |
| 10 | Título vacío | title con solo espacios |
422 con errors[].field == "title" y code == "TITLE_EMPTY" |
| 11 | Título ausente | cuerpo {} |
422 con TITLE_REQUIRED |
| 12 | Título no textual | {"title": 42} |
422 con TITLE_REQUIRED |
| 13 | Título largo | 121 caracteres | 422 con TITLE_TOO_LONG |
| 14 | Límite inclusivo | 120 caracteres exactos | 201 |
| 15 | JSON roto | cuerpo no analizable | 400 MALFORMED_JSON, nunca 500 |
| 16 | Tipo de contenido | Content-Type: text/plain |
415 UNSUPPORTED_MEDIA_TYPE |
| 17 | Cuerpo grande | por encima de 64 KiB | 413 BODY_TOO_LARGE y el servicio sigue sano |
| 18 | Tarea ausente | GET /tasks/{id} desconocido |
404 TASK_NOT_FOUND |
| 19 | Ruta ausente | ruta que no existe | 404 ROUTE_NOT_FOUND |
| 20 | Método | DELETE /tasks |
405 METHOD_NOT_ALLOWED con cabecera Allow |
Comprobación transversal de todo error#
Cada respuesta de error de la tabla pasa además por la misma verificación. Un error con el código correcto y el sobre equivocado también incumple:
- viaja como
application/problem+json; - lleva los cuatro miembros obligatorios:
type,title,status,code; typees un URI resoluble ystatuscoincide con el código HTTP;- no filtra el interior: ni traza, ni
node_modules, ni rutas del sistema de archivos, ni consultas SQL.
El punto 4 se comprueba por expresión regular sobre el cuerpo serializado. Es la prueba que sobrevive a un cambio de framework: da igual qué lo genere, si aparece una traza en la respuesta, falla.
Por qué errors[] por campo#
Un 422 que solo dice «datos inválidos» impide construir una interfaz accesible: sin saber qué campo falló, el formulario no puede asociar el mensaje al control correspondiente ni mover el foco hasta él. Por eso el contrato lo exige y por eso hay cuatro casos (10 a 13) dedicados a comprobarlo.
Desviaciones declaradas#
Una implementación puede cumplir el contrato con un mecanismo distinto, siempre que lo declare. Las conocidas hoy:
| Implementación | Desviación | Efecto |
|---|---|---|
| Spring Boot | El límite de 64 KiB se comprueba después de que el contenedor haya leído el cuerpo | Mismo código de respuesta; menor protección de memoria que la referencia |
Una desviación declarada es información útil sobre el ecosistema. Una desviación silenciosa es un fallo de la comparación.