# Sprint 16 — E2E regression suite + visual regression + nightly verify

Цель: построить «постоянный» regression-контур, чтобы регресс ловился
сам — не «вспомнили посмотреть». 35 user-flow specs + 60 visual
snapshot'ов + автоматический nightly + CI на каждый push в main.

Старт: 2026-06-07 (после Sprint 15). Исполнитель: Claude Opus 4.7.

## Принципы

- Каждый flow — независимый, использует фабрику для подготовки данных
  через API (не через UI-клики).
- Visual baseline — fresh stage post-deploy. Diff threshold 0.2% +
  маски на динамический контент (timestamps в артикулах, KPI).
- Полный прогон < 15 минут (Playwright workers + retry=1 на CI).
- НЕ трогать: `global.json`, prod admin.food-market.kz, POS WPF.

## Чек-лист

- [x] **1. Regression suite** — `tests/regression/flows/` — **35 ключевых
  flow-тестов** в 8 spec-файлах (auth, catalog, documents post/unpost,
  reports, multi-tenant isolation, i18n+permissions+2FA+audit,
  realtime+misc). Прогон параллелен (workers=2 локально, 4 на CI).
  Отчёт `reports/playwright-html/` + JSON `reports/results.json`.
- [x] **2. Visual regression** — `tests/regression/visual/` —
  **60 snapshot'ов** (15 страниц × 2 темы × 2 viewport'a:
  desktop 1280×800 + mobile Pixel 5 375×667). Threshold 0.002 (0.2%) +
  маски на артикулы/KPI/delta'ы для устойчивости к timestamp-дрейфу.
- [x] **3. Test data factories** — `tests/regression/factories/` —
  `OrgFactory.for(slug).withProducts(N).withCounterparties(M).withSupplies(K).build()`
  собирает org через API за O(N) HTTP-вызовов (signup → token → refs →
  products → counterparties → posted supplies). Используется в каждом
  flow-тесте вместо signup-form.
- [x] **4. Forgejo workflow `.forgejo/workflows/regression.yml`** —
  on `workflow_run` после Docker API/Web, wait-for-ready → install
  pnpm + chromium → flows + visual → артефакты + Telegram на падение.
  Cache на pnpm-store + Playwright-browsers — повторный прогон ~3 мин.
- [x] **5. Nightly cron** — `~/nightly-verify.sh` + cron `0 4 * * *`:
  health-check → если падает, redeploy-stage → smoke flows
  (`@smoke` tag) → в воскресенье ещё полный flows + visual.
  Лог `~/.fm-watchdog/nightly-YYYYMMDD.log`, ротация >14 дней.
  Telegram-уведомление если упало (читает токен из
  `~/.fm-watchdog/telegram-token`).
- [x] **6. README badges** — добавлены 4 CI-status badge (CI, Docker API,
  Stage verify, Regression — берутся с Forgejo `actions/workflows/*.svg`)
  + coverage badge (`badges/coverage.svg`, генерируется
  `scripts/generate-badges.sh` из cobertura.xml, авто-коммит из CI step
  «Update coverage badge»).

## Замеры

### Regression suite stats

| Файл | Tests | Время (workers=2) |
|---|---|---|
| `flows/01-factory-smoke.spec.ts` | 1 | 5s |
| `flows/02-auth.spec.ts` | 4 (login/signup/refresh/wrong-pw) | 4s |
| `flows/03-catalog.spec.ts` | 5 (CRUD product/counterparty/store/price-type) | 6s |
| `flows/04-documents.spec.ts` | 8 (supply/enter/retail-sale/loss/transfer/demand/supplier-return post+unpost) | 12s |
| `flows/05-reports.spec.ts` | 4 (sales/stock/profit/abc с проверкой чисел) | 6s |
| `flows/06-multi-tenant.spec.ts` | 3 (list-isolation, get-by-id-isolation, sales-isolation) | 4s |
| `flows/07-i18n-permissions.spec.ts` | 5 (locale switch, 2FA enroll, audit log, anon→401) | 5s |
| `flows/08-realtime-misc.spec.ts` | 5 (dashboard render, search, /health/ready) | 6s |
| **Total** | **35** | **~30 секунд** ✓ |

| Visual project | Snapshot count | Время |
|---|---|---|
| `desktop-chromium` 1280×800 | 30 (15 страниц × 2 темы) | 2m 10s |
| `mobile-chromium` 375×667 (Pixel 5) | 30 | 2m 5s |
| **Total** | **60** | **~4 минуты** |

**Общий прогон**: ~30 сек (flows) + ~4 мин (visual) = **< 5 минут** end-to-end —
существенно ниже 15-минутного целевого порога.

### Coverage badge

