Документация Вложения тест-кейсов

Вложения тест-кейсов

Вложения TCM — файлы, привязанные к кейсу, шагу или запуску: обычно скриншоты, трассы,
HAR-файлы или PDF. Загрузки стримятся прямо в выбранный blob-бэкенд без буферизации всего
payload в памяти, а per-namespace квота делает потребление хранилища предсказуемым.

Об URL в примерах: во всех примерах используется localhost:5770 как адрес Mockarty по умолчанию. Если ваш инстанс работает на удалённом сервере, замените localhost:5770 на его реальный адрес. Подробнее — в разделе Полезные функции и советы.

Смежные страницы: Управление тест-кейсами · Рабочий процесс ревью

Бэкенды хранилища

Выбирается через MOCKARTY_BLOB_BACKEND:

  • fs (по умолчанию) — локальная ФС. Обязательно: MOCKARTY_BLOB_FS_ROOT. Запись атомарна (.tmp + fsync + rename). Раскладка: <root>/<namespace>/<shard-a>/<shard-b>/<sha256>; рядом meta/<sha256>.json с метаданными.
  • s3 — любой S3-совместимый bucket (AWS S3, MinIO, Yandex Object Storage, VK Cloud, Cloud.ru). Обязательно: MOCKARTY_BLOB_S3_ENDPOINT, MOCKARTY_BLOB_S3_BUCKET. Опционально: MOCKARTY_BLOB_S3_REGION, MOCKARTY_BLOB_S3_ACCESS_KEY, MOCKARTY_BLOB_S3_SECRET_KEY, MOCKARTY_BLOB_S3_PREFIX, MOCKARTY_BLOB_S3_USE_SSL (по умолчанию true).

Оба бэкенда реализуют один и тот же интерфейс — переход с fs на s3 требует только
перезапуска и правок env; уже загруженные файлы остаются в исходном бэкенде.

Пайплайн загрузки

  1. Клиент POST-ит multipart/form-data с полем file на /api/v1/namespaces/:ns/tcm/attachments/upload?parentKind=<kind>&parentId=<id>.
  2. Сервер сразу спулит тело на диск под io.LimitReader (по умолчанию 25 MiB; +1 байт позволяет различить «ровно у лимита» и «превышено»).
  3. Предварительная проверка квоты по реально записанным байтам (не по заявленному Content-Length).
  4. Image pipeline: PNG ≥ 512 KiB пере-кодируется в JPEG q=85; для любого изображения создаётся thumbnail 256×256.
  5. Бэкенд пишет финальный блоб и метаданные.
  6. Ответ — Attachment с UUID, MIME, размером, sha256 и storage key.

Пиковое потребление RAM на одну загрузку ограничено working set image-декодера, а не
размером payload — 1 000 параллельных загрузок по 25 MiB не означают 75 GiB в памяти.

Parent kinds

parentKind Куда прикреплено
tcm_case Кейс (общее на все версии — откат к v3 видит вложения v5).
tcm_case_step Один шаг (per-version; rollback возвращает исходные вложения шага).
tcm_case_run Артефакт запуска (логи, финальные отчёты).
tcm_case_run_step Evidence шага, зафиксированный при резолве.
review_comment Файл, приложенный к комментарию ревью.

Эндпоинты

Метод Путь Назначение
POST /tcm/attachments/upload?parentKind=&parentId= Multipart-загрузка.
GET /tcm/attachments?parentKind=&parentId= Список с пагинацией.
GET /tcm/attachments/:id Только метаданные.
GET /tcm/attachments/:id/raw Стриминг тела. RFC 6266 Content-Disposition с filename*=UTF-8''….
GET /tcm/attachments/:id/thumb Thumbnail 256×256 JPEG (404, если источник — не картинка).
GET /tcm/attachments/:id/view Inline-показ для просмотрщика отчётов. На бэкенде S3/MinIO делает 302-redirect на короткоживущий presigned-URL — байты (и range/seek-запросы видео) идут прямо из объектного хранилища; на файловом бэкенде стримит тело. Inline-рендер только для картинок, видео, аудио, PDF и текста — всё прочее (включая SVG/HTML) отдаётся на скачивание.
POST /tcm/attachments/presign Запросить presigned загрузку напрямую в хранилище для крупного артефакта (видео, полноэкранный скриншот, trace-бандл). Тело: {parentKind, parentId, filename, contentType, sha256, size}. Возвращает {uploadUrl, uri, method, expiresAt, maxBytes}. На файловом бэкенде (presign невозможен) — 501 с {"fallback":"/upload"}, клиент переходит на multipart /upload.
POST /tcm/attachments/confirm Записать метаданные после PUT байт по presigned-URL. Тело: {parentKind, parentId, filename, contentType, sha256}. Путь в хранилище выводится на сервере из (namespace, sha256) — URI от клиента не доверяется; 409, если байты ещё не загружены.
PUT /tcm/attachments/:id/content Перезаписать содержимое вложения на месте (тело запроса — новое содержимое; тот же id, ссылки остаются рабочими). Текстовые типы (text/plain, Markdown, JSON, XML, CSV, YAML, HTML) редактируются из текстового просмотрщика (лимит 5 MiB). Картинки заменяются аннотированной версией (Content-Type: image/png, лимит 15 MiB) — у строки обновляются media type + размеры под аннотированный PNG. Прочие типы → 415.
PATCH /tcm/attachments/:id Переименование (display-only). Тело: {"originalName": "..."}. Ключ в хранилище, MIME, размеры и существующие /raw / /thumb URL остаются валидны.
DELETE /tcm/attachments/:id Soft-delete; asynchronous cleanup удаляет тело после истечения retention.

