# Системное тестирование 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 --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`.