food-market/docs/sprint28-progress.md
nns f6b50baa50
Some checks are pending
Auto-tag / Create date-tag (push) Waiting to run
docs(s28): сводка interim soak (31min, 91k iter, 0 fail) + overnight work
2026-06-09 03:47:03 +05:00

171 lines
8.5 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.

# Sprint 28 — docs sync + test coverage gap-fill (overnight)
Цель: воспользоваться overnight-окном для накопления реального soak-
теста (4 часа), параллельно — мелкие реальные улучшения: точность
auto-generated api-reference, новые integration-тесты, CI-integration.
Старт: 2026-06-09 03:15. Исполнитель: Claude Opus 4.7 (autonomous).
Продолжение [[sprint27_done]].
## Что сделано
### 1. api-reference.md теперь имеет 240 endpoint'ов (было 195)
`ApiReferenceDocsJob` regex для return-type'a слишком строгий — матчил
только 1-level generic. Не ловил `Task<ActionResult<PagedResult<EmployeeDto>>>`
(double-nested), поэтому пропускал ~45 endpoint'ов.
**Фикс:** новый regex `[^(=;{}\n]+?` для return-type (любой identifier
с любой глубиной генериков) в:
- `src/food-market.api/Background/ApiReferenceDocsJob.cs` (runtime job
для weekly auto-gen в /content-root/api-reference-generated.md)
- `scripts/gen-api-reference.py` (Python-эквивалент для коммита в репо)
Результат: **240 endpoints, 58 controllers** (было 195/57). Пример
пропуска: `EmployeesController` имел 4 из 5 endpoint'ов; теперь все 5
(GET /api/organization/employees был пропущен — это list endpoint с
return типом `Task<ActionResult<PagedResult<EmployeeDto>>>`).
### 2. observability.md дополнен (Sprint 20+ + Sprint 26)
- Добавлен ряд про `food_market_disk_free_bytes` (Sprint 20 `DiskMonitoringJob`).
- Новый раздел «quality-watchdog метрики» — 5 метрик textfile exporter'a
(`quality_watchdog_run_total`, `step_failure_total`, `endpoint_p95_ms`,
`last_run_status`, `incidents_total`).
- Раздел «Готовые dashboards» теперь упоминает оба JSON (food-market.json
+ quality-watchdog.json) с UID/назначением.
### 3. Integration spec #7: 1C-CSV import + GDPR org-export
`tests/integration/07-import-export-flows.spec.ts`:
- POST `/api/catalog/products/import/1c-csv` с 1С-форматом
(Наименование;Штрихкод;Цена;Единица;Группа в semicolon-CSV) →
возвращает `{created, skipped, errors[], ids[]}`.
- POST `/api/org/export` (НЕ `/api/admin/org-export`, как я было
предположил — был баг в моих оригинальных предположениях) →
возвращает `{id, status, ...}`.
- orgB не видит export orgA — multi-tenant clean.
Прогон 8.2s. Pass.
### 4. PruneQualityTestOrgsAsync unit test
`tests/food-market.IntegrationTests/PruneQualityTestOrgsTests.cs` — два
[Fact]'a:
- `Deletes_only_quality_orgs_older_than_threshold` — 4 org'и (старая
quality, свежая quality, реальная org, edge-старше-threshold). Threshold
24h. Удаляет 2 (старую quality + edge). Свежая quality и реальная
остаются.
- `Returns_zero_when_no_candidates_match` — только свежая quality →
deleted=0, org остаётся.
Тест требует Testcontainers (PostgreSQL для information_schema +
DO $$ блоков) — не SQLite. Прогоняется в CI на dev-vm; локально нет
.NET 8.0.417 SDK (global.json pin).
### 5. Forgejo CI workflow: integration suite
`.forgejo/workflows/regression.yml` добавлен шаг "Run integration
cross-feature suite" после regression flows+visual. На failure артефакты
из tests/integration/reports/ тоже загружаются. Telegram-уведомление
обновлено: `35 flows + 60 visual + 8 integration`.
### 6. 4-часовой soak test запущен в фоне
`setsid nohup k6 run ... &` — детачнутая сессия. Параметры:
- DURATION=4h, RPS=50
- E2E_ADMIN_URL=stage
- Output: `/tmp/soak-real/k6.log`, `/tmp/soak-real/summary.json`
- Monitor: 5-минутный snapshot в `/tmp/soak-real/metrics.csv`
ETA финиша: ~07:15. Финальные числа добавлю в этот файл после
завершения.
## Soak: интерим-результаты (на момент 03:46, 31 мин run)
| Метрика | Значение | Замечание |
|---|---|---|
| iterations | 91371 | 0 interrupted |
| iter/sec | 49.2 | target 50 (constant-arrival-rate) |
| api_mem_mb (range) | 250-465 | spike 465 — это redeploy для HSTS, нормальное |
| api_cpu_pct (steady) | 54-83% | под нагрузкой |
| pg_connections | 19-56 | spike 56 во время restart, обратно к 19 |
| disk_free_gb | 30 | без изменений |
| products p95 | 32-256ms | 256ms был во время старта (warmup) |
Зафиксированы 0 failures (`http_req_failed.rate=0.0`). Memory не растёт
линейно — bounces around 250-300 MiB после warmup.
Soak продолжается до ~07:15. Финальные числа в этом разделе после
завершения.
## Дополнительная работа (overnight)
После основных 6 пунктов чек-листа также сделано:
### 7. fix: ApiReferenceDocsJob handle `~/path` ASP.NET convention
`HttpPost("~/connect/token")` — ASP.NET Core конвенция «absolute from
root, ignore class [Route]». До фикса генератор клеил с base-route'ом →
`/~/connect/token` (невалидный). Теперь tilde корректно срезается.
Виден `/connect/token` в api-reference.md.
### 8. fix(security): HSTS header on stage
Найдено: stage отдавал security-headers (CSP, X-Frame-Options, etc.) но
БЕЗ Strict-Transport-Security. Причина: `UseHsts()` в ASP.NET Core не
срабатывает за nginx-прокси (api видит HTTP, не HTTPS — нет
ForwardedHeaders middleware'a).
Фикс: добавлен `add_header Strict-Transport-Security` в
`deploy/nginx.conf`. max-age=2592000 (30 дней), без includeSubDomains
и без preload — pre-emptive consent, можно безопасно убрать.
Verified: `curl -I https://test.admin.food-market.kz/``strict-transport-security: max-age=2592000`.
Integration test `tests/integration/08-security-headers.spec.ts` (2
[Fact]) проверяет 7 security-заголовков на главной + на 404.
### 9. test: ApiReferenceDocsJob regex lock-down
`tests/food-market.UnitTests/ApiReferenceDocsJobTests.cs` — создаёт
временный controller-файл с double-nested generic
(`Task<ActionResult<PagedResult<Dto>>>`), прогоняет GenerateAsync,
проверяет count=3 и наличие всех routes в output.
Защищает от регрессии Sprint 28 fix'a.
### 10. perf: integration suite workers=2 (46% speedup)
Раньше `tests/integration/playwright.config.ts` имел `workers: 1`
из-за signup rate-limit. После Sprint 26 повышения stage до
5000/min — больше не упирается. Включил `workers: 2` → прогон 66s → 36s.
### 11. README + ONBOARDING update до Sprint 28
Sprint history дополнен 25-28. Endpoint count обновлён до 240. Sprint
counter unified.
## Метрики
| | До Sprint 28 | После | Δ |
|---|---|---|---|
| API endpoints в docs/api-reference.md | 195 (из 240) | 240 | +45 |
| Controllers в reference | 57 | 58 | +1 |
| Integration specs | 6 | 7 | +1 |
| CI integration step | (нет) | Sprint 27/28 step | new |
| PruneQualityTestOrgs unit test | (нет) | 2 [Fact]'a | new |
| Observability metrics doc | 6 кастомных | 11 кастомных (+ disk + 5 watchdog) | +5 |
## Артефакты этой ночи
- `scripts/gen-api-reference.py` — Python-генератор (соответствует
обновлённому `ApiReferenceDocsJob.cs`)
- `docs/api-reference.md` — пересгенерирован, 240 endpoints
- `docs/observability.md` — дополнен
- `tests/integration/07-import-export-flows.spec.ts` — новый spec
- `tests/food-market.IntegrationTests/PruneQualityTestOrgsTests.cs` — новый C# test
- `.forgejo/workflows/regression.yml` — added integration step
- `/tmp/soak-real/k6.log`, `/tmp/soak-real/metrics.csv` — soak в процессе