using Vanguard.Rules.Cards; using Vanguard.Rules.Decks; using Xunit; namespace Vanguard.Rules.Tests; public class DeckValidatorTests { private readonly CardCatalog _catalog = TestCatalog.Instance; [Fact] public void Legal_Deck_Has_No_Violations() { var deck = BuildLegalDeck(); Assert.Empty(DeckValidator.Validate(_catalog, deck)); } [Fact] public void Deck_Must_Have_Fifty_Cards() { var deck = BuildLegalDeck(); deck.RemoveAt(0); var violations = DeckValidator.Validate(_catalog, deck); Assert.Contains(violations, v => v.Code == "deck.size"); } [Fact] public void Copy_Limit_Counts_By_Name_Across_Printings() { var deck = DeckWith(("BT01/002EN", 4), ("TD01/005EN", 4)); var violations = DeckValidator.Validate(_catalog, deck); var copies = Assert.Single(violations, v => v.Code == "deck.copies"); Assert.Contains("Blaster Blade", copies.Message); } [Fact] public void Copy_Limit_Counts_Parallel_Art_As_Same_Card() { var deck = DeckWith(("BT01/002EN", 4), ("BT01/S02EN", 4)); var violations = DeckValidator.Validate(_catalog, deck); Assert.Contains(violations, v => v.Code == "deck.copies"); } [Fact] public void Sentinel_Limit_Is_Four() { var deck = DeckWith(("BT01/011EN", 4), ("BT01/015EN", 1)); var violations = DeckValidator.Validate(_catalog, deck); Assert.Contains(violations, v => v.Code == "deck.sentinel"); } [Fact] public void Heal_Limit_Is_Four() { var deck = DeckWith(("BT01/047EN", 4), ("BT01/053EN", 1)); var violations = DeckValidator.Validate(_catalog, deck); Assert.Contains(violations, v => v.Code == "deck.heal"); } [Fact] public void Unknown_Printing_Is_Reported() { var deck = BuildLegalDeck(); deck[0] = "BT99/999EN"; var violations = DeckValidator.Validate(_catalog, deck); Assert.Contains(violations, v => v.Code == "deck.unknown-printing"); } private List BuildLegalDeck() => DeckWith(); /// /// Собирает колоду на 50 карт: сначала заданные принты, затем по 4 копии /// каждого имени, кроме Sentinel и Heal (чтобы не упираться в их лимиты). /// private List DeckWith(params (string PrintingId, int Count)[] seed) { var deck = new List(); var usedNames = new HashSet(StringComparer.Ordinal); foreach (var (printingId, count) in seed) { deck.AddRange(Enumerable.Repeat(printingId, count)); usedNames.Add(_catalog.Printing(printingId).NameId); } foreach (var group in _catalog.Printings .Where(p => !p.IsParallel) .GroupBy(p => p.NameId) .OrderBy(g => g.Key, StringComparer.Ordinal)) { if (deck.Count >= DeckValidator.RequiredSize) break; if (usedNames.Contains(group.Key)) continue; var card = _catalog.Card(group.Key); if (card.IsSentinel || card.Trigger == TriggerType.Heal) continue; var printingId = group.First().PrintingId; usedNames.Add(group.Key); for (var i = 0; i < DeckValidator.MaxCopiesPerName && deck.Count < DeckValidator.RequiredSize; i++) deck.Add(printingId); } return deck; } }