Quality upgrades, batch covers, staging preview, and resume after reload are in the queue. Loudness and crossfade work on web and Android, and the documented checks now have suites. Co-authored-by: Cursor <cursoragent@cursor.com>
8.2 KiB
Тестирование
Пирамида
| Уровень | Где | Инструменты | Что покрываем |
|---|---|---|---|
| Unit | Go | testing, testify, табличные тесты |
Политики прав (вся матрица из auth-and-roles.md), парсинг шаблонов путей и слияние с тегами, нормализация имён, подпись ссылок, расчёт implicit_score, рендер write_template и очистка имён, обратимость шаблон ↔ регэксп, учёт квоты при конкурентных резервах, переходы статусов загрузки |
| Unit | Python | pytest, hypothesis |
Формулы скоринга, context_fit, MMR, бандит, обновление сессионного вектора, правила переранжирования |
| Unit | TS | vitest |
packages/core: очередь, события (подсчёт played_ms, засчитанное прослушивание), single-flight refresh, офлайн-очередь |
| Компоненты | Web | vitest + Testing Library + axe-core |
Поиск, плеер, волна, формы админки, вход; серьёзные нарушения axe на главной и входе |
| Компоненты | Mobile | vitest (apps/mobile/test) |
Офлайн-волна и решение диалога обновления |
| Интеграция | Go | testcontainers-go (Postgres + pgvector, Redis, Meilisearch) |
Репозитории, миграции, сканер, API через httptest; TestUserJourney — сценарии 1–2 и скан |
| Интеграция | Python | testcontainers |
Чтение событий из Redis Stream, kNN в pgvector, ночной пайплайн на синтетических данных |
| Контракт | — | tests/contract/schemathesis.sh |
Ответы соответствуют openapi.yaml; нужен запущенный api |
| E2E | Web | Playwright (apps/web/e2e, Chromium, pnpm --filter @pipu/web test:e2e) |
Сценарии 1, 4 и 9 на фейковом API |
| E2E | Mobile | Maestro (apps/mobile/.maestro) |
Вход, коллекция, проверка обновления. Ночной прогон (PIPU_E2E_SERVER, PIPU_E2E_USER, PIPU_E2E_PASSWORD), не на каждый PR |
| Качество рекомендаций | Python | офлайн-оценка | NDCG / Recall на синтетическом датасете с известными «вкусами»; регрессия качества ломает тест |
Тестовая музыка
Реальную музыку в репозиторий не кладём. tests/fixtures/music/generate.sh генерирует через ffmpeg
~200 коротких (5–20 с) файлов разных форматов (FLAC, MP3, Opus, M4A) с синусами / шумом разного темпа и громкости,
с полными, частичными и отсутствующими тегами, в нескольких структурах папок — для проверки шаблонов,
дублей, переименований, кириллицы в путях. Генерация детерминирована (фиксированные параметры), результат кэшируется в CI.
Для аудиоанализа — отдельный набор из нескольких клипов с известным BPM (метроном 90 / 128 / 170).
E2E-сценарии (обязательные)
Сценарии 1, 4 и 9 гоняет Playwright. Сценарии входа, ролей, поиска, плеера, волны и языка — apps/web/src/App.test.tsx.
Сценарии источника и скана — TestUserJourney в services/api (нужен TEST_DATABASE_URL).
- Первый запуск: вход bootstrap-админа → принудительная смена логина / пароля → главная.
- Админ создаёт модератора; модератор создаёт пользователя; модератор не может создать модератора (UI и API).
- Админ добавляет
local-источник, задаёт два шаблона, предпросмотр, скан → треки появились в поиске. - Поиск: выпадающий список → переход к исполнителю; Enter → страница поиска.
- Воспроизведение: запуск трека, перемотка, перезагрузка страницы → трек и позиция восстановлены.
- Моя волна: запуск с занятием «Бегаю» → в выдаче треки с BPM из профиля; дизлайк → следующий трек, трек в «Дизлайках».
- Коллекция: лайк → в «Лайках»; история содержит прослушанное.
- Смена пароля пользователем → старые сессии разлогинены.
- Переключение языка ru ↔ en.
- Сброс пароля пользователю → обязательная смена при входе.
- Очередь на добавление: модератор загружает папку из 5 файлов, где 1 — точная копия трека из библиотеки,
1 — тот же трек в другом формате → первый отброшен на предпроверке, второй — по отпечатку; оба не учтены в квоте;
оставшиеся 3 —
pending_review. Админ правит альбом, одобряет → файлы появились в хранилище поwrite_template, трек найден поиском; квота освобождена. - Квота: лимит 50 МБ, загрузка 80 МБ → часть файлов отклонена
uploads.queue_full; модератор не может одобрить собственную загрузку, а обычный пользователь — открыть загрузку вообще (UI и API).
Правила
- Покрытие: Go и Python ≥ 70% по строкам для
internal/*/serviceиpipu_rec/*; дляauth/policy— 100% веток. - Тест на каждый исправленный баг.
- Flaky-тесты помечаются и чинятся в течение недели, а не перезапускаются бесконечно.
- Нагрузочный smoke (k6, вручную перед релизом) — см. ниже.
Нагрузочный smoke
tests/load/smoke.js (k6 ≥ 0.50), 3 минуты: 100 одновременных слушателей волны (старт волны, начало потока
каждого трека — первые 64 КБ, события прослушивания, следующая пачка с отзывами) и 20 запросов подсказок поиска
в секунду. Пороги — цели из api.md и recommendations.md:
| Запрос | p95 |
|---|---|
GET /search/suggest |
< 50 мс |
POST /wave/sessions/{id}/next |
< 150 мс |
POST /wave/sessions |
< 300 мс |
начало потока (Range: bytes=0-65535) |
< 300 мс |
POST /events |
< 100 мс |
Ошибок < 1%. k6 завершается с ненулевым кодом, если порог не выполнен.
k6 run -e BASE_URL=https://music.example.com -e PIPU_USER=loadtest -e PIPU_PASSWORD=… tests/load/smoke.js
Пользователь — отдельный обычный (role=user): тест наполняет его историю и лайки. Нагрузку можно менять:
-e LISTENERS=20 -e SUGGEST_RPS=5 -e DURATION=1m. Запускать с машины в той же сети, что и сервер, —
иначе в цифрах будет задержка канала.