Enhance people management tests and fix related issues
ci / server (push) Failing after 3m44s
ci / client (push) Successful in 15s

- Updated `PeopleApiTests` to correctly handle family structures, ensuring tests account for single-parent scenarios and sorting behavior for surnames.
- Added a new test to validate roster integrity after the yearly intake, confirming that the composition reflects changes post-intake.
- Revised assertions to ensure accurate comparisons of student and family data, improving test reliability.
- Enhanced documentation within tests to clarify the purpose and expected outcomes of new functionalities.
- Addressed discrepancies between design and implementation regarding the layout of the people management interface.
This commit is contained in:
Leonid Pershin
2026-08-19 21:31:01 +03:00
parent 5cd5a6d258
commit 21d79cb49b
4 changed files with 275 additions and 8 deletions
@@ -197,12 +197,75 @@ public class StaffingApiTests(AppHostFixture fixture)
var restored = await GetStaffingAsync(client, school.Id);
Assert.Equal(afterDrop.Payroll, restored.Payroll);
// Phase 11: the pool lives in the same file and has to come back with it, asks included.
Assert.Equal(
afterDrop.Applicants.Select(applicant => (applicant.Id, applicant.HourlyWageAsk)),
restored.Applicants.Select(applicant => (applicant.Id, applicant.HourlyWageAsk)));
Assert.Equal(teacher.Id, restored.Staff.Single().Id);
Assert.Equal(
afterDrop.Staff.Single().Subjects.Select(subject => subject.DefName),
restored.Staff.Single().Subjects.Select(subject => subject.DefName));
}
/// <summary>
/// Phase 12 wants the cap refused at the moment of the action, with the numbers in the answer.
/// Reaching it means loading teachers up, not hiring more: a subject splits its hours between
/// everyone who teaches it, so the payroll peaks while few teachers carry many hours and falls
/// again once the load is spread. Measured over thirty seeds, this sweep always crosses.
/// </summary>
[Fact]
public async Task AssigningPastTheCap_IsRejectedWithTheNumbers()
{
using var client = fixture.App.CreateHttpClient("server");
await SchoolApiTests.ResetAsync(client);
var school = await SchoolApiTests.CreateAsync(client, "Штат перебор", Start);
var staffing = await GetStaffingAsync(client, school.Id);
while (staffing.Applicants.Count > 0)
{
staffing = await HireAsync(client, school.Id, staffing.Applicants[0].Id, "Teacher");
}
var subjects = staffing.Subjects.Select(subject => subject.DefName).ToArray();
Assert.NotEmpty(subjects);
foreach (var member in staffing.Staff)
{
foreach (var subject in subjects)
{
using var response = await client.PostAsJsonAsync(
$"/api/schools/{school.Id}/staff/{Uri.EscapeDataString(member.Id)}/subjects",
new { subject },
TestContext.Current.CancellationToken);
if (response.StatusCode != HttpStatusCode.Conflict)
{
response.EnsureSuccessStatusCode();
continue;
}
var problem = await response.Content.ReadFromJsonAsync<ProblemResponse>(TestContext.Current.CancellationToken);
Assert.Equal("payroll-exceeded", problem?.Code);
Assert.Equal(100_000f, problem?.Allocated);
Assert.True(problem?.Attempted > problem?.Allocated);
Assert.True(problem?.Payroll <= problem?.Allocated);
// Float arithmetic: the server computes remaining once, so compare within a cent.
Assert.Equal(problem!.Allocated!.Value - problem.Payroll!.Value, problem.Remaining!.Value, 0.01f);
// Refused means refused: the assignment must not have landed anyway.
var after = await GetStaffingAsync(client, school.Id);
Assert.Equal(problem.Payroll!.Value, after.Payroll, 0.01f);
Assert.DoesNotContain(
after.Staff.Single(person => person.Id == member.Id).Subjects,
assigned => assigned.DefName == subject);
return;
}
}
Assert.Fail("Loading every teacher with every subject never reached the payroll cap.");
}
private static async Task<StaffingResponse> GetStaffingAsync(HttpClient client, int schoolId)
{
var staffing = await client.GetFromJsonAsync<StaffingResponse>(