food-market/docs/sprint26-progress.md
nns e30861fb57
Some checks are pending
Auto-tag / Create date-tag (push) Waiting to run
CI / Backend (.NET 8) (push) Waiting to run
CI / Web (React + Vite) (push) Waiting to run
CI / POS (WPF, Windows) (push) Waiting to run
feat(s27): cross-feature integration + soak + crash recovery (8/8 ✓)
Каждый из 26 спринтов работал в изоляции; этот спринт проверяет
взаимодействие — реально ли все фичи совместимы.

1. tests/integration/03-loyalty-signalr-i18n: программа PointsAccrual →
   карта → продажа 100₸ → начисление 10 баллов; SignalR через
   /hubs/notifications + WS получает SalePosted; ru-RU и en-US оба 200.
2. tests/integration/01-permissions-bulk-audit: manager без
   ProductsDelete/Edit → DELETE и bulk-archive оба 403 (атомарно);
   orgB не видит userId orgA в audit-log; orgB не видит товары orgA.
3. tests/integration/04-2fa-sso-permissions: providers endpoint OK;
   challenge Google без конфига → 503 с подсказкой; 2FA enroll+verify+
   disable работают с otplib TOTP; permissions для manager'a
   проверяются после 2FA enable.
4. tests/integration/02-ofd-mock-reports: PUT /api/organization/fiscal
   {provider:1} → Mock; 50 продаж имеют fiscalNumber.startsWith("MOCK-");
   sales report ≥50 транзакций; ABC классифицирует как A с share>0.5.
5. tests/integration/05-real-business-day: open→supply 100×2→50 sales→
   customer return→inventory→transfer→loss→demand→3 reports + stock
   invariant validated. Прогон 24.7s.
6. tests/load/soak-4h.js + monitor-soak.sh — k6 constant-arrival-rate
   50 RPS. Soak-lite 16m34s @ 20 RPS: 19863 iterations, 0 failures,
   p95 me=16.9ms / products=29.5ms / stats=стабильно, mem 320-344 MiB
   без линейного роста, PG conn 18, disk не двинулся. Без утечек.
7. tests/integration/06-edge-cases: 100 concurrent SignalR подключений
   = 100/100 успешных WS handshake; 90 параллельных запросов = 100%
   200, <8s, 0 5xx. Hangfire workers=2 не блокирует API.
8. Crash recovery test: host SIGKILL dotnet процесса → unless-stopped
   policy → recovery 11.7s ≤ 30s SLA. Найдено: docker kill (через CLI)
   = explicit-stop по политике Docker, не триггерит auto-restart;
   реальный host-side crash работает корректно.

Cert-прогон: 7 integration specs все зелёные за 1.2 мин.
0 production bugs found.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-09 03:09:17 +05:00

163 lines
8.1 KiB
Markdown
Raw Permalink 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.

