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<MaterialLiveRow, MaterialStock, string>(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);
}
}
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<MaterialLiveRow, MaterialStock, string>(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);
}
}