Implement asynchronous bumper rendering and add integration tests: refactor BumperAsset to support async rendering, introduce BumperRenderBackgroundService for processing, and create TeleWave.Integration.Tests with Testcontainers-Postgres for comprehensive testing of scheduling and bumper generation scenarios. Update documentation and ensure tests skip if Docker is not available.

This commit is contained in:
Leonid Pershin
2026-07-25 23:42:40 +03:00
parent 6c18a9da79
commit d6ad8e11a4
22 changed files with 1967 additions and 197 deletions
+41 -11
View File
@@ -42,7 +42,7 @@
### Планировщик / медиа
- [x] **M8. Нет таймаута на ffmpeg/ffprobe**`ProcessRunner.cs` + `MediaOptions` (ffprobe 120с, транскод/рендер 1800с, конфигурируемо).
- [x] **M9. Гонка при конкурентной генерации расписания канала**`ScheduleGenerator.cs` (транзакция + `pg_advisory_xact_lock` по каналу).
- [~] **M10. Рендер заставок синхронно внутри тика планировщика**`ScheduleBumperResolver.cs:174`. _(частично: M8 ограничивает худший случай, кэш делает рендеры редкими, M9 сериализует канал. Полный вынос рендера из тика — отдельная архитектурная задача, флажок ниже.)_
- [x] **M10. Рендер заставок синхронно внутри тика планировщика**вынесен в асинхронную очередь: планировщик создаёт ассет заставки в Pending + кэш-строку и ставит в очередь, ffmpeg крутит новый `BumperRenderBackgroundService` вне тика/транзакции. До готовности плейлист отдаёт филлер.
- [x] **M11. Override через полночь не работает**`SchedulePlannerModels.cs:40` (`EndMinute <= StartMinute` → пустое окно).
### Фронтенд
@@ -66,7 +66,7 @@
- [x] **L9. `AllowedHosts: "*"` и dev-креды БД в appsettings.json** — осознанные dev-дефолты, переопределяются env в проде; C2 теперь форсит реальный JWT-ключ. Оставлено как задокументированное.
- [x] **L10. SSRF-поверхность в ImageDownloader** (без allowlist схемы/хоста) — `ImageDownloader.cs:16` (allowlist http/https).
- [x] **L11. Документация (CLAUDE.md/README) отстала** — обновлены разделы «текущее состояние» и рабочий принцип под реальный объём.
- [~] **L12. Пробелы в тестах**частично: добавлены тесты на M11 (override через полночь), L3 (Single). Полное покрытие фоновых сервисов/оркестрации/эндпоинтов — отдельная задача.
- [x] **L12. Пробелы в тестах**добавлен проект `TeleWave.Integration.Tests` на Testcontainers-Postgres: генерация расписания, M10 (Pending-заставки + очередь), M9 (advisory-lock против гонки), M7 (откат Register), M5 (транзакция DeleteShowMedia). При отсутствии Docker тесты пропускаются (SkippableFact). Плюс unit-тесты на M11/L3.
---
@@ -166,13 +166,43 @@ Api:EnableOpenApi неявно). Тесты: +2 domain (override через по
---
## Осталось (флажки на будущее — требуют отдельного решения)
## Журнал — M10 (async-рендер заставок) и L12 (интеграционные тесты)
- **M10 (полностью)** — вынести рендер ТВ-заставок из тика планировщика: генерировать расписание с
плейсхолдерами и рендерить ассеты асинхронно, чтобы один канал не задерживал достройку остальных и
чтобы не держать транзакцию/advisory-lock во время ffmpeg.
- **L12 (полностью)** — интеграционные тесты фоновых сервисов (media-конвейер, планировщик),
оркестрации `ScheduleGenerator`, эндпоинтов (нужен реальный/контейнерный Postgres — InMemory не
тянет транзакции/advisory-lock/raw SQL).
- **M15 (доменно)** — если нужны разные файлы с одинаковым именем: пересмотреть уникальность
`MediaAsset` (сейчас по `OriginalFileName`).
### 2026-07-25 — M10: асинхронная очередь рендера заставок
- **Домен**: `BumperAsset` получил `ChannelId`/`TemplateId`/`VariantId` (чтобы фоновый рендерер
восстановил спецификацию), фабрика обновлена; миграция `BumperAssetRenderContext` (Guid-колонки с
дефолтом для существующих строк — самовосстановление при перерендере). `MediaAsset.RegisterGenerated`
теперь честно Pending до готовности.
- **Очередь**: `IBumperRenderQueue` + `BumperRenderQueue` (сигнал поверх Channel, как у медиа).
- **`ScheduleBumperResolver`**: больше не рендерит — создаёт Pending-ассет (Source=Generated) + кэш-строку
и ставит в очередь; переиспользует Ready/Pending/Processing, перерендерит только Failed (по новейшей
кэш-строке). Тик планировщика и его транзакция больше не держатся во время ffmpeg (снимает и остаток M9).
- **`BumperRenderBackgroundService`**: последовательно берёт Pending Generated → восстанавливает спеку
(`BumperSpecFactory`) → ffmpeg → MarkReady/MarkFailed; сброс прерванных на старте; idle-poll 30с.
- **`MediaProcessingBackgroundService`**: Generated-ассеты исключены из claim/reset (их обслуживает
рендерер заставок).
- **Стриминг менять не пришлось**: `GetLivePlaylistQueryHandler` уже фильтрует по `Status==Ready`
Pending-заставка автоматически деградирует в филлер, пока рендерится.
- **Поведение**: заставка перехода впервые появляется на тик позже (до этого — филлер); дальше берётся
из кэша. Осознанный компромисс (выбран владельцем).
### 2026-07-25 — L12: интеграционные тесты на Testcontainers-Postgres
Новый проект `tests/TeleWave.Integration.Tests``.slnx`): поднимает `postgres:16-alpine`, применяет
реальные миграции, гоняет то, что InMemory не умеет (транзакции, advisory-lock, ExecuteDelete). 5 тестов:
генерация Program-расписания; создание Pending-заставок (Generated) + постановка в очередь (M10);
сериализация конкурентной генерации advisory-lock'ом без перекрытий (M9); откат Register при сбое
переноса файла (M7); атомарное удаление в DeleteShowMedia (M5). При отсутствии Docker — `SkippableFact`
пропускает (CI на раннере без Docker не падает; `dotnet test TeleWave.slnx` в CI прогонит их при наличии
Docker на ubuntu-latest).
Проверка: сборка решения 0/0; **98 domain + 49 application + 5 integration** зелёные; фронт `tsc`/`oxlint`
чистые.
---
## Осталось (доменное решение владельца, не дефект)
- **M15 (доменно)** — если понадобятся разные файлы с одинаковым именем: пересмотреть уникальность
`MediaAsset` (сейчас по `OriginalFileName` — намеренно, клиент это зеркалит).