using System.ComponentModel.DataAnnotations; using AyCode.Services.Nav.Ekaer; using AyCode.Services.Nav.Ekaer.Models; using TradeReasonType = AyCode.Services.Nav.Ekaer.Models.Common.TradeReasonType; using TradeType = AyCode.Services.Nav.Ekaer.Models.Common.TradeType; namespace AyCode.Services.Tests.Nav; /// /// Unit tesztek az -ra: séma-szintű (generált DataAnnotations) és /// üzleti szabályok (vonó jármű, tétel, feladó/címzett) ellenőrzése, hibalista-jelleggel. A validátor /// általános NAV/EKÁER réteg — a teszt csak az AyCode.Services-re támaszkodik (semmi projekt-specifikus). /// [TestClass] public sealed class EkaerTradeCardValidatorTests { private static readonly EkaerTradeCardValidator Sut = new(); // ---- Helpers ------------------------------------------------------------ /// Teljesen érvényes Create művelet, egy érvényes tétellel. A tesztek ezt „rontják el". private static TradeCardOperationType ValidOperation() { var operation = new TradeCardOperationType { Index = 1, Operation = OperationType.Create, TradeCard = new TradeCardType { TradeType = TradeType.I, SellerName = "Beszállító Kft", SellerVatNumber = "12345678-2-42", SellerCountry = "HU", SellerAddress = "1011 Budapest Fo utca 1", DestinationName = "FruitBank Kft", DestinationVatNumber = "98765432-2-41", DestinationCountry = "HU", DestinationAddress = "1102 Budapest Raktar utca 5", Vehicle = new BasicVehicleDetailType { PlateNumber = "ABC123", Country = "HU" }, }, }; operation.TradeCard.Items.Add(new TradeCardItemType { ItemExternalId = "1", TradeReason = TradeReasonType.A, ProductVtsz = "08081010", ProductName = "Alma", Weight = 123.5m, }); return operation; } private static bool HasError(IReadOnlyList errors, string fragment) => errors.Any(e => e.ErrorMessage?.Contains(fragment, StringComparison.OrdinalIgnoreCase) == true); // ---- Érvényes eset ------------------------------------------------------ [TestMethod] public void Validate_ValidOperation_NoErrors() { var errors = Sut.Validate(ValidOperation()); Assert.AreEqual(0, errors.Count, $"érvényes művelet nem adhat hibát; kapott: {string.Join(" | ", errors.Select(e => e.ErrorMessage))}"); } // ---- Üzleti szabályok --------------------------------------------------- [TestMethod] public void Validate_MissingVehicle_ReportsError() { var op = ValidOperation(); op.TradeCard!.Vehicle = null; Assert.IsTrue(HasError(Sut.Validate(op), "vehicle"), "a hiányzó vonó jármű hibát ad"); } [TestMethod] public void Validate_NoItems_ReportsError() { var op = ValidOperation(); op.TradeCard!.Items.Clear(); Assert.IsTrue(HasError(Sut.Validate(op), "tétel"), "a tétel nélküli tradeCard hibát ad"); } [TestMethod] public void Validate_MissingSellerName_ReportsError() { var op = ValidOperation(); op.TradeCard!.SellerName = null; Assert.IsTrue(HasError(Sut.Validate(op), "feladó"), "hiányzó feladónév → hiba"); } [TestMethod] public void Validate_MissingDestinationName_ReportsError() { var op = ValidOperation(); op.TradeCard!.DestinationName = " "; Assert.IsTrue(HasError(Sut.Validate(op), "címzett"), "üres/whitespace címzettnév → hiba"); } // ---- Séma-szint (generált DataAnnotations) ------------------------------ [TestMethod] public void Validate_InvalidVtszPattern_ReportsError() { var op = ValidOperation(); op.TradeCard!.Items[0].ProductVtsz = "12"; // pattern [0-9]{4,8} → túl rövid Assert.IsTrue(HasError(Sut.Validate(op), "productVtsz"), "rossz VTSZ-formátum → séma hiba"); } [TestMethod] public void Validate_NullVtsz_ReportsError() { var op = ValidOperation(); op.TradeCard!.Items[0].ProductVtsz = null; // a Required kapja el Assert.IsTrue(HasError(Sut.Validate(op), "productVtsz"), "null VTSZ → Required hiba"); } [TestMethod] public void Validate_EmptyVtsz_ReportsError() { var op = ValidOperation(); op.TradeCard!.Items[0].ProductVtsz = ""; // a séma átengedné — az üzleti szabály kapja el Assert.IsTrue(HasError(Sut.Validate(op), "productVtsz"), "üres VTSZ → üzleti szabály hiba"); } [TestMethod] public void Validate_InvalidPlateNumber_ReportsError() { var op = ValidOperation(); op.TradeCard!.Vehicle!.PlateNumber = "AB"; // MinLength 4 / pattern sérül var errors = Sut.Validate(op); Assert.IsTrue(HasError(errors, "plateNumber") || HasError(errors, "vehicle"), "túl rövid rendszám → séma hiba"); } [TestMethod] public void Validate_MissingProductName_ReportsError() { var op = ValidOperation(); op.TradeCard!.Items[0].ProductName = null; // [Required(AllowEmptyStrings=false)] Assert.IsTrue(HasError(Sut.Validate(op), "productName"), "hiányzó terméknév → séma hiba"); } // ---- Lista / művelet-szint ---------------------------------------------- [TestMethod] public void Validate_EmptyList_ReportsError() { var errors = Sut.Validate(new List()); Assert.IsTrue(HasError(errors, "legalább egy"), "üres lista → hiba"); } [TestMethod] public void Validate_DeleteWithoutTradeCard_DoesNotRequireTradeCard() { var op = new TradeCardOperationType { Index = 1, Operation = OperationType.Delete, Tcn = "ABC123" }; Assert.IsFalse(HasError(Sut.Validate(op), "a tradeCard kötelező"), "delete + Tcn esetén a tradeCard hiánya nem hiba"); } [TestMethod] public void Validate_NonDeleteWithoutTradeCard_ReportsError() { var op = new TradeCardOperationType { Index = 1, Operation = OperationType.Create, TradeCard = null }; Assert.IsTrue(HasError(Sut.Validate(op), "tradeCard"), "create esetén a tradeCard kötelező"); } // ---- Hibalista-jelleg: minden hibát összegyűjt -------------------------- [TestMethod] public void Validate_CollectsAllErrors_DoesNotStopAtFirst() { var op = ValidOperation(); op.TradeCard!.Vehicle = null; // üzleti op.TradeCard.Items.Clear(); // üzleti op.TradeCard.SellerName = null; // üzleti op.TradeCard.DestinationVatNumber = null; // üzleti var errors = Sut.Validate(op); Assert.IsTrue(errors.Count >= 4, $"minden hibát összegyűjt, nem áll meg az elsőnél; kapott {errors.Count}"); } [TestMethod] public void Validate_IndexedPath_WhenMultipleOperations() { var bad = ValidOperation(); bad.TradeCard!.Vehicle = null; var errors = Sut.Validate([ValidOperation(), bad]); Assert.IsTrue(errors.Any(e => e.ErrorMessage?.Contains("[1]") == true), "a hibák a művelet indexével prefixeltek"); } }