using FluentAssertions; using foodmarket.Api.Infrastructure.Invoices; using Xunit; namespace foodmarket.UnitTests; /// Контрольная цифра EAN-8/EAN-13/UPC-A/GTIN-14. Проверка нужна в /// фото-приёмке: если модель переписала цифры штрихкода со снимка товара /// сама (barcode_source="read"), одна ошибка в любой цифре иначе /// молча дала бы чужой товар. Порт из ~/ref/sklad. public class EanTests { // Валидные настоящие штрихкоды (из handoff: «15/15 реальных проходят»). public static TheoryData Valid => new() { "4870001234560", // EAN-13 "4607031440148", // EAN-13 (пример из KZ-ритейла) "0012000161551", // UPC-A дополненный до 13 "73513537", // EAN-8 "012345678905", // UPC-A (12) }; [Theory, MemberData(nameof(Valid))] public void Valid_passes(string digits) => Ean.IsValid(digits).Should().BeTrue(); [Fact] public void Broken_check_digit_fails() { // Все эти — валидные минус один «кривой» символ контрольной. Ean.IsValid("4870001234561").Should().BeFalse(); Ean.IsValid("4607031440149").Should().BeFalse(); } [Theory] [InlineData("")] [InlineData(null)] [InlineData("12345")] // короткий [InlineData("12345678901")] // 11 — не стандарт [InlineData("12345678901A")] // буква [InlineData("4870 001 234 56")] // пробелы — Ean.IsValid смотрит на точные цифры public void Non_digit_or_bad_length_fails(string? d) => Ean.IsValid(d).Should().BeFalse(); [Fact] public void One_digit_error_in_middle_caught() { // Из handoff: «15/15 с одной испорченной цифрой ловятся» — // гарантируется весовой суммой 3,1,3,1,… var ok = "4870001234560"; foreach (var i in new[] { 0, 3, 5, 8, 11 }) { var mutated = ok.ToCharArray(); mutated[i] = (char)('0' + ((mutated[i] - '0' + 5) % 10)); // ± 5 в этой позиции Ean.IsValid(new string(mutated)).Should().BeFalse( $"позиция {i}: ошибку в одной цифре должен поймать"); } } }