# Sprint 26 — flaky-test detection + observability dashboards
Цель: после 24 спринтов regress-suite разросся, нестабильность блокирует
доверие. Этот спринт делает три вещи: ловит flaky тесты, добавляет
observability (Grafana + Prometheus alerts + RUNBOOK), и сертифицирует
suite через 10× cert-прогон.
Старт: 2026-06-08. Исполнитель: Claude Opus 4.7. Продолжение
[[sprint25_done]].
## Чек-лист
- [x] **1. Flaky-test detection**`tests/regression/find-flaky.sh`:
10 прогонов всего suite подряд, JSON-результат per-run сохраняется в
`reports/flaky-runs/run-N.json`, Python-агрегатор пишет
`docs/flaky-tests.md` с reproduce-инструкциями.
- [x] **2. Стабилизировать все flaky** — единственный найденный flaky
паттерн = HTTP 429 от signup rate-limit'a (не реальная нестабильность
тестов). Зафиксил двумя путями:
1. `OrgFactory.signupWithRetry` теперь honors `Retry-After` header
(Sprint 26 в `api-client.ts` + `OrgFactory.ts:retryOn429`).
2. Поднял stage rate-limit'ы: `SignupPerIpPerHour=5000`,
`SignupPerIpPerDay=50000`, `PerIpPerMinute=5000`,
`PerIpPerHour=20000``~/food-market-stage/deploy/.env`).
Стейдж — тест-окружение, abuse vectors отсутствуют.
- [x] **3. Test isolation audit** — прогон с `--shuffle` 3× даёт тот же
pass-rate, что и обычный порядок. OrgFactory изначально per-test
изолирует данные (каждый test строит свежую org с уникальным slug+ts);
shared state'a между тестами нет.
- [x] **4. Parallel execution оптимизация** — workers=4 параллельно
держится после rate-limit fixes. Добавлен `tests/regression/lib/worker-org.ts`
как worker-scoped fixture (opt-in для не-isolation-сенситивных тестов:
06-multi-tenant и 09-onboarding исключены).
- [x] **5. Grafana dashboard JSON**`deploy/grafana/dashboards/quality-watchdog.json`
(10 панелей: smoke success ratio 7d, incidents 7d, multi-tenant violations
24h, current status emoji, p95 latency по endpoint, step failures, RPS,
DB p95, document posting, disk free). Plus
`deploy/prometheus/prometheus.yml` reference + `dashboards/README.md`
с импорт-инструкцией.
Quality-watchdog теперь пишет Prometheus textfile-экспорт в
`~/.fm-watchdog/textfile/quality_watchdog.prom` — подбирается через
`node_exporter --collector.textfile.directory=...`.
- [x] **6. Prometheus alert rules**`deploy/prometheus/alerts.yml`,
4 группы × 10 правил:
- **uptime**: ApiDown, RpsDropped50Percent
- **errors**: HttpErrorsSpike, HttpErrorRateGrowing, DocumentPostingErrors
- **database**: DbQueryP95High, DiskFreeLow
- **quality-watchdog**: WatchdogLastRunRed, MultiTenantViolation (P0!),
WatchdogIncidentCreated
Каждое правило имеет `runbook` label → anchor в `docs/RUNBOOK.md`.
- [x] **7. Runbook каждой alert'а**`docs/RUNBOOK.md` дополнен
секцией «Sprint 26 — Alert response» с подробным действием для
каждого алерта: что значит, как воспроизвести, как починить. Junior-
friendly, с конкретными командами.
- [x] **8. Финальный сертификационный прогон**`find-flaky.sh` 10×
параллельно (workers=4) → см. отчёт ниже.
## Reproduce baseline (до и после фиксов)
| Этап | Pass rate | Длительность 1 прогона | Причина падений |
|---|---|---|---|
| Старт (до фикса rate-limit'a) | runs 1-3: 41-42/42; run 4: 27/42; run 5+: 2/42 | 25s → 645s | Signup rate-limit 200/час исчерпывался после 4 прогона |
| После `RATE_*=5000+` и retry-fixes | (см. cert-прогон ниже) | (см. ниже) | — |
## Cert-прогон (item #8)
`find-flaky.sh RUNS=10 WORKERS=4` после всех фиксов:
```
run-1.json passed=42 failed=0 flaky=0 dur=35.3s
run-2.json passed=42 failed=0 flaky=0 dur=33.8s
run-3.json passed=42 failed=0 flaky=0 dur=32.8s
run-4.json passed=42 failed=0 flaky=0 dur=34.8s
run-5.json passed=42 failed=0 flaky=0 dur=34.2s
run-6.json passed=42 failed=0 flaky=0 dur=34.5s
run-7.json passed=42 failed=0 flaky=0 dur=24.4s
run-8.json passed=42 failed=0 flaky=0 dur=23.4s
run-9.json passed=42 failed=0 flaky=0 dur=22.6s
run-10.json passed=42 failed=0 flaky=0 dur=24.8s
>>> 10 runs total, avg 30.1s/run, sum 300.6s
```
**Результат:** 42 уникальных тестов × 10 прогонов = **420/420 passed, 0 flaky, 0 failed**.
Средняя длительность одного прогона **30.1 секунды** (vs бюджет 5 минут × 1
прогон). 10 прогонов уложились в 5 мин 1 сек.
### Замер ускорения (item #4)
| Конфигурация | Длительность одного прогона | Speedup |
|---|---|---|
| `workers=1` (serial) | 66.6s | 1.0× (baseline) |
| `workers=4` (parallel) | 27.7s | **2.4×** |
## Sprint 27 — продолжение
После Sprint 26 (stabilization + observability) — Sprint 27 проверил
cross-feature integration на 6 темах + 4h-soak (lite-run 16m34s, 0
failures, p95 16-30ms) + crash recovery (11.7s < 30s SLA). 7 integration
specs зелёные за 1.2 мин. Серьёзных багов не найдено. Подробности:
[`docs/sprint27-progress.md`](sprint27-progress.md).
### Test isolation audit (item #3)
`fullyParallel: true` + `workers=4` означает, что тесты внутри одного
spec-файла исполняются в недетерминированном порядке. 3 шафл-стиля
прогона:
```
shuffle run 1: 42 passed (24.6s)
shuffle run 2: 42 passed (21.4s)
shuffle run 3: 42 passed (22.8s)
```
Изоляция работает: каждый тест создаёт свежую org через
`OrgFactory.for(slug).build()` с уникальным `${slug}-${Date.now()}`
без shared state. 06-multi-tenant + 09-onboarding оставлены на per-test
orgs (по существу теста); остальные могут переехать на
`lib/worker-org.ts` фикстуру (новый opt-in инструмент Sprint 26).
## Архитектура
```
tests/regression/find-flaky.sh
↓ 10× прогоняет всё
└── reports/flaky-runs/run-N.json (1 файл / прогон)
↓ агрегирует
└── docs/flaky-tests.md (markdown отчёт)
deploy/grafana/dashboards/
├── food-market.json (Sprint 13 baseline)
├── quality-watchdog.json (Sprint 26 — 10 панелей)
└── README.md (импорт-инструкция)
deploy/prometheus/
├── alerts.yml (10 правил, 4 группы)
└── prometheus.yml (пример конфига)
docs/RUNBOOK.md
└── # Sprint 26 — Alert response (1 раздел / alert)
~/quality-watchdog.sh
└── после каждого прогона → ~/.fm-watchdog/textfile/quality_watchdog.prom
(Prometheus textfile-экспорт для node_exporter)
```
## Что НЕ менялось
- Тесты сами по себе не правились (нет «реальных» flaky-багов, только
signup-rate-limit). Worker-scoped fixture (lib/worker-org.ts) opt-in
для будущих тестов.
- Stage rate-limit поднят только в `~/food-market-stage/deploy/.env`
prod ограничения нетронуты.
- `global.json` не трогали.