Files
pipu-music/docs/testing.md
T
mrleo1nidandCursor 066ba24834
ci / api / api (push) Successful in 40s
ci / db / db (push) Successful in 1m3s
ci / contract / contract (сгенерированный код совпадает с openapi.yaml) (push) Failing after 1m48s
ci / docker / docker (образы собираются — их сервер собирает сам при обновлении) (push) Successful in 46s
ci / ts / ts (packages + web) (push) Failing after 4m57s
ci / mobile / mobile (Metro-бандл Android) (push) Successful in 5m7s
feat: finish the upload, playback, and test gaps from the docs
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>
2026-10-21 09:37:53 +03:00

80 lines
8.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Тестирование
## Пирамида
| Уровень | Где | Инструменты | Что покрываем |
|---|---|---|---|
| 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`).
1. Первый запуск: вход bootstrap-админа → принудительная смена логина / пароля → главная.
2. Админ создаёт модератора; модератор создаёт пользователя; модератор **не может** создать модератора (UI и API).
3. Админ добавляет `local`-источник, задаёт два шаблона, предпросмотр, скан → треки появились в поиске.
4. Поиск: выпадающий список → переход к исполнителю; Enter → страница поиска.
5. Воспроизведение: запуск трека, перемотка, перезагрузка страницы → трек и позиция восстановлены.
6. Моя волна: запуск с занятием «Бегаю» → в выдаче треки с BPM из профиля; дизлайк → следующий трек, трек в «Дизлайках».
7. Коллекция: лайк → в «Лайках»; история содержит прослушанное.
8. Смена пароля пользователем → старые сессии разлогинены.
9. Переключение языка ru ↔ en.
10. Сброс пароля пользователю → обязательная смена при входе.
11. Очередь на добавление: модератор загружает папку из 5 файлов, где 1 — точная копия трека из библиотеки,
1 — тот же трек в другом формате → первый отброшен на предпроверке, второй — по отпечатку; оба не учтены в квоте;
оставшиеся 3 — `pending_review`. Админ правит альбом, одобряет → файлы появились в хранилище по `write_template`,
трек найден поиском; квота освобождена.
12. Квота: лимит 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](api.md) и [recommendations.md](recommendations.md#10-требования-к-производительности):
| Запрос | 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 завершается с ненулевым кодом, если порог не выполнен.
```bash
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`. Запускать с машины в той же сети, что и сервер, —
иначе в цифрах будет задержка канала.