Traducción de
docs/en/runtime/scenarios.md, que es el original. Si discrepan, manda el inglés.
JSON es el canon. El emparejado de escenarios es determinista y admite proveedor, operación, límites de importe y metadatos exactos. JavaScript arbitrario está prohibido.
Los escenarios propios viven sólo como ficheros canónicos en
scenarios/<proveedor>/*.json. npm run scenario:compile valida el DSL seguro y
genera apps/api/src/modules/scenarios/default-scenarios.ts como representación
de ejecución. npm run build, npm run typecheck y npm test corren el
compilador antes, lo que impide que el JSON y el comportamiento se desvíen en
silencio.
Los escenarios a medida se persisten con POST /scenarios y
{ "key": "equipo/rechazo-importe-alto", "definition": { ... } }. La respuesta
devuelve una referencia de estilo inmutable, como
equipo/rechazo-importe-alto@1. Las sesiones a medida tienen que usar la
referencia con @versión explícita, para que una versión posterior no cambie en
silencio una prueba que ya existía.
El DSL admite a propósito un conjunto pequeño de reglas deterministas. Las claves
de acción desconocidas se rechazan; no hay JavaScript, ni eval, ni ejecución de
funciones. Compilar o cachear en Redis es una optimización posterior de
rendimiento, no la fuente de la verdad.
#then.after — un pago que se mueve solo
{
"provider": "mercadopago",
"version": 1,
"when": { "operation": "payment.create" },
"then": {
"outcome": "requires_action",
"after": { "seconds": 5, "outcome": "captured" },
"webhook": { "enabled": true }
}
}
Un ticket pagado en el mostrador de una tienda, una transferencia que compensa por la noche, una cartera que redirige y vuelve. En todos ellos el pago cambia mientras la integración no hace nada, y a la integración se lo cuenta un webhook — que es el caso que más fácilmente se implementa mal y el más difícil de reproducir contra un proveedor real, porque no hay petición que puedas mandar para que alguien entre en una tienda.
| Regla | Por qué |
|---|---|
Sólo sobre un pago que acaba en requires_action |
Es el único estado en que espera a alguien que no es la integración. En los demás quien actúa es la integración, y un temporizador moviéndolo sería el emulador actuando en su nombre |
outcome es captured o failed |
Un pago que espera puede completarse o caducar. Devolver o disputar con un temporizador sería el emulador haciendo una operación que es de la integración |
seconds entre 1 y 3600 |
Un escenario es algo que una prueba espera; uno que dispara mañana es una fila que nadie verá moverse |
El dinero siempre es external |
No entró nadie, y un ticket pagado en una tienda es efectivo llegando del mundo. Debitar un saldo sería inventarse un pagador |
El worker lo barre con un reloj. El plano de control puede correr el barrido a
demanda — POST /applications/{id}/payments/run-timed-transitions, con un asOf
opcional — que es lo que permite a una prueba observar un ticket de cinco minutos
sin esperar cinco minutos. La misma razón por la que run-settlement existe a su
lado.
Vienen tres: mercadopago/ticket-paid-later, mercadopago/ticket-expired y
stripe/bank-transfer-clears.
#Carencia actual del DSL
then.httpStatus y then.fault.once siguen existiendo en la forma TypeScript de
un escenario, pero GatewayService no los consume. No uses ninguno de los dos
en un escenario nuevo hasta que haya comportamiento y pruebas. Para un fallo
HTTP, usa then.fault: { "type": "http_error", "status": 429 }; ése es el camino
implementado.