API de casos de ejecución
GET /api/v1/runs/{runUlid}/cases devuelve todos los casos que pertenecen a
una ejecución abierta o cerrada. Se añadieron dos campos como
extensiones aditivas y compatibles con versiones anteriores — las
integraciones existentes que ignoran claves desconocidas no se ven afectadas.
Listar casos de una ejecución
Sección titulada «Listar casos de una ejecución»GET /api/v1/runs/{runUlid}/casesRequiere viewer+ en el proyecto de la ejecución. Devuelve 404 si la
ejecución está fuera de la organización del llamador.
Forma de la respuesta (por elemento)
Sección titulada «Forma de la respuesta (por elemento)»Cada elemento del array cases incluye los campos habituales más:
| Campo | Tipo | Descripción |
|---|---|---|
caseLifecycleStatus | object | null | El estado del ciclo de vida actual (en tiempo real, no del snapshot) del caso fuente. null cuando el campo de estado no está configurado o el caso fuente fue eliminado. |
customFieldValues[].optionSystemKey | string | null | La clave de sistema propia de la opción (p. ej. status_active, status_draft). null para opciones personalizadas definidas por el usuario y para tipos de campo no selectores. |
Objeto caseLifecycleStatus
Sección titulada «Objeto caseLifecycleStatus»Cuando no es null, el objeto tiene la misma forma que un CustomFieldValueEntry:
| Campo | Tipo | Descripción |
|---|---|---|
optionUlid | string | ULID de la opción de ciclo de vida seleccionada |
optionName | string | Nombre visible de la opción, resuelto según el idioma de la solicitud (ver más abajo) |
optionColor | string | null | Color hexadecimal, si existe |
optionIcon | string | null | Identificador de icono, si existe |
optionSystemKey | string | null | Clave de sistema: status_active, status_draft, status_deprecated, o null para opciones personalizadas |
Idioma de la solicitud. optionName se resuelve según el encabezado
Accept-Language de la solicitud: una organización que tradujo los nombres
de sus opciones de estado al español recibe el nombre en español al enviar
Accept-Language: es. Un idioma ausente o no soportado resuelve al nombre en
inglés, así que los integradores que nunca envían el encabezado obtienen
respuestas idénticas byte a byte a las de antes de este comportamiento.
En tiempo real vs. snapshot
Sección titulada «En tiempo real vs. snapshot»El contenido del caso de prueba (título, pasos, descripción) se
congela en un snapshot cuando el caso se añade a la ejecución — los cambios
en el caso original no afectan ninguna ejecución existente. caseLifecycleStatus
es deliberadamente no un snapshot: se resuelve en tiempo real mediante un
join al caso fuente, por lo que siempre refleja el estado actual del caso en el
repositorio. Si necesitas el estado del snapshot, usa el campo priority
(que sí forma parte del snapshot).
Ejemplo
Sección titulada «Ejemplo»curl -sS \ -H "Authorization: Bearer $PROBARA_API_TOKEN" \ "https://app.probara.net/api/v1/runs/01J...RUN/cases"{ "cases": [ { "runCaseUlid": "01J...RC", "caseUlid": "01J...CASE", "caseNumber": 7, "titleSnapshot": "Flujo de pago", "status": "untested", "caseLifecycleStatus": { "optionUlid": "01J...OPT", "optionName": "Borrador", "optionColor": "#F59E0B", "optionIcon": "circle-dashed", "optionSystemKey": "status_draft" }, "customFieldValues": [ { "fieldUlid": "01J...FLD", "fieldName": "Prioridad", "optionUlid": "01J...PRI", "optionName": "Alta", "optionColor": "#EF4444", "optionIcon": null, "optionSystemKey": null } ] } ]}Cuando el caso fuente no tiene estado de ciclo de vida configurado, o el caso fue eliminado:
{ "caseLifecycleStatus": null}Cuando una entrada de customFieldValues pertenece a una opción de sistema (campo de estado del ciclo de vida):
{ "optionSystemKey": "status_active"}Cuando pertenece a una opción personalizada (definida por el usuario):
{ "optionSystemKey": null}Marcar un caso
Sección titulada «Marcar un caso»PATCH /api/v1/runs/{runUlid}/cases/{runCaseUlid}Define assigneeUlid / status / elapsedMs. Un cambio de status marca todos los pasos y
agrega una fila de resultado; el resultUlid de la respuesta lleva el intento agregado (null
en un patch que solo cambia el asignado o el tiempo transcurrido, sin cambiar el estado).
Requiere member+ en el proyecto de la ejecución; viewer recibe 403 forbidden.
Comportamiento en una ejecución cerrada
Sección titulada «Comportamiento en una ejecución cerrada»Permitido en una ejecución abierta o cerrada sin abortar cuando la configuración de
ejecución allowResultsInClosedRuns del proyecto (ver
Configuración de ejecución) resuelve a true (el
valor por defecto del código). Una organización o proyecto que lo fije en false restaura el
comportamiento anterior, más estricto: una ejecución cerrada rechaza el marcado con
409 conflict. Una ejecución abortada siempre lo rechaza, sin importar el valor de esta
configuración.
403 run_case_assignee_locked
Sección titulada «403 run_case_assignee_locked»Cuando la configuración assigneeResultLock del proyecto está activa y el caso tiene un
asignado, solo ese asignado — o un solicitante exento por projects.manage o por ser el
propietario actual del proyecto — puede marcarlo. Cualquier otro solicitante es rechazado,
incluido un token de API: no existe una exención para solicitantes automatizados sobre este
bloqueo.
{ "error": { "code": "run_case_assignee_locked", "message": "Only the assigned tester may record a result for this case" }}422 elapsed_ms_required
Sección titulada «422 elapsed_ms_required»Cuando la configuración timeTracking del proyecto es "required" y la solicitud define un
status terminal sin elapsedMs, el marcado se rechaza y no se persiste nada:
{ "error": { "code": "validation_failed", "message": "elapsedMs is required", "details": { "code": "elapsed_ms_required", "path": ["elapsedMs"] } }}Con "off" u "optional", un elapsedMs provisto se sigue aceptando y persistiendo — la
configuración solo gobierna si se muestra la interfaz de seguimiento de tiempo del cliente web y
si se rechaza un marcado sin elapsedMs, nunca si se descarta un valor provisto.
El marcado en bloque y el envío de resultados en bloque están exentos de este rechazo — ni
POST /runs/{runUlid}/cases/bulk-mark ni POST /runs/{runUlid}/cases/bulk-submit-result llevan
un campo elapsedMs por caso que el servidor pudiera exigir, así que timeTracking: "required"
nunca bloquea ninguno de los dos endpoints en bloque.
Consulta Configuración de ejecución para el vocabulario completo de configuraciones y la cadena de resolución.
Reconciliar la selección de casos de una ejecución
Sección titulada «Reconciliar la selección de casos de una ejecución»PATCH /api/v1/runs/{runUlid}/casesReconcilia la selección de casos de una ejecución a la selección completa
deseada que se envíe, en una sola transacción atómica. A diferencia de
POST /runs/{runUlid}/cases (solo agrega — añade los ULIDs de caso dados y
nunca quita nada), este endpoint trata la ausencia del conjunto deseado como
una eliminación. Ambos endpoints siguen disponibles y no cambian entre sí.
Requiere member+ (el mismo permiso de ejecución que correr los tests);
viewer recibe 403. Permitido en una ejecución abierta o cerrada sin
abortar (completada, o cerrada automáticamente al tener resultado todos sus
casos); rechaza solo una ejecución abortada con 409, sin mutar nada.
Cuerpo de la solicitud
Sección titulada «Cuerpo de la solicitud»{ "caseUlids": ["01J...A", "01J...B", "01J...D"], "caseAssignees": [{ "caseUlid": "01J...D", "assigneeUlid": "01J...USER" }]}| Campo | Tipo | Descripción |
|---|---|---|
caseUlids | string[] | La selección completa deseada (ULIDs de caso fuente). Un array vacío es válido — elimina todos los casos de la ejecución. |
caseAssignees | array (opcional) | Asignado por caso para la selección deseada. Un ULID de caso ausente de este array significa que ese caso queda (o pasa a quedar) sin asignar — no existe un valor explícito de “limpiar”. |
Dada la selección actual y la solicitud anterior, el servidor calcula la diferencia y la aplica, de forma atómica:
- Agrega cada caso de
caseUlidsque aún no está en la ejecución — congelando su título y pasos como un snapshot nuevo,status: "untested", con el asignado provisto (si lo hay) aplicado en la misma operación. - Elimina cada caso actual ausente de
caseUlids— en cascada con sus snapshots de pasos y adjuntos de resultado, igual que el endpoint de un solo casoDELETE /runs/{runUlid}/cases/{runCaseUlid}. - Conserva cada caso presente en ambos, sin cambios (
titleSnapshot,status,assigneeUlid,elapsedMs). - Reasigna cualquier caso conservado cuyo asignado provisto difiera del actual.
La respuesta es el resumen reconciliado de la ejecución (RunResponse), la
misma forma que devuelve cualquier otra mutación de ejecución — no
incluye el array de casos de la ejecución; llama a
GET /runs/{runUlid}/cases para obtener el listado actualizado.
Validación del asignado
Sección titulada «Validación del asignado»Cada assigneeUlid provisto debe resolver a un miembro de la organización de
la ejecución. Un ULID de asignado ajeno a la organización o desconocido
devuelve 404 not_found — igual que el comportamiento existente de la
creación (POST /projects/{projectId}/runs) y del patch por caso
(PATCH /runs/{runUlid}/cases/{runCaseUlid}) para el mismo campo, no un
422 validation_failed. En ese caso no se agrega, elimina ni reasigna ningún
caso.
Eventos de auditoría
Sección titulada «Eventos de auditoría»Emite exactamente un evento por cada caso que realmente cambió:
run.case_added por cada caso agregado, run.case_removed por cada caso
eliminado, run.case_assigned por cada caso cuyo asignado cambió (incluyendo
una limpieza, to: null) — ver API de eventos de auditoría
para las formas compartidas de metadata. Un caso sin cambios tanto en
membresía como en asignado no emite nada. El asignado incorporado de un caso
recién agregado se registra en run.case_added y no emite además un
run.case_assigned.
Ejemplo
Sección titulada «Ejemplo»curl -sS -X PATCH \ -H "Authorization: Bearer $PROBARA_API_TOKEN" \ -H "Content-Type: application/json" \ -d '{"caseUlids": ["01J...A", "01J...D"]}' \ "https://app.probara.net/api/v1/runs/01J...RUN/cases"Límites de solicitud
Sección titulada «Límites de solicitud»Cada uno de los siguientes tres endpoints limita el arreglo caseUlids en el
cuerpo de la solicitud. Son techos contra abuso, no techos de producto — el
servidor procesa cualquier tamaño aceptado sin fallar por límite de
parámetros. Enviar más del límite responde 422 validation_failed nombrando
el campo caseUlids, y no muta nada.
| Endpoint | Campo | Límite |
|---|---|---|
POST /api/v1/runs/{runUlid}/cases (agregar casos) | caseUlids | 500 |
PATCH /api/v1/runs/{runUlid}/cases (reconciliar) | caseUlids | 5000 |
POST /api/v1/projects/{projectId}/runs (crear ejecución) | caseUlids | 5000 |
Relacionado
Sección titulada «Relacionado»- Referencia API — paginación, errores, autenticación
- Referencia interactiva v1 — esquemas completos
- API de búsqueda de casos — búsqueda entre suites con
customFieldValues