food-market/docs/sprint1-progress.md
nns 9ef9df3ed5 docs(sprint1): P1-21 done — все 9 пунктов выполнены, итог
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-27 03:16:25 +05:00

90 lines
7.6 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.

# Спринт 1 — стабилизация (P0 код/инфра)
Автономная работа. После каждого пункта: `dotnet build` (SDK 8.0.126), релевантные тесты,
коммит порцией, отметка `[x]` здесь, коммит прогресса.
> Сборка: POS-проект (`food-market.pos`, net8.0-windows) на Linux не собирается — это
> ожидаемо (нужен Windows SDK). Эталон сборки — `dotnet build src/food-market.api/food-market.api.csproj`
> + solution-сборка тестовых проектов.
## Чек-лист
1. [x] **P0-3 Rate-limit**`Microsoft.AspNetCore.RateLimiting` (sliding window) на
`/connect/token` и `/api/auth/signup`. 5/мин/IP, 20/час/IP. Тест: 6-я попытка за минуту → 429.
`AuthRateLimiterExtensions` (global limiter + chained окна, gate по пути), отдельные
бакеты на эндпоинт. Проверено curl на :5091 — token 6→429, signup 6→429, бакеты независимы.
2. [x] **P0-4 Health checks**`/health/live` (alive) + `/health/ready` (DB ping + миграции
применены). docker-compose healthcheck → `/health/ready`.
`DatabaseReadyHealthCheck` (CanConnect + GetPendingMigrations), JSON-writer, tag `ready`.
Проверено: live→200 (checks:[]), ready→200 (database Healthy). Dockerfile + compose api
healthcheck на `/health/ready`, web ждёт api `service_healthy`. `/health` оставлен для
совместимости. Прим.: startup `Migrate()` — fail-fast при DB-down на буте (вне scope, compose
гейтит api на `postgres: service_healthy`).
3. [x] **P0-5 Permission-based authz**`PermissionHandler` + `[RequiresPermission("...")]`
читающий флаги `RolePermissions`. Заменить `[Authorize(Roles=...)]` в каталоге/документах.
E2E: кастомная роль без `ProductsEdit` → 403 на PUT товара.
`PermissionAuthorizationHandler` (live из БД: Employee→EmployeeRole→Permissions) +
`RequiresPermissionAttribute` + динамический `PermissionAuthorizationPolicyProvider`
(policy `perm:*`). SuperAdmin/Identity-Admin — full-access шорткат (custom-роли не маппятся
на Admin). Заменены role-гейты в 8 catalog + 2 document контроллерах (Currencies/Countries
оставлены SuperAdmin — глобальный справочник). Закрывает «роли — фикция» из аудита.
Проверка: curl на :5091 (403/200/400) + e2e `roles` step08 — зелёный 8/8.
Rate-limit стал конфигурируемым (`RateLimiting:*`) — иначе повторные логины тестов → 429.
4. [x] **P0-1 OpenIddict prod-ключи** — signing+encryption сертификаты из пути в конфиге,
persistent self-signed если файла нет. Dev-поведение не ломать. Документировать.
`OpenIddictKeyConfigurator`: dev RSA-XML (без изменений), prod X509 из
`OpenIddict:SigningCertPath`/`EncryptionCertPath`/`CertPassword`, self-signed (5 лет) в
App_Data при отсутствии. Проверено: prod 5-сегм. JWE, persist через рестарт (тот же
fingerprint, pre-restart токен валиден); dev 3-сегм. JWT. `docs/openiddict-keys.md`.
5. [x] **P0-6 Авто-бэкап**`deploy/food-market-backup.service` + `.timer`, скрипт
backup+ротация 30 дней, `docs/backup-restore.md`. Только артефакты в репо.
`food-market-backup.sh` (pg_dump -Fc + tar uploads, ротация 30д, атомарная запись),
systemd timer ежедневно 03:00 (Persistent). Проверено: дамп PGDMP/248 TOC, pg_restore --list ок.
6. [x] **P0-8**`deploy/.env.example` + `docs/secrets.md`.
`.env.example` (все required+опц.), `secrets.md` (таблица/ротация/гигиена), проброс
`OpenIddict__Issuer`/`CertPassword` в compose. `compose config` валиден.
7. [x] **P0-9**`docs/release-checklist.md`.
✅ Пред/во время/после выкатки + откат + прод; ссылки на secrets/backup/openiddict/stage-setup.
8. [x] **P1-20 Unit-тесты**`tests/food-market.UnitTests`: `StockService.ApplyMovement`,
расчёт Cost в `SuppliesController.Post`, валидация платежа `RetailSalesController.Post`,
multi-tenant query filter.
✅ 23 теста зелёные. Чистая логика вынесена в Application (`MovingAverageCost`,
`RetailPaymentValidator`) и используется контроллерами. StockService + query-filter на
SQLite in-memory (EF8 поддерживает `ToJson`). `FakeTenantContext`, `SqliteDb` helper.
9. [x] **P1-21 Integration-тесты** — Testcontainers.PostgreSql + WebApplicationFactory:
signup-flow, supply post→unpost, retail overselling, tenant isolation A vs B, permission-проверки.
`tests/food-market.IntegrationTests` — 10 тестов зелёные на реальном postgres:16-alpine
(Ryuk off, RateLimiting off через env). `ApiFactory`+`ApiActor`. Все 5 сценариев покрыты.
## Итог
**Все 9 пунктов выполнены.** Спринт 1 (стабилизация P0/P1-инфра) завершён 2026-05-27.
Сводка:
- **P0-3** rate-limit (5/мин+20/час на IP, конфигурируем) — `AuthRateLimiterExtensions`.
- **P0-4** health `/health/live` + `/health/ready` (БД+миграции), compose/Dockerfile healthcheck.
- **P0-5** permission-based authz (`[RequiresPermission]` + handler по флагам роли), 10 контроллеров.
- **P0-1** OpenIddict prod X509-ключи из конфига, persistent self-signed.
- **P0-6** авто-бэкап (systemd timer + скрипт + ротация 30д) + `backup-restore.md`.
- **P0-8** `deploy/.env.example` + `secrets.md`.
- **P0-9** `release-checklist.md`.
- **P1-20** unit-тесты (23) — `MovingAverageCost`, `RetailPaymentValidator`, StockService, query-filter.
- **P1-21** integration-тесты (10) — Testcontainers + WebApplicationFactory.
Сборка зелёная (`dotnet build src/food-market.api`); тесты: **23 unit + 10 integration = 33 зелёных**.
POS (net8.0-windows) на Linux не собирается — ожидаемо, вне scope.
Пропущено намеренно (по инструкции): P0-7 ОФД (нужен внешний оператор), gateway nginx HTTPS,
`global.json` (локальный даунгрейд не коммитим). Установка backup-таймера/сертификатов на
prod-vm — отдельный деплой-шаг (артефакты готовы).
### Эффект на код вне P0/P1
- Чистая логика вынесена в Application (`MovingAverageCost`, `RetailPaymentValidator`) — контроллеры используют её.
- `Program` стал `public partial` для WebApplicationFactory.
- e2e `roles` step08: gap → реальная проверка permission-enforcement (8/8 зелёный).
## Лог
- Каждый пункт: build + тесты + коммит порцией + отметка [x] + коммит прогресса.
- Все правки на ветке `main` (origin Forgejo), без коммита `global.json`.