# Entorno de prueba

Las claves de prueba (`ruta_test_…`) trabajan en un **espacio aislado** de tu empresa. Sirven para integrar y probar sin ensuciar la operación del courier.

## Qué cambia y qué no

| | Real (producción) | De prueba |
| --- | --- | --- |
| Endpoints, cuerpos y errores | Iguales | Iguales |
| Validaciones (texto, teléfono, zona, comercio, programación, cobro) y funciones del plan | Sí | Sí, las mismas |
| Cupo mensual del plan | Cuenta | **No cuenta** |
| Aparece en el panel, la app del repartidor, el seguimiento y los reportes | Sí | **Nunca** |
| Avisos (webhooks) | A webhooks reales | Sólo a webhooks **De prueba** |
| `test` en cada entrega y cada aviso | `false` | `true` |

Además:

- Una clave de prueba **no ve** entregas reales y una clave de producción **no ve** entregas de prueba (`404`).
- `trackingUrl` tiene la forma de siempre (`…/track/test-…`) pero no abre una página real.
- El comprobante es una imagen de muestra y la etiqueta PDF lleva el sello «PRUEBA - SIN VALIDEZ».
- Hasta 1000 entregas de prueba por empresa; se borran solas a los 30 días.
- Las zonas (`GET /api/v1/zones`) son las reales de la empresa.

## Simular el recorrido

`POST /api/v1/deliveries/{id}/simulate` (permiso `deliveries:write`, **sólo claves de prueba**; con una clave de producción responde `403 TEST_KEY_REQUIRED`) avanza la entrega como lo haría el actor real:

| `action` | Desde | Hasta | Avisos |
| --- | --- | --- | --- |
| `ASSIGN` | `CREATED` | `ASSIGNED` | `delivery.assigned` |
| `PICK_UP` | `ASSIGNED`, `OUT_FOR_DELIVERY` (una vez por intento) | sin cambio de estado | `delivery.picked_up` |
| `START` | `ASSIGNED` | `OUT_FOR_DELIVERY` | `delivery.out_for_delivery` |
| `DELIVER` | `OUT_FOR_DELIVERY` | `DELIVERED` (con comprobante de muestra; si había monto a cobrar, se declara cobrado entero) | `delivery.delivered`, `delivery.proof_added` |
| `FAIL` | `OUT_FOR_DELIVERY` | `FAILED` | `delivery.failed` |
| `RETRY` | `FAILED` | `CREATED` con `attemptNumber` + 1 | `delivery.retry_scheduled` |
| `SETTLE` | `DELIVERED` con cobro | sin cambio de estado (`settledAt`) | `delivery.collection_settled` |

Cada cambio de estado emite también `delivery.status_changed`. Un paso que el estado no permite responde `409 DELIVERY_STATE_INVALID`.

Opcionales: `failureReasonCode` sólo con `FAIL` (`RECIPIENT_ABSENT` por defecto; también `ADDRESS_NOT_FOUND`, `ACCESS_UNAVAILABLE`, `RECIPIENT_REFUSED`, `UNSAFE_LOCATION`, `PACKAGE_ISSUE`, `OTHER`) y `collectionMethod` sólo con `DELIVER` (`CASH` por defecto, `TRANSFER`, `OTHER`).

```bash
curl -X POST "$RUTA_API/api/v1/deliveries/$ID/simulate" \
  -H "Authorization: Bearer $RUTA_TEST_KEY" -H "Content-Type: application/json" \
  -d '{"action": "FAIL", "failureReasonCode": "ADDRESS_NOT_FOUND"}'
```

Responde `200` con la entrega actualizada.
