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

7.6 KiB
Raw Permalink Blame History

Спринт 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. P0-3 Rate-limitMicrosoft.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. 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. P0-5 Permission-based authzPermissionHandler + [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. 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. 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. P0-8deploy/.env.example + docs/secrets.md. .env.example (все required+опц.), secrets.md (таблица/ротация/гигиена), проброс OpenIddict__Issuer/CertPassword в compose. compose config валиден.
  7. P0-9docs/release-checklist.md. Пред/во время/после выкатки + откат + прод; ссылки на secrets/backup/openiddict/stage-setup.
  8. 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. 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.