Просмотр и редактирование в UI

Клик по вложению в кейсе или отчёте о запуске открывает его в модалке
предпросмотра — без скачивания и без ухода со страницы:

  • Картинки, видео, аудио, PDF показываются inline. На бэкенде S3/MinIO плеер
    стримит прямо из объектного хранилища (перемотка видео работает) — админ-нода
    не проксирует байты.
  • Markdown рендерится форматированным; текст, логи, JSON, CSV, XML, YAML
    — в моноширинном просмотрщике.
  • Word (.docx) и Excel (.xlsx) показываются только для чтения (Word —
    форматированным HTML, Excel — таблицами по листам). Изменить их можно только
    повторной загрузкой отредактированного файла.
  • Текстовые артефакты редактируются на месте. Откройте текст/Markdown/JSON/
    CSV/… вложение, нажмите Редактировать, измените содержимое и Сохранить —
    то же вложение обновляется без повторной загрузки, ссылки остаются рабочими.
  • Скриншоты можно аннотировать. Откройте картинку, нажмите Аннотировать и
    рисуйте стрелки, прямоугольники, эллипсы, выделение, карандашом или текстом.
    Сохранить копию оставляет оригинал и добавляет версию с пометками;
    Заменить оригинал перезаписывает то же вложение. Аннотированные скриншоты
    багов затем попадают в отчёт о запуске, который вы отправляете разработчикам.
    (На бэкенде S3/MinIO бакету нужен настроенный CORS, чтобы редактор смог
    прочитать байты картинки; файловый бэкенд same-origin и не требует настройки.)
  • Большие файлы (свыше 15 MiB) показывают кнопку скачивания вместо inline-показа,
    чтобы браузер оставался отзывчивым.

Загрузка напрямую в хранилище (крупные артефакты)

Для больших файлов (записи, видео) на бэкенде S3/MinIO клиенты загружают байты
напрямую в объектное хранилище — минуя админ-ноду:

  1. POST /tcm/attachments/presign с sha256 + size файла → presigned PUT-URL.
  2. PUT байт по этому URL (клиент стримит с диска; есть ретраи).
  3. POST /tcm/attachments/confirm — записать вложение.

mockarty-cli attachments upload <file> --parent-kind … --parent-id … и раннеры
используют этот путь автоматически, откатываясь на multipart /upload, когда
бэкенд — файловая система.

Квоты и лимиты

  • Per-namespace квота: лимит хранилища на namespace, настраивается админом в Settings → Storage → Attachments. При включённом жёстком ограничении загрузки выше лимита возвращают 409 Conflict.
  • Ошибки скана: публикуются как событие tcm.attachment.scan_failed и фиксируются в аудит-логе; блоб не сохраняется.
  • MIME-whitelist: по умолчанию PNG, JPEG, WebP, GIF, PDF, JSON, XML, plain text, CSV, ZIP, HAR, application/octet-stream. Расширяется через конфигурацию адаптера. Не-whitelisted MIME → 415 Unsupported Media Type.

Удаление и retention

  • Soft-delete фиксирует, кто удалил вложение, когда и почему. Метаданные хранятся в рамках retention — восстановление возможно.
  • Тело блоба удаляется асинхронно; незавершённые удаления тел доводятся до конца при graceful shutdown до выхода процесса.
  • Legal-hold блокирует hard-delete пока не снят, даже после истечения retention.

Air-gapped

Image pipeline — чистый Go без внешних кодеков. S3-бэкенд использует minio-go без
С-зависимостей, поэтому тот же контейнер запускается air-gapped против MinIO / Yandex
Object Storage. Единственный компонент с выходом наружу — опциональный антивирусный сканер
оставить вложения полностью offline.

Offload вложений external-run

CI-приёмник /api/v1/namespaces/:ns/tcm/external-runs принимает inline base64-вложения
вместе с каждым запуском. Маленькие payload’ы (логи, JSON-отчёты) остаются inline,
чтобы контекст кейса был самодостаточным; крупные скриншоты или HAR’ы могут раздуть
case_context_json за глубинный лимит JSON в Postgres и замедлить листинги case-run’ов.

Переключение inline → blob offload — переменной MOCKARTY_EXTERNAL_RUN_BLOB:

Значение Поведение
(не задано) или inline Хранить вложения inline (по умолчанию). Не требует blob-бэкенда. Подходит для одноноды и малых команд.
auto / fs / s3 Перенаправлять каждое inline-вложение в сконфигурированный blob-бэкенд (MOCKARTY_BLOB_BACKEND) и заменять тело на ссылку blobUri. Подсказка (fs / s3) — для читабельности конфига; фактический бэкенд выбирается через MOCKARTY_BLOB_BACKEND.

При включённом offload в строке запуска лежит только URI; UI забирает артефакт по
требованию через /tcm/attachments/:id/raw. Inline остаётся fallback’ом — если бэкенд
временно недоступен, upload всё равно запишет запуск с inline-телом, а не выкинет
результат.