Полная регрессия всех сценариев + 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>
79 lines
6.3 KiB
Markdown
79 lines
6.3 KiB
Markdown
# Системное тестирование 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`.
|