food-market/tests/food-market.UnitTests/RolePermissionsTests.cs
nns 9588d03bf4 test(s15): axe a11y + focus traps + unit coverage 80% + property tests + backup drill
Sprint 15 финальный — реальные axe + coverage + pg_restore numbers.

Ключевые цифры:
- axe-core: critical=0 on 10 страниц stage'а; serious 12→9
  после фиксов (sidebar contrast + 8 icon-only back-arrow aria-labels).
- Unit coverage: Application 56%→83%, Domain 11%→79%, combined
  60%→80%. Тестов 68→147 (+79).
- Backup recovery drill: RTO ~25 секунд end-to-end
  (pg_dump 2s + pg_restore 4s + dotnet startup 19s).

Что сделано:
1. @axe-core/playwright + stage-ui-15 (10 страниц) + stage-ui-16
   (SR smoke на login: getByLabel, role=alert, aria-describedby,
   keyboard nav).
2. useFocusTrap hook (WCAG 2.4.3 + 2.1.2): return-focus, mount-focus,
   Tab cycle. Подключён к Modal + ConfirmDialog с opt-in
   defaultFocus='cancel'|'confirm'. ConfirmDialog по дефолту фокусит
   Cancel для destructive actions (safer чем Enter→Delete).
3. A11y фиксы:
   • text-slate-400→text-slate-500 в sidebar (contrast 2.63→4.61).
   • 8 страниц edit с back-arrow Link — aria-label + aria-hidden
     на иконке + текст-slate-500 цвет.
   • Modal close button — то же.
   • LoginPage — aria-invalid/aria-describedby/role=alert на
     ошибках валидации.
   • Field component — role="alert" на error span (announce'ит SR).
4. 8 файлов unit-тестов: PhoneNormalization, PagedRequest,
   RequiredGuid, RolePermissions (Domain), DomainPocoSmoke,
   DomainFullPropertyTouch, CatalogDtosSmoke, StockServiceProperty
   (4 seeds × 4 size + batch + 2-product isolation).
5. Backup-drill: pg_dump со stage'а → fresh postgres:16-alpine →
   pg_restore → dotnet run против восстановленной БД → /health/ready
   Healthy. Команды и timing в RUNBOOK.md.
6. Docs review:
   • MULTI-TENANCY чеклист «добавить tenant-сущность» расширен с 6
     до 19 шагов (Domain → EF Config → Migration с Xmin →
     RolePermissions → Validation → Controller + RequiresPermission →
     Audit + SensitiveOpsAudit → property tests).
   • ARCHITECTURE.md — Sprint 13-15 changes таблица.
   • DEVELOPER-GUIDE.md — «что добавилось после первого guide'а» +
     a11y pitfalls в «что НЕ делать».

Stage smoke ✓. Это финальный автономно-безопасный спринт. Дальше
нужен вход от user'а (ОФД keys, MoySklad tokens, Windows для POS,
прод-деплой план, kz-перевод, реальный SMTP).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-07 14:53:38 +05:00

97 lines
3.3 KiB
C#
Raw Permalink 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.

using FluentAssertions;
using foodmarket.Domain.Organizations;
using Xunit;
namespace foodmarket.UnitTests;
/// <summary>RolePermissions — POCO с 34 булевыми флагами и static-фабрикой
/// All() которая создаёт «полный» набор для системной роли Администратор.
/// Раньше в репо не было тестов на эту сущность.</summary>
public class RolePermissionsTests
{
[Fact]
public void Default_constructor_all_false()
{
var p = new RolePermissions();
// Sanity-check: default RolePermissions = «всё запрещено». Это важно
// потому что новая роль создаётся с этим значением, и пока юзер не
// выставил флаги — она не имеет доступа.
p.ProductsView.Should().BeFalse();
p.RetailSalesOperate.Should().BeFalse();
p.OrgSettingsManage.Should().BeFalse();
p.LoyaltyManage.Should().BeFalse();
}
[Fact]
public void All_factory_enables_every_permission()
{
var p = RolePermissions.All();
// Каталог
p.ProductsView.Should().BeTrue();
p.ProductsEdit.Should().BeTrue();
p.ProductsDelete.Should().BeTrue();
p.ProductGroupsManage.Should().BeTrue();
p.PriceTypesManage.Should().BeTrue();
p.UnitsManage.Should().BeTrue();
// Закупки
p.SuppliesView.Should().BeTrue();
p.SuppliesEdit.Should().BeTrue();
p.SuppliesPost.Should().BeTrue();
p.SuppliesDelete.Should().BeTrue();
// Продажи
p.DemandsView.Should().BeTrue();
p.DemandsEdit.Should().BeTrue();
p.DemandsPost.Should().BeTrue();
p.RetailSalesOperate.Should().BeTrue();
p.RetailSalesRefund.Should().BeTrue();
// Контрагенты
p.CounterpartiesView.Should().BeTrue();
p.CounterpartiesEdit.Should().BeTrue();
p.CounterpartiesDelete.Should().BeTrue();
// Склад
p.StocksView.Should().BeTrue();
p.InventoryEdit.Should().BeTrue();
p.LossEdit.Should().BeTrue();
p.EnterEdit.Should().BeTrue();
p.TransferEdit.Should().BeTrue();
// Отчёты
p.ReportsView.Should().BeTrue();
p.ReportsFinanceView.Should().BeTrue();
p.ReportsStockView.Should().BeTrue();
// Настройки
p.OrgSettingsManage.Should().BeTrue();
p.EmployeesManage.Should().BeTrue();
p.RolesManage.Should().BeTrue();
p.StoresManage.Should().BeTrue();
p.RetailPointsManage.Should().BeTrue();
p.CashRegistersManage.Should().BeTrue();
p.IntegrationsManage.Should().BeTrue();
// Sprint 9
p.LoyaltyManage.Should().BeTrue();
p.PromotionsManage.Should().BeTrue();
}
[Fact]
public void Individual_flags_can_be_set()
{
var p = new RolePermissions
{
ProductsView = true,
RetailSalesOperate = true,
};
p.ProductsView.Should().BeTrue();
p.RetailSalesOperate.Should().BeTrue();
// Остальные остаются false.
p.ProductsEdit.Should().BeFalse();
p.OrgSettingsManage.Should().BeFalse();
}
}