using XFEEDToolBox.Core.Engineering; using XFEEDToolBox.Core.Presentation; using XFEExtension.NetCore.XUnit.Attributes; namespace XFEEDToolBox.Test; [TestFixture] public class MaterialWarehouseTests { private static MaterialStock[] Stocks => [ new("nickel", "Nickel", "Raw", 1, 40, 300), new("arsenic", "Arsenic", "Raw", 2, 0, 250), new("selenium", "Selenium", "Raw", 4, 150, 150), new("conductivecomponents", "Conductive Components", "Manufactured", 1, 81, 300), new("chemical", "Chemical Manipulators", "Manufactured", 4, 6, 150), new("encoded", "Encoded Data", "Encoded", 5, 20, 100), new("unknown", "Unknown Material", "Other", 0, 9, 0) ]; [Test] public void CategoryOwnershipGradeAndQuantityFiltersCombine() { var result = MaterialWarehouseQuery.Apply(Stocks, new() { Category = "raw", Ownership = MaterialOwnership.Unowned, MinimumGrade = 2, MaximumGrade = 4, MinimumQuantity = 0, MaximumQuantity = 0 }); Check.Equal("arsenic", result.Single().Key); Check.Equal(6, MaterialWarehouseQuery.Apply(Stocks, new() { Ownership = MaterialOwnership.Owned }).Count); Check.Equal(7, MaterialWarehouseQuery.Apply(Stocks, new()).Count); Check.Equal(0, MaterialWarehouseQuery.Apply(Stocks, new() { Ownership = MaterialOwnership.Unowned, MinimumQuantity = 1 }).Count); } [Test] public void SearchIgnoresWhitespaceAndCaseAndMatchesGameKeys() { Check.Equal("conductivecomponents", MaterialWarehouseQuery.Apply(Stocks, new() { Search = " COMPONENTS\tconductive " }).Single().Key); Check.Equal("conductivecomponents", MaterialWarehouseQuery.Apply(Stocks, new() { Search = "CONDUCTIVECOMPONENTS" }).Single().Key); Check.Equal(Stocks.Length, MaterialWarehouseQuery.Apply(Stocks, new() { Search = " \t " }).Count); Check.Equal(0, MaterialWarehouseQuery.Apply(Stocks, new() { Search = "no such material" }).Count); } [Test] public void PercentageBoundariesExcludeUnknownCapacity() { MaterialStock[] rows = [new("quarter", "Quarter", "Raw", 1, 25, 100), new("half", "Half", "Raw", 1, 50, 100), new("near", "Near", "Raw", 1, 80, 100), new("full", "Full", "Raw", 1, 100, 100), new("unknown", "Unknown", "Other", 0, 0, 0)]; Check.Equal(0, MaterialWarehouseQuery.Apply(rows, new() { StockLevel = MaterialStockLevel.BelowQuarter }).Count); Check.Equal("quarter", MaterialWarehouseQuery.Apply(rows, new() { StockLevel = MaterialStockLevel.BelowHalf }).Single().Key); Check.Equal(2, MaterialWarehouseQuery.Apply(rows, new() { StockLevel = MaterialStockLevel.NearlyFull }).Count); Check.Equal("full", MaterialWarehouseQuery.Apply(rows, new() { StockLevel = MaterialStockLevel.Full }).Single().Key); Check.Equal(3, MaterialWarehouseQuery.Apply(rows, new() { StockLevel = MaterialStockLevel.NotFull }).Count); Check.Equal("unknown", MaterialWarehouseQuery.Apply(rows, new() { StockLevel = MaterialStockLevel.UnknownCapacity }).Single().Key); } [Test] public void QuantitiesGradesAndRatiosSortNumericallyInBothDirections() { var ascending = MaterialWarehouseQuery.Apply(Stocks, new() { Sort = MaterialSort.Quantity }); Check.That(ascending.Select(m => m.Owned).SequenceEqual(new[] { 0, 6, 9, 20, 40, 81, 150 })); var descending = MaterialWarehouseQuery.Apply(Stocks, new() { Sort = MaterialSort.Quantity, Descending = true }); Check.That(descending.Select(m => m.Owned).SequenceEqual(new[] { 150, 81, 40, 20, 9, 6, 0 })); Check.Equal(5, MaterialWarehouseQuery.Apply(Stocks, new() { Sort = MaterialSort.Grade, Descending = true }).First().Grade); var filled = MaterialWarehouseQuery.Apply(Stocks, new() { Sort = MaterialSort.Fill, Descending = true }); Check.That(filled.Select(m => m.Key).SequenceEqual(new[] { "selenium", "conductivecomponents", "encoded", "nickel", "chemical", "arsenic", "unknown" })); foreach (var sort in new[] { MaterialSort.Fill, MaterialSort.RemainingCapacity }) foreach (var direction in new[] { false, true }) Check.Equal("unknown", MaterialWarehouseQuery.Apply(Stocks, new() { Sort = sort, Descending = direction }).Last().Key); Check.Equal("selenium", MaterialWarehouseQuery.Apply(Stocks, new() { Sort = MaterialSort.RemainingCapacity }).First().Key); Check.Equal("nickel", MaterialWarehouseQuery.Apply(Stocks, new() { Sort = MaterialSort.RemainingCapacity, Descending = true }).First().Key); } [Test] public void DefaultOrderGroupsCategoriesAndKeepsTiesStable() { var first = MaterialWarehouseQuery.Apply(Stocks, new()); Check.That(first.Select(m => m.Key).SequenceEqual(new[] { "selenium", "arsenic", "nickel", "chemical", "conductivecomponents", "encoded", "unknown" })); var reversed = MaterialWarehouseQuery.Apply(Stocks.Reverse(), new()); Check.That(first.SequenceEqual(reversed)); Check.Equal("Arsenic", MaterialWarehouseQuery.Apply(Stocks, new() { Sort = MaterialSort.Name }).First().Name); Check.Equal("Unknown Material", MaterialWarehouseQuery.Apply(Stocks, new() { Sort = MaterialSort.Name, Descending = true }).First().Name); } [Test] public void ChecklistUsesCurrentStockInsteadOfStaleOwnedCounts() { MaterialNeed[] needs = [new("NICKEL", "Nickel", 30, 0), new("conductivecomponents", "Conductive Components", 60, 999), new("CONDUCTIVECOMPONENTS", "Conductive Components", 40, 999), new("arsenic", "Arsenic", 0, 0)]; Check.Equal("conductivecomponents", MaterialWarehouseQuery.Apply(Stocks, new() { Checklist = MaterialChecklistFilter.Missing }, needs).Single().Key); Check.Equal("nickel", MaterialWarehouseQuery.Apply(Stocks, new() { Checklist = MaterialChecklistFilter.Ready }, needs).Single().Key); Check.Equal(2, MaterialWarehouseQuery.Apply(Stocks, new() { Checklist = MaterialChecklistFilter.Tracked }, needs).Count); Check.Equal("conductivecomponents", MaterialWarehouseQuery.Apply(Stocks, new() { Sort = MaterialSort.Shortage, Descending = true }, needs).First().Key); Check.Equal(0, MaterialWarehouseQuery.Apply(Stocks, new() { Checklist = MaterialChecklistFilter.Tracked }, []).Count); } [Test] public void InvalidRangesReturnActionableValidationAndNoResults() { MaterialWarehouseFilter[] invalid = [ new() { MinimumGrade = 4, MaximumGrade = 2 }, new() { MinimumQuantity = 50, MaximumQuantity = 10 }, new() { MinimumQuantity = -1 }, new() { MaximumQuantity = .5 }, new() { MinimumQuantity = double.PositiveInfinity } ]; foreach (var filter in invalid) { Check.That(filter.ValidationMessage.Length > 0); Check.Equal(0, MaterialWarehouseQuery.Apply(Stocks, filter).Count); } Check.Equal("", new MaterialWarehouseFilter { MaximumGrade = 3, MinimumQuantity = 0 }.ValidationMessage); Check.That(MaterialWarehouseQuery.Apply(Stocks, new() { MaximumGrade = 3 }).All(m => m.Grade is > 0 and <= 3)); } [Test] public void LiveUpdatesReorderExistingRowsAndRemoveNewlyCoveredShortages() { var live = new LiveCollection(m => m.Key, m => new(m)); MaterialWarehouseFilter filter = new() { Sort = MaterialSort.Quantity, Descending = true }; live.Apply(MaterialWarehouseQuery.Apply(Stocks, filter)); var nickel = live.Single(m => m.Key == "nickel"); var changed = Stocks.Select(m => m.Key == "nickel" ? m with { Owned = 200 } : m).ToArray(); var delta = live.Apply(MaterialWarehouseQuery.Apply(changed, filter)); Check.That(ReferenceEquals(nickel, live.First())); Check.Equal(200, nickel.Owned); Check.Equal(0, delta.Added + delta.Removed); MaterialNeed[] needs = [new("nickel", "Nickel", 100, 0)]; Check.Equal(1, MaterialWarehouseQuery.Apply(Stocks, new() { Checklist = MaterialChecklistFilter.Missing }, needs).Count); Check.Equal(0, MaterialWarehouseQuery.Apply(changed, new() { Checklist = MaterialChecklistFilter.Missing }, needs).Count); } }