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>
97 lines
3.3 KiB
C#
97 lines
3.3 KiB
C#
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();
|
||
}
|
||
}
|