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>
46 lines
1.9 KiB
C#
46 lines
1.9 KiB
C#
using FluentAssertions;
|
||
using foodmarket.Application.Common;
|
||
using Xunit;
|
||
|
||
namespace foodmarket.UnitTests;
|
||
|
||
/// <summary>Нормализация телефонов KZ (Sprint 15: расширили покрытие
|
||
/// под цель 70% по Application). Контракт: пустой → null, 11 цифр '77...' →
|
||
/// +7..., с ведущей '8' → переписать в '7', невалидные → null.</summary>
|
||
public class PhoneNormalizationTests
|
||
{
|
||
[Theory]
|
||
[InlineData("+7 700 123 45 67", "+77001234567")]
|
||
[InlineData("87001234567", "+77001234567")]
|
||
[InlineData("77001234567", "+77001234567")]
|
||
[InlineData("8 (700) 123-45-67", "+77001234567")]
|
||
[InlineData(" +7-700-1234567 ", "+77001234567")]
|
||
public void Normalizes_valid_kz_numbers(string raw, string normalized)
|
||
=> PhoneNormalization.TryNormalizeKz(raw).Should().Be(normalized);
|
||
|
||
[Theory]
|
||
[InlineData(null)]
|
||
[InlineData("")]
|
||
[InlineData(" ")]
|
||
public void Empty_input_returns_null(string? raw)
|
||
=> PhoneNormalization.TryNormalizeKz(raw).Should().BeNull();
|
||
|
||
[Theory]
|
||
[InlineData("+79161234567")] // RU, начинается с 79
|
||
[InlineData("12345")] // слишком короткий
|
||
[InlineData("+77001234567890")] // слишком длинный
|
||
[InlineData("abcdef")] // нет цифр
|
||
[InlineData("+74950001234")] // RU 74…
|
||
public void Invalid_returns_null(string raw)
|
||
=> PhoneNormalization.TryNormalizeKz(raw).Should().BeNull();
|
||
|
||
[Fact]
|
||
public void IsValidOrEmpty_treats_null_as_valid()
|
||
{
|
||
PhoneNormalization.IsValidOrEmpty(null).Should().BeTrue();
|
||
PhoneNormalization.IsValidOrEmpty("").Should().BeTrue();
|
||
PhoneNormalization.IsValidOrEmpty("+77001234567").Should().BeTrue();
|
||
PhoneNormalization.IsValidOrEmpty("garbage").Should().BeFalse();
|
||
}
|
||
}
|