Implement yearly intake functionality in school simulation, allowing for the graduation of students and the addition of new first-year pupils. Update the roster management to reflect these changes, ensuring proper entity handling in the simulation. Enhance the API to support name sets during roster installation and revise related tests to validate the new intake process and roster updates.
This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
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, "Slavic", 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, "Slavic", September));
|
||||
var b = Snapshot(YearlyIntake.Apply(catalog, before, Fixtures.SchoolSeed, "Slavic", 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, "Slavic", 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, "Slavic", 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));
|
||||
}
|
||||
|
||||
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}"));
|
||||
}
|
||||
Reference in New Issue
Block a user