163 lines
7.0 KiB
C#
163 lines
7.0 KiB
C#
namespace HSchool.People.Tests;
|
|
|
|
public class YearlyIntakeTests
|
|
{
|
|
private static readonly DateTime September = new(2012, 9, 1, 0, 0, 0, DateTimeKind.Utc);
|
|
|
|
[Fact]
|
|
public void DatesBetween_SkipsAStartAlreadyOnFirstSeptember()
|
|
{
|
|
var start = new DateTime(2012, 9, 1, 0, 0, 0, DateTimeKind.Utc);
|
|
Assert.Empty(YearlyIntake.DatesBetween(start, start.AddMinutes(1)));
|
|
Assert.Equal(
|
|
[new DateTime(2012, 9, 1, 0, 0, 0, DateTimeKind.Utc)],
|
|
YearlyIntake.DatesBetween(new DateTime(2012, 4, 3, 6, 0, 0, DateTimeKind.Utc), start));
|
|
}
|
|
|
|
[Fact]
|
|
public void FourClassrooms_GraduatesTheOldestYearNotEleventh()
|
|
{
|
|
var map = Fixtures.Classrooms(4);
|
|
var catalog = Fixtures.Catalog();
|
|
var before = Fixtures.Generate(map);
|
|
Assert.Equal(4, before.Classes.Max(schoolClass => schoolClass.Year));
|
|
Assert.DoesNotContain(before.Classes, schoolClass => schoolClass.Year == 11);
|
|
|
|
var after = YearlyIntake.Apply(catalog, before, Fixtures.SchoolSeed, "Russia", September);
|
|
var demand = SchoolDemand.From(catalog, map);
|
|
|
|
Assert.True(RosterFit.Matches(after, demand));
|
|
Assert.Equal(before.People.Count(person => person.IsStudent), after.People.Count(person => person.IsStudent));
|
|
Assert.Equal(before.Classes.Sum(schoolClass => schoolClass.Capacity), after.Classes.Sum(schoolClass => schoolClass.Capacity));
|
|
Assert.Equal(4, after.Classes.Max(schoolClass => schoolClass.Year));
|
|
Assert.Contains(after.Classes, schoolClass => schoolClass.Year == 1);
|
|
Assert.All(after.Classes, schoolClass => Assert.Equal(schoolClass.Capacity, schoolClass.PupilIds.Count));
|
|
|
|
var graduated = before.Classes
|
|
.Where(schoolClass => schoolClass.Year == 4)
|
|
.SelectMany(schoolClass => schoolClass.PupilIds)
|
|
.ToHashSet(StringComparer.Ordinal);
|
|
Assert.All(graduated, id => Assert.DoesNotContain(after.People, person => person.Id == id));
|
|
}
|
|
|
|
[Fact]
|
|
public void SameInputs_YieldTheSameIntake()
|
|
{
|
|
var map = Fixtures.Classrooms(4);
|
|
var catalog = Fixtures.Catalog();
|
|
var before = Fixtures.Generate(map);
|
|
var a = Snapshot(YearlyIntake.Apply(catalog, before, Fixtures.SchoolSeed, "Russia", September));
|
|
var b = Snapshot(YearlyIntake.Apply(catalog, before, Fixtures.SchoolSeed, "Russia", September));
|
|
Assert.Equal(a, b);
|
|
}
|
|
|
|
[Fact]
|
|
public void NewFirstYear_CanShareAFamilyWithAnOlderPupil()
|
|
{
|
|
var map = Fixtures.Classrooms(4);
|
|
var catalog = Fixtures.Catalog();
|
|
var before = Fixtures.Generate(map);
|
|
var after = YearlyIntake.Apply(catalog, before, Fixtures.SchoolSeed, "Russia", September);
|
|
var people = after.People.ToDictionary(person => person.Id, StringComparer.Ordinal);
|
|
var yearOf = after.Classes.ToDictionary(schoolClass => schoolClass.Id, schoolClass => schoolClass.Year, StringComparer.Ordinal);
|
|
|
|
Assert.Contains(after.Families, family =>
|
|
{
|
|
var years = family.ChildIds
|
|
.Select(id => people[id])
|
|
.Where(person => person.IsStudent && person.ClassId is not null)
|
|
.Select(person => yearOf[person.ClassId!])
|
|
.ToHashSet();
|
|
return years.Contains(1) && years.Any(year => year > 1);
|
|
});
|
|
}
|
|
|
|
[Fact]
|
|
public void ParentsOfOnlyGraduates_LeaveUnlessTheyWorkHere()
|
|
{
|
|
var map = Fixtures.Classrooms(4);
|
|
var catalog = Fixtures.Catalog();
|
|
var before = Fixtures.Generate(map);
|
|
var people = before.People.ToDictionary(person => person.Id, StringComparer.Ordinal);
|
|
var graduating = before.Classes
|
|
.Where(schoolClass => schoolClass.Year == 4)
|
|
.SelectMany(schoolClass => schoolClass.PupilIds)
|
|
.ToHashSet(StringComparer.Ordinal);
|
|
|
|
var shouldLeave = new List<string>();
|
|
var staffWhoStay = new List<string>();
|
|
foreach (var family in before.Families)
|
|
{
|
|
if (family.ChildIds.Count == 0 || family.ChildIds.Any(id => !graduating.Contains(id)))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
foreach (var parentId in family.ParentIds)
|
|
{
|
|
if (people[parentId].IsStaff)
|
|
{
|
|
staffWhoStay.Add(parentId);
|
|
}
|
|
else
|
|
{
|
|
shouldLeave.Add(parentId);
|
|
}
|
|
}
|
|
}
|
|
|
|
var after = YearlyIntake.Apply(catalog, before, Fixtures.SchoolSeed, "Russia", September);
|
|
var afterIds = after.People.Select(person => person.Id).ToHashSet(StringComparer.Ordinal);
|
|
Assert.NotEmpty(shouldLeave);
|
|
Assert.All(shouldLeave, id => Assert.DoesNotContain(id, afterIds));
|
|
Assert.All(staffWhoStay, id => Assert.Contains(id, afterIds));
|
|
Assert.All(
|
|
after.People.Where(person => staffWhoStay.Contains(person.Id)),
|
|
person => Assert.False(person.IsParent));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Slice 3 stopped generating staff, so the "unless they work here" branch of the test above
|
|
/// is vacuously empty. Hire still marks a parent <c>IsStaff</c>; intake must not drop them
|
|
/// with the graduates' civilian co-parent.
|
|
/// </summary>
|
|
[Fact]
|
|
public void StaffParentOfOnlyGraduates_StaysAfterIntake()
|
|
{
|
|
var map = Fixtures.Classrooms(4);
|
|
var catalog = Fixtures.Catalog();
|
|
var generated = Fixtures.Generate(map);
|
|
var graduating = generated.Classes
|
|
.Where(schoolClass => schoolClass.Year == 4)
|
|
.SelectMany(schoolClass => schoolClass.PupilIds)
|
|
.ToHashSet(StringComparer.Ordinal);
|
|
var family = generated.Families.First(candidate =>
|
|
candidate.ChildIds.Count > 0
|
|
&& candidate.ChildIds.All(id => graduating.Contains(id)));
|
|
var staffId = family.ParentIds[0];
|
|
var civilians = family.ParentIds.Skip(1).ToArray();
|
|
var before = generated with
|
|
{
|
|
People = generated.People
|
|
.Select(person => person.Id.Equals(staffId, StringComparison.Ordinal)
|
|
? person with { IsStaff = true, Position = "Librarian" }
|
|
: person)
|
|
.ToArray(),
|
|
};
|
|
|
|
var after = YearlyIntake.Apply(catalog, before, Fixtures.SchoolSeed, "Russia", September);
|
|
var stayed = Assert.Single(after.People, person => person.Id == staffId);
|
|
|
|
Assert.True(stayed.IsStaff);
|
|
Assert.False(stayed.IsParent);
|
|
Assert.All(civilians, id => Assert.DoesNotContain(after.People, person => person.Id == id));
|
|
}
|
|
|
|
private static string Snapshot(Roster roster) =>
|
|
string.Join('\n', roster.People.Select(person =>
|
|
$"{person.Id}|{person.FamilyId}|{person.IsStudent}|{person.IsStaff}|{person.IsParent}|{person.ClassId}|{person.Name.Full}|{Skills(person)}"));
|
|
|
|
private static string Skills(Person person) =>
|
|
string.Join(',', person.Skills.OrderBy(pair => pair.Key, StringComparer.Ordinal).Select(pair => $"{pair.Key}={pair.Value}"));
|
|
}
|