Files
h-school/tests/HSchool.People.Tests/DressGeneratorTests.cs
T
Leonid PershinandCursor 37501c506a Dress people at generation from a separate apparel stream.
Wardrobes live on the person in people.json: everyday layers, textbooks per parallel, and chance-based bags from catalog data. Hauling and the carry cap are defs, not constants. The golden roster gains an items column; later family members shift because Hauling consumes extra appearance rolls. Old saves without items are dressed on load.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-20 03:24:54 +03:00

155 lines
6.0 KiB
C#

namespace HSchool.People.Tests;
public class DressGeneratorTests
{
[Fact]
public void SameSeedCountryMapAndLanguage_YieldTheSameItemsColorsAndPlaces()
{
var map = Fixtures.Classrooms(11);
var a = Fixtures.Generate(map);
var b = Fixtures.Generate(map);
Assert.Equal(ItemsSnapshot(a), ItemsSnapshot(b));
}
[Fact]
public void WornLayers_DoNotOverlap_AndADressFillsBottomAndTop()
{
var catalog = Fixtures.Catalog();
var roster = Fixtures.Generate(Fixtures.Classrooms(11));
foreach (var person in roster.People)
{
var occupied = new HashSet<string>(StringComparer.Ordinal);
foreach (var item in person.Items.Where(row => row.Location == ItemLocations.Worn))
{
Assert.True(catalog.Things.TryGetValue(item.Def, out var def));
foreach (var layer in def.Layers)
{
Assert.True(occupied.Add(layer), $"{person.Id} wears two things on {layer}.");
}
}
}
Assert.Contains(
roster.People,
person => person.Items.Any(item =>
item.Location == ItemLocations.Worn && item.Def.Equals("Dress", StringComparison.Ordinal)));
var dressed = roster.People.First(person =>
person.Items.Any(item => item.Location == ItemLocations.Worn && item.Def == "Dress"));
var worn = dressed.Items.Where(item => item.Location == ItemLocations.Worn).Select(item => item.Def).ToHashSet();
Assert.DoesNotContain("TShirt", worn);
Assert.DoesNotContain("Shirt", worn);
Assert.DoesNotContain("Skirt", worn);
Assert.DoesNotContain("Pants", worn);
}
[Fact]
public void Men_DoNotWearSkirts_AndWornColors_AreNotBright()
{
var catalog = Fixtures.Catalog();
var roster = Fixtures.Generate(Fixtures.Classrooms(11));
foreach (var person in roster.People)
{
foreach (var item in person.Items.Where(row => row.Location == ItemLocations.Worn))
{
Assert.True(catalog.Things.TryGetValue(item.Def, out var def));
if (!person.Female)
{
Assert.False(
def.Sex is not null && def.Sex.Equals("female", StringComparison.OrdinalIgnoreCase),
$"{person.Id} wears {item.Def}.");
}
if (item.Color is { } color
&& catalog.Colors.TryGetValue(color, out var colorDef))
{
Assert.DoesNotContain(ColorTags.Bright, colorDef.Tags, StringComparer.Ordinal);
}
}
}
}
[Fact]
public void PupilInAMathematicsYear_HasAMathematicsTextbook()
{
var roster = Fixtures.Generate(Fixtures.Classrooms(11));
var years = roster.Classes.ToDictionary(row => row.Id, row => row.Year, StringComparer.Ordinal);
var pupil = roster.People.First(person =>
person.IsStudent
&& person.ClassId is { } classId
&& years[classId] >= 5);
Assert.Contains(
pupil.Items,
item => item.Def.Equals("Textbook", StringComparison.Ordinal)
&& item.Subject is not null
&& item.Subject.Equals("Mathematics", StringComparison.Ordinal));
}
[Fact]
public void ParentsAndApplicants_AreDressed()
{
var catalog = Fixtures.Catalog();
var roster = Fixtures.Generate(Fixtures.VanillaMap());
var pool = ApplicantPool.Create(catalog, roster, Fixtures.SchoolSeed, "Russia", Fixtures.AsOf);
Assert.Contains(roster.People, person => person.IsParent);
Assert.All(
roster.People.Where(person => person.IsParent),
parent => Assert.Contains(parent.Items, item => item.Location == ItemLocations.Worn));
Assert.NotEmpty(pool.Applicants);
Assert.All(
pool.Applicants,
applicant => Assert.Contains(applicant.Person.Items, item => item.Location == ItemLocations.Worn));
}
[Fact]
public void BagMass_DoesNotExceedCapacity()
{
var catalog = Fixtures.Catalog();
var roster = Fixtures.Generate(Fixtures.Classrooms(11));
var pool = ApplicantPool.Create(catalog, roster, Fixtures.SchoolSeed, "Russia", Fixtures.AsOf);
foreach (var person in roster.People.Concat(pool.Applicants.Select(row => row.Person)))
{
var held = CarryMass.Held(catalog, person.Items);
var cap = CarryMass.Capacity(catalog, person.Skills);
Assert.True(held <= cap, $"{person.Id} carries {held} over {cap}.");
}
}
[Fact]
public void PeopleJsonWithoutInventory_IsDressedOnEnsure()
{
var catalog = Fixtures.Catalog();
var roster = Fixtures.Generate(Fixtures.Classrooms(4));
var stripped = new Roster(
roster.People.Select(person => person with { Items = [] }).ToArray(),
roster.Families,
roster.Classes);
Assert.True(DressGenerator.NeedsDressing(stripped, ApplicantPool.Empty));
var dressed = DressGenerator.EnsureRoster(catalog, stripped, Fixtures.SchoolSeed, Fixtures.AsOf);
Assert.False(DressGenerator.NeedsDressing(dressed, ApplicantPool.Empty));
Assert.Equal(ItemsSnapshot(roster), ItemsSnapshot(dressed));
}
[Fact]
public void EveryoneHasHauling()
{
var roster = Fixtures.Generate(Fixtures.Classrooms(4));
Assert.All(roster.People, person => Assert.True(person.Skills.ContainsKey("Hauling")));
}
private static string ItemsSnapshot(Roster roster) =>
string.Join(
'\n',
roster.People.OrderBy(person => person.Id, StringComparer.Ordinal).Select(person =>
$"{person.Id}|" + string.Join(
',',
person.Items.Select(item =>
$"{item.Def}:{item.Color ?? "-"}:{item.Location}:{item.Subject ?? "-"}:{item.Condition}"))));
}