food-market/tests/food-market.UnitTests/PhoneNormalizationTests.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

46 lines
1.9 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.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();
}
}