- `scripts/generate-badges.sh` берёт cobertura.xml, считает покрытие
  по Application + Domain (combined), генерирует SVG через shields.io
  (offline fallback inline).
- Текущее значение: **80%** (от Sprint 15 baseline).
- Цвет шкалы: <50% red, 50-69% yellow, 70-84% green, ≥85% brightgreen.
- CI step «Update coverage badge» (`.forgejo/workflows/ci.yml`) на
  каждый push в main:
  1. `dotnet test --collect:"XPlat Code Coverage"`,
  2. `bash scripts/generate-badges.sh`,
  3. diff → commit `chore(badges): update coverage [skip ci]` → push.

### Nightly cron

- Скрипт `~/nightly-verify.sh` (217 строк bash).
- Crontab: `0 4 * * * /home/nns/nightly-verify.sh`.
- Последовательность:
  1. `curl /health/ready` — если не Healthy → `~/deploy-stage.sh` → повторная проверка → если опять упала → Telegram + exit.
  2. `pnpm install` (если node_modules нет) + `playwright test flows/ --grep @smoke`.
  3. Если воскресенье — полный прогон `flows/ visual/`.
  4. Telegram-уведомление при провале (`~/.fm-watchdog/telegram-{token,chat}`).
- Логи: `~/.fm-watchdog/nightly-YYYYMMDD.log`, `find -mtime +14 -delete`
  чистит каждый запуск.

## Журнал

### 2026-06-07 старт
Sprint 15 закрыт (7/7 ✓). Поехали по regression-чек-листу.

### 2026-06-07 п.3 (factory) — фундамент
`tests/regression/factories/OrgFactory.ts` + `api-client.ts` + `types.ts`.
Builder-pattern: `OrgFactory.for(slug).withProducts(N)…build()`.
Реквест-клиент на `fetch` (Node 20+), retry на 429 со сдвинутым backoff
(под Sprint 13 IP-лимит signup'a).

### 2026-06-07 п.1 (35 flows)
8 spec-файлов с тегами `@smoke` на ключевых flows для быстрого прогона.
API-driven где возможно (быстрее UI-кликов), Playwright UI только там
где нужно (form-login, dashboard render, локаль switch).

Несколько мелких фиксов по ходу:
- Profit/ABC report возвращают непосредственно List, не PagedResult — `rowsOf` helper.
- Multi-tenant isolation проверять по `id`, не `name` (одинаковые
  product-names в разных org'ах — норма).
- Login редиректит на «/» (OnboardingPage) на свежей org, не /dashboard.
- Loss-endpoint enum'у reason требует int, не строку.

### 2026-06-07 п.2 (visual 60)
2 spec'a (auth-pages + authenticated-pages). Baseline на свежий
deploy. Маски на динамический контент:
- `table td:nth-child(2)` (артикул с Date.now()),
- `[data-kpi]` (зависит от текущей даты),
- `[data-delta]` (стрелка от prev period).

snapshotPathTemplate включает `{projectName}` чтобы desktop+mobile
snapshot'ы не затирали друг друга.

### 2026-06-07 п.4 (forgejo workflow)
`.forgejo/workflows/regression.yml` — `on workflow_run` после
Docker API/Web. Cache на pnpm-store + Playwright-browsers. Артефакты
upload при failure, Telegram-уведомление в обоих случаях.

### 2026-06-07 п.5 (nightly cron)
`~/nightly-verify.sh` + crontab entry. Health → redeploy → smoke →
weekly full + Telegram. Логи с ротацией.

### 2026-06-07 п.6 (badges)
`scripts/generate-badges.sh` — coverage из cobertura.xml → SVG через
shields.io с offline-fallback. 4 CI-status badge + coverage badge
добавлены в README. CI-step авто-обновляет coverage badge на push в main.

## Итог

Все 6 пунктов ✓. Локальные числа:
- **35 flow-тестов**: 35/35 ✓ при workers=2 (~30 сек).
- **60 visual snapshot'ов**: 60/60 ✓ при CI=1 (retries=1) (~4 мин).
- **Полный прогон**: ~5 минут — **3× ниже** 15-минутного целевого порога.

Контур регрессии работает:
- На каждый push в main: Forgejo `Docker API`/`Docker Web` → `regression.yml`
  → 35 flows + 60 visual + Telegram.
- Каждую ночь: nightly cron → health → smoke (или полный в воскресенье)
  + Telegram при провале.
- Coverage и CI-status badges в README обновляются автоматически.

Следующее расширение (не в этом sprint'е):
- Перенести visual baseline'ы в LFS если они станут большими (сейчас 60
  PNG ~10 МБ, в репе ок).
- Добавить performance regression (k6 в CI nightly), сейчас k6 запускается
  только вручную.
- Заглушить flake в `product-new light` через определённый wait или
  более широкую маску.
