food-market/tests/e2e/reports/systemic-2026-05-26.md
nns 6098c03e1a docs(e2e): итоговый отчёт 2026-05-26 — 15 сценариев зелёные (124 шага)
Полная регрессия всех сценариев + 6 новых областей этой сессии (employees,
roles, superadmin-console, platform-smtp, auth-password, security-edge).
За день исправлено 4 бага: уволенный сотрудник логинится (P0), конкурентное
проведение приёмки ломает инвариант (critical), refresh не гасится после
ротации (high), change-owner принимал короткий reason (medium). Нереализованный
по ТЗ функционал (отчёты/склад-документы/POS/permission-authz/login-ratelimit)
зафиксирован как Logic gaps.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 12:05:23 +05:00

6.3 KiB
Raw Blame History

Системное тестирование Food Market — 2026-05-26

Инициировано Opus 4.7 по плану docs/TZ-тестирование.md (продолжение systemic-2026-05-23.md). Среда: docker food-market-postgres (postgres:16-alpine, 127.0.0.1:5434) + dotnet 8 API локально на :5081 + E2E через axios/psql + mock MoySklad. Запуск: E2E_ADMIN_URL=http://127.0.0.1:5081 ./tests/e2e/run.sh <scenario> --api-only.

0. TL;DR

Сценарий Шаги Результат
full-cycle 12
multi-tenant-isolation 12
documents-edge 10
auth-edge 10
auth-password 6
catalog-edge 12
stock-invariant-deep 10
stock-concurrency 4
reports-stats 5
moysklad-import 7
employees 10
roles 8
superadmin-console 6
platform-smtp 6
security-edge 6

Итого 15 сценариев, 124 шага — все зелёные. Багов нет.

Исправлено за день: 4 бага (2 P0/critical, 1 high, 1 medium) + доработка тестируемости MoySklad.

1. Найденные баги и исправления

BUG #1 (P0) — Уволенный сотрудник продолжает логиниться (commit 5091d43)

employees step07. EmployeesController.Delete (увольнение и soft-delete) и Update (деактивация) меняли только Employee.IsActive, но не трогали связанный AppUser. Логин и refresh гейтятся на User.IsActive → уволенный сохранял полный доступ и обновлял токены до 30 дней (ТЗ 0.4). Fix: SetLinkedUserActiveAsync — при деактивации сотрудника гасит User.IsActive и отзывает его valid OpenIddict-токены; при реактивации возвращает доступ.

BUG #2 (critical) — Конкурентное проведение приёмки ломает инвариант остатков (commit 15f27fd)

stock-concurrency step03. Supply.Post шёл на Read Committed, ApplyMovementAsync делает read-modify-write по Stock.Quantity без RowVersion. Двойное проведение одной приёмки применяло остаток дважды (Stock=32, Σ Movement=39). Fix: IsolationLevel.Serializable + перехват конфликта сериализации (40001/40P01) → 409.

BUG #3 (high) — Refresh-token остаётся валидным после ротации (commit 32729e7)

auth-edge step03. Новый principal не получал TokenId старого refresh → RedeemTokenEntry не гасил его; плюс 30-секундный reuse-leeway OpenIddict. Fix: проброс TokenId + SetRefreshTokenReuseLeeway(TimeSpan.Zero).

BUG #4 (medium) — change-owner принимал слишком короткий reason (commit 01568ba)

superadmin-console step04. Смена владельца писала reason в аудит, но проверяла лишь непустоту. PlatformSettings уже требует ≥10 — привели change-owner к той же планке (ТЗ 2.8).

Доработка — базовый URL MoySklad из конфигурации (commit e78e921)

MoySklad:BaseUrl (дефолт — боевой) позволяет наводить клиент на mock-сервер в e2e, не трогая прод.

2. Покрытие по разделам ТЗ

P0/P1 функциональные области, реализованные в коде, — покрыты и зелёные: Auth (login/refresh/signup/forgot-reset), Multi-tenancy, Catalog, Supplies, RetailSales, Stock (+ конкурентность), Employees, Roles, SuperAdmin Console, SMTP, MoySklad import, дашбордная выручка, безопасность (auth-гейт, traversal, SQLi, CORS, межтенантная 404).

3. Logic gaps — нереализованный по ТЗ функционал (НЕ баги)

  • Отчёты (2.12): профит по себестоимости, ABC-анализ, «остатки на дату», экспорт CSV/XLSX — нет ReportsController, RetailSaleLine без Cost-снимка. Есть только /stats (валовая выручка).
  • Складские документы (2.11): Оприходование/Списание/Перемещение/Инвентаризация — не реализованы (только read-only StockController).
  • POS Sync API (2.13) — не реализован.
  • Permission-based авторизация (2.7.2, «после P0-5») — эндпоинты только role-based [Authorize(Roles=...)]; флаги RolePermissions справочные.
  • Рейт-лимит логина (2.1.2, «после P0-3») — на /connect/token нет (у forgot-password — есть).
  • Системных ролей 3, а не 4-6 — намеренное упрощение (миграция Phase4b_RolesSimplify): Администратор/Кладовщик/Кассир. ТЗ устарело.
  • Supply.Unpost использует тот же read-modify-write без транзакции — теоретически уязвим к гонке (вне фокуса; Post закрыт).

4. Вне зоны API-e2e (по ТЗ — отдельные инструменты)

UI/UX (2.14, Playwright), публичный сайт (2.15, Astro/Lighthouse), инфраструктура/DevOps (2.16), нагрузочное (2.18) — требуют браузера/нагрузочных стендов, в данном прогоне не покрывались.

5. Окружение

  • SDK на dev-vm только 8.0.126; global.json репозитория остаётся 8.0.417 (локальный даунгрейд в коммиты не включён).
  • admin.food-market.kz — отдельный деплой с другой БД; e2e гоняются против локального API, подключённого к контейнеру food-market-postgres.