Вложения тест-кейсов
Вложения 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; уже загруженные файлы остаются в исходном бэкенде.
Пайплайн загрузки
- Клиент POST-ит
multipart/form-dataс полемfileна/api/v1/namespaces/:ns/tcm/attachments/upload?parentKind=<kind>&parentId=<id>. - Сервер сразу спулит тело на диск под
io.LimitReader(по умолчанию 25 MiB; +1 байт позволяет различить «ровно у лимита» и «превышено»). - Предварительная проверка квоты по реально записанным байтам (не по заявленному
Content-Length). - Image pipeline: PNG ≥ 512 KiB пере-кодируется в JPEG q=85; для любого изображения создаётся thumbnail 256×256.
- Бэкенд пишет финальный блоб и метаданные.
- Ответ —
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 клиенты загружают байты
напрямую в объектное хранилище — минуя админ-ноду:
POST /tcm/attachments/presignсsha256+sizeфайла → presignedPUT-URL.PUTбайт по этому URL (клиент стримит с диска; есть ретраи).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-телом, а не выкинет
результат.