Ir al contenido

API de comentarios en casos de prueba

Los comentarios de casos de prueba se vinculan a un caso específico usando un almacén genérico de comentarios con clave (entity_type='test_case', entity_id), compartido con los comentarios de defectos — ambas superficies son estructuralmente idénticas y solo difieren en la ruta de la entidad. Los comentarios están delimitados a la organización a través del ULID del caso de prueba.

Los comentarios admiten un nivel de respuestas en hilo, fijado y resolución de menciones @. Consulta Adjuntos en comentarios para el flujo de adjuntos de imágenes.

MétodoRutaDescripción
GET/api/v1/test-cases/{caseUlid}/commentsListar comentarios de primer nivel (paginación por cursor)
POST/api/v1/test-cases/{caseUlid}/commentsAgregar un comentario, o una respuesta usando parentUlid
PATCH/api/v1/test-cases/{caseUlid}/comments/{commentUlid}Editar un comentario o respuesta
DELETE/api/v1/test-cases/{caseUlid}/comments/{commentUlid}Eliminar un comentario (elimina en cascada sus respuestas)
POST/api/v1/test-cases/{caseUlid}/comments/{commentUlid}/pinFijar un comentario de primer nivel
DELETE/api/v1/test-cases/{caseUlid}/comments/{commentUlid}/pinDesfijar un comentario de primer nivel

{caseUlid} debe pertenecer a la organización activa. El acceso cruzado entre organizaciones devuelve 404 not_found.

Todos los endpoints requieren una sesión autenticada o un token Bearer con al menos acceso viewer.

OperaciónRol mínimo
GETVisualizador
POST (comentario o respuesta)Miembro
PATCHMiembro (propio comentario únicamente)
DELETEMiembro (propio) o Administrador/Propietario (cualquiera)
POST / DELETE .../pinMiembro (cualquier comentario de primer nivel)
GET/api/v1/test-cases/{caseUlid}/comments

Devuelve comentarios de primer nivel (parentUlid: null) con paginación por cursor.

ParámetroTipoDescripción
cursorstringCursor ULID de la página anterior (nextCursor).
limitintegerTamaño de página. Predeterminado 50, máximo 200.
sortoldest | newestOrden de los comentarios de primer nivel. Predeterminado oldest (sin cambios respecto al comportamiento público previo). Las respuestas siempre se mantienen en orden cronológico bajo su comentario padre, sin importar este valor.
{
"items": [
{
"ulid": "01J…",
"body": "Reproducido en staging con una sesión nueva del navegador.",
"author": {
"ulid": "01J…",
"displayName": "Ada Lovelace",
"avatarKey": null,
"avatarVersion": 0
},
"createdAt": 1700000000000,
"editedAt": null,
"attachments": [],
"parentUlid": null,
"pinnedAt": null,
"pinnedBy": null,
"mentions": [],
"replyCount": 1,
"replies": [
{
"ulid": "01J…RPLY",
"body": "Confirmado — mismo resultado de mi lado. @[01J…GRACE]",
"author": {
"ulid": "01J…",
"displayName": "Grace Hopper",
"avatarKey": null,
"avatarVersion": 0
},
"createdAt": 1700000100000,
"editedAt": null,
"attachments": [],
"parentUlid": "01J…",
"pinnedAt": null,
"pinnedBy": null,
"mentions": [{ "ulid": "01J…GRACE", "displayName": "Grace Hopper" }]
}
]
}
],
"nextCursor": "01J…",
"pinned": []
}
  • author es null cuando el miembro fue removido de la organización.
  • editedAt es null cuando el comentario nunca fue editado.
  • nextCursor es null en la última página.
  • pinned es el conjunto completo de comentarios de primer nivel fijados para este caso de prueba, ordenado por pinnedAt ascendente, independiente del cursor paginado de items — represéntalo por encima del feed. Es idéntico sin importar el valor de sort.
  • Un objeto de respuesta nunca lleva replyCount ni replies — la profundidad del hilo está limitada a un nivel.
  • El arreglo replies de un elemento de primer nivel está en orden cronológico ascendente y limitado a 100 entradas en línea.

Importante: las respuestas no aparecen en la enumeración plana de items[] — solo son accesibles a través de replies[] de su comentario padre. Este es un cambio aditivo pero no compatible byte a byte con clientes que asumían que items[] enumeraba todos los comentarios: itera replies en cada elemento si necesitas el contenido de las respuestas, y usa replyCount si solo necesitas un conteo.

POST/api/v1/test-cases/{caseUlid}/comments
CampoRequeridoNotas
bodyCadena no vacía, máximo 5000 caracteres. Los valores que solo contienen espacios son rechazados. Puede contener tokens de mención @[ulid] (ver Menciones).
parentUlidnoULID de un comentario de primer nivel existente en el mismo caso de prueba. Crea una respuesta de profundidad 1. Omítelo para crear un comentario de primer nivel (comportamiento sin cambios).
attachmentsnoVer Adjuntos en comentarios.

Errores:

  • 422 validation_failedbody está vacío, contiene solo espacios, supera los 5000 caracteres, o parentUlid apunta a un comentario que es en sí mismo una respuesta (responder a una respuesta es rechazado).
  • 404 not_foundparentUlid no resuelve a un comentario de primer nivel en este caso de prueba (entidad incorrecta u organización incorrecta).
  • 403 — rol visualizador.
{
"ulid": "01J…",
"body": "Se ve bien en móvil también.",
"author": { "ulid": "01J…", "displayName": "Ada", "avatarKey": null, "avatarVersion": 0 },
"createdAt": 1700000000000,
"editedAt": null,
"attachments": [],
"parentUlid": null,
"pinnedAt": null,
"pinnedBy": null,
"mentions": [],
"replyCount": 0,
"replies": []
}

Auditoría: emite test_case.commented con metadata.excerpt (primeros 240 caracteres del cuerpo).

PATCH/api/v1/test-cases/{caseUlid}/comments/{commentUlid}

Solo el autor original del comentario puede editarlo (exclusivo del autor, ya sea de primer nivel o respuesta). Los administradores y propietarios no pueden editar el comentario de otro miembro. Las menciones se resuelven de nuevo a partir del cuerpo actualizado en cada edición.

CampoRequeridoNotas
bodyCuerpo de reemplazo, misma validación que POST.
attachmentsnoLista completa de adjuntos deseada; ver Adjuntos en comentarios.

CommentResponse actualizado con editedAt establecido en el timestamp actual y mentions[] reflejando el nuevo cuerpo.

Errores: 403 si no es el autor. 404 si el comentario no existe en este caso (caseUlid incorrecto / org incorrecta).

Auditoría: emite test_case.comment_updated con metadata.excerpt.

DELETE/api/v1/test-cases/{caseUlid}/comments/{commentUlid}

Los autores pueden eliminar su propio comentario. Los administradores y propietarios pueden eliminar cualquier comentario. Eliminar un comentario de primer nivel elimina en cascada todas sus respuestas, purgando los adjuntos de cada respuesta en cascada y reembolsando el almacenamiento antes de eliminar las filas.

Errores: 403 si el solicitante es un miembro que intenta eliminar el comentario de otro miembro.

Auditoría: emite test_case.comment_deleted para el comentario eliminado, más un test_case.comment_deleted adicional por cada respuesta eliminada en cascada.

POST/api/v1/test-cases/{caseUlid}/comments/{commentUlid}/pin

Fija un comentario de primer nivel para que siempre aparezca en el arreglo pinned[] de la respuesta de listado, mostrado antes del feed paginado sin importar sort. Cualquier miembro o rol superior puede fijar cualquier comentario de primer nivel (no solo el propio). No hay límite en la cantidad de comentarios fijados por caso de prueba.

Errores: 422 validation_failed si commentUlid corresponde a una respuesta — solo los comentarios de primer nivel pueden fijarse. 403 para el rol visualizador. 404 si el comentario no existe en este caso.

Comentario actualizado con pinnedAt (epoch ms) y pinnedBy (resumen del actor que lo fijó) establecidos.

Auditoría: emite test_case.comment_pinned.

DELETE/api/v1/test-cases/{caseUlid}/comments/{commentUlid}/pin

Limpia pinnedAt/pinnedBy. Cualquier miembro o rol superior puede desfijar.

Comentario actualizado con pinnedAt: null, pinnedBy: null.

Errores: 403 para el rol visualizador. 404 si el comentario no existe en este caso.

Auditoría: emite test_case.comment_unpinned.

El cuerpo de un comentario puede incluir tokens @[ulid] que referencian a un miembro de la organización por su ULID (la forma ULID, no una cadena de nombre visible, para que renombrar a alguien nunca rompa una mención). Al crear o editar, el servidor:

  1. Extrae cada token @[ulid] del cuerpo.
  2. Resuelve cada token únicamente contra la membresía de la organización activa — un ulid de otra organización, o uno que no resuelve a un miembro, se deja silenciosamente como texto plano sin estilo y no crea ninguna fila de mención.
  3. Persiste una fila por cada mención válida distinta, y reconcilia el conjunto en cada edición (las menciones eliminadas se limpian, las nuevas se agregan).

El arreglo mentions[] de la respuesta siempre refleja el nombre visible actual del miembro mencionado — renombrar a un miembro actualiza toda mención pasada que lo referencie. Las menciones nunca disparan una notificación ni un correo.

Cuando se elimina un caso de prueba (DELETE /api/v1/test-cases/{caseUlid}), todos sus comentarios y respuestas se eliminan primero, junto con sus adjuntos. No se requiere ninguna llamada API adicional; la cascada es atómica dentro del manejador de eliminación.

Ventana de terminal
# Listar la página de comentarios más recientes primero
curl -sS \
-H "Authorization: Bearer $PROBARA_API_TOKEN" \
"https://probara.net/api/v1/test-cases/01J...CASE/comments?sort=newest"
# Agregar un comentario de primer nivel
curl -sS -X POST \
-H "Authorization: Bearer $PROBARA_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"body":"Verificado en Safari 17 — aprueba."}' \
"https://probara.net/api/v1/test-cases/01J...CASE/comments"
# Responder a un comentario de primer nivel, mencionando a un compañero
curl -sS -X POST \
-H "Authorization: Bearer $PROBARA_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"body":"Gracias @[01J...GRACE] — confirmado también de mi lado.","parentUlid":"01J...CMT"}' \
"https://probara.net/api/v1/test-cases/01J...CASE/comments"
# Fijar un comentario
curl -sS -X POST \
-H "Authorization: Bearer $PROBARA_API_TOKEN" \
"https://probara.net/api/v1/test-cases/01J...CASE/comments/01J...CMT/pin"
# Desfijar un comentario
curl -sS -X DELETE \
-H "Authorization: Bearer $PROBARA_API_TOKEN" \
"https://probara.net/api/v1/test-cases/01J...CASE/comments/01J...CMT/pin"
# Editar un comentario
curl -sS -X PATCH \
-H "Authorization: Bearer $PROBARA_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"body":"Nota actualizada luego de re-probar."}' \
"https://probara.net/api/v1/test-cases/01J...CASE/comments/01J...CMT"
# Eliminar un comentario
curl -sS -X DELETE \
-H "Authorization: Bearer $PROBARA_API_TOKEN" \
"https://probara.net/api/v1/test-cases/01J...CASE/comments/01J...CMT"