Census role gaps become RoleGaps, so a bin skipped by the census has a test; a bin authored on another line is refused
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
fb7b95fb06
commit
beb733f4b5
3 changed files with 55 additions and 10 deletions
|
|
@ -126,6 +126,13 @@ public sealed class ProductTiersCommandTests : IDisposable
|
|||
Refused(new ProductLinesCommand.TierRow("Sparrow", "Goldcrest", .5f, "x"), "already belongs");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void A_bin_already_authored_on_another_line_is_refused()
|
||||
{
|
||||
Assert.Equal(0, ProductLinesCommand.Run(Apply, _root));
|
||||
Refused(new ProductLinesCommand.TierRow("Spotlight", "Goldcrest", .67f, "x"), "already belongs");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void A_line_the_catalog_does_not_hold_is_refused()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -239,6 +239,36 @@ public sealed class RolesBackfillCommandTests : IDisposable
|
|||
finally { db.Cache.Dispose(); }
|
||||
}
|
||||
|
||||
// The census reports a seller that authors no spread for a role of its design as a gap, and never a bin: a bin reads its line's.
|
||||
[Fact]
|
||||
public void The_census_reports_a_roleless_seller_and_never_a_bin()
|
||||
{
|
||||
var db = AetherDb.Open(catalogWritable: true, root: _root);
|
||||
var autocannon = db.Cache.GetAll<WeaponItemData>().Single(weapon => weapon.Name == "Autocannon");
|
||||
var seller = db.Cache.GetAll<FactionProductData>().OrderBy(p => p.Name, StringComparer.Ordinal).First(p => p.Design.Key.Equals(db.Cache.RefOf(autocannon).Key));
|
||||
var sellerName = seller.Name;
|
||||
seller.Roles = new List<ProductRole>();
|
||||
db.Cache.Commit(batch =>
|
||||
{
|
||||
batch.Upsert(typeof(FactionProductData), seller, db.Cache.RefOf(seller).Key);
|
||||
batch.Upsert(typeof(FactionProductData), new FactionProductData
|
||||
{
|
||||
Name = "Binned " + sellerName, Design = seller.Design, Manufacturer = seller.Manufacturer,
|
||||
Bin = new ProductBin { Line = db.Cache.RefOf(seller), Floor = 0f }
|
||||
});
|
||||
});
|
||||
db.Cache.Dispose();
|
||||
|
||||
db = AetherDb.Open(root: _root);
|
||||
try
|
||||
{
|
||||
var gaps = Program.RoleGaps(db.Cache, db.Cache.GetAll<EquippableItemData>().ToArray(), db.Cache.GetAll<FactionProductData>().ToArray()).ToArray();
|
||||
Assert.Contains(gaps, gap => gap.Product.Name == sellerName && gap.Missing.Length == 2);
|
||||
Assert.DoesNotContain(gaps, gap => gap.Product.Name == "Binned " + sellerName);
|
||||
}
|
||||
finally { db.Cache.Dispose(); }
|
||||
}
|
||||
|
||||
// roles-migrate authors a role on every product that sells a design lacking one; a bin sells none of its own.
|
||||
[Fact]
|
||||
public void A_binned_seller_gains_no_roles_from_roles_migrate()
|
||||
|
|
|
|||
|
|
@ -129,25 +129,33 @@ public static class Program
|
|||
// or a product added after roles landed), so every sold, role-bearing design is checked against every
|
||||
// product that actually sells it, not only the ones this run's roles-migrate happened to touch.
|
||||
var roleGaps = 0;
|
||||
foreach (var item in items.Where(i => i.Roles != null && i.Roles.Count > 0))
|
||||
foreach (var (product, item, missing) in RoleGaps(db.Cache, items, products))
|
||||
{
|
||||
var design = db.Cache.RefOf(item).Key;
|
||||
// A bin authors no spread: it reads its line's (ProductBin).
|
||||
var sellers = products.Where(p => p.Bin == null && p.Design.Key.Equals(design)).ToArray();
|
||||
foreach (var product in sellers)
|
||||
{
|
||||
var covered = new HashSet<string>((product.Roles ?? new List<ProductRole>()).Select(r => r.Role));
|
||||
var missing = item.Roles.Select(r => r.Name).Where(r => !covered.Contains(r)).ToArray();
|
||||
if (missing.Length == 0) continue;
|
||||
roleGaps++;
|
||||
Console.WriteLine($" GAP: {product.Name} sells {item.Name} but authors no spread for: {string.Join(", ", missing)}" +
|
||||
" -- a newly minted lot rolls the ProductRole default (mean .5) for these, not this maker's own quality");
|
||||
}
|
||||
roleGaps++;
|
||||
Console.WriteLine($" GAP: {product.Name} sells {item.Name} but authors no spread for: {string.Join(", ", missing)}" +
|
||||
" -- a newly minted lot rolls the ProductRole default (mean .5) for these, not this maker's own quality");
|
||||
}
|
||||
Console.WriteLine($"\n{roleGaps} (product, role) pairs where a sold, role-bearing design's product authors no matching spread");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Every seller of a role-bearing design that authors no spread for one of its roles. A bin authors no spread: it reads its
|
||||
// line's (ProductBin), so it is never a gap.
|
||||
internal static IEnumerable<(FactionProductData Product, EquippableItemData Design, string[] Missing)> RoleGaps(
|
||||
GameCult.Caching.CultCache cache, IEnumerable<EquippableItemData> items, IEnumerable<FactionProductData> products)
|
||||
{
|
||||
foreach (var item in items.Where(i => i.Roles != null && i.Roles.Count > 0))
|
||||
{
|
||||
var design = cache.RefOf(item).Key;
|
||||
foreach (var product in products.Where(p => p.Bin == null && p.Design.Key.Equals(design)))
|
||||
{
|
||||
var covered = new HashSet<string>((product.Roles ?? new List<ProductRole>()).Select(r => r.Role));
|
||||
var missing = item.Roles.Select(r => r.Name).Where(r => !covered.Contains(r)).ToArray();
|
||||
if (missing.Length > 0) yield return (product, item, missing);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Cut 7 (docs/stats-and-power-cut.md): "products author quality, designs author roles." Zero designs
|
||||
// declared a role and zero products authored role quality before this command; every field->role table below
|
||||
// is the one place that content is decided, so the operator reviews a legible table instead of hand-picking
|
||||
|
|
|
|||
Loading…
Reference in a new issue