Enhance people management tests and fix related issues
- 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:
@@ -35,7 +35,10 @@ public class PeopleApiTests(AppHostFixture fixture)
|
||||
|
||||
var page = await GetPeopleAsync(client, school.Id, "sort=surname&pageSize=20");
|
||||
var surnames = page.People.Select(person => person.Surname).ToArray();
|
||||
Assert.Equal(surnames.OrderBy(name => name, StringComparer.Ordinal), surnames);
|
||||
|
||||
// The server alphabetises Ё with Е, so plain ordinal order is the wrong yardstick: it only
|
||||
// agrees because no vanilla surname starts with Ё. Fold the same way the server does.
|
||||
Assert.Equal(surnames.OrderBy(Alphabetised, StringComparer.Ordinal), surnames);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -139,14 +142,29 @@ public class PeopleApiTests(AppHostFixture fixture)
|
||||
var school = await SchoolApiTests.CreateAsync(client, "Люди семья", Start);
|
||||
|
||||
var page = await GetPeopleAsync(client, school.Id, "role=student&year=5&pageSize=10");
|
||||
var pupil = page.People[0];
|
||||
|
||||
var card = await client.GetFromJsonAsync<PersonCardResponse>(
|
||||
$"/api/schools/{school.Id}/people/{Uri.EscapeDataString(pupil.Id)}?lang=ru",
|
||||
TestContext.Current.CancellationToken);
|
||||
// Every fifth-to-tenth family is single-parent on purpose (design/people.md), and the
|
||||
// roster seed is the school id — which depends on how many schools ran before this test.
|
||||
// Demanding two parents from whoever happens to sort first therefore fails on some runs;
|
||||
// look for a pupil who has a mother instead of assuming the first one does.
|
||||
PersonCardResponse? card = null;
|
||||
foreach (var candidate in page.People)
|
||||
{
|
||||
var candidateCard = await client.GetFromJsonAsync<PersonCardResponse>(
|
||||
$"/api/schools/{school.Id}/people/{Uri.EscapeDataString(candidate.Id)}?lang=ru",
|
||||
TestContext.Current.CancellationToken);
|
||||
Assert.NotNull(candidateCard);
|
||||
Assert.Equal(candidate.Id, candidateCard.Id);
|
||||
Assert.InRange(candidateCard.Family.Parents.Count, 1, 2);
|
||||
|
||||
if (candidateCard.Family.Parents.Any(parent => parent.Female))
|
||||
{
|
||||
card = candidateCard;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Assert.NotNull(card);
|
||||
Assert.Equal(pupil.Id, card.Id);
|
||||
Assert.Equal(2, card.Family.Parents.Count);
|
||||
Assert.Contains(card.Needs, need => need.Id == "Sleep" && need.Value == 1f);
|
||||
Assert.NotEmpty(card.Body);
|
||||
Assert.NotEmpty(card.Skills);
|
||||
@@ -156,9 +174,94 @@ public class PeopleApiTests(AppHostFixture fixture)
|
||||
$"/api/schools/{school.Id}/people/{Uri.EscapeDataString(mother.Id)}?lang=ru",
|
||||
TestContext.Current.CancellationToken);
|
||||
Assert.NotNull(motherCard);
|
||||
Assert.Contains(motherCard.Family.Children, child => child.Id == pupil.Id);
|
||||
Assert.Contains(motherCard.Family.Children, child => child.Id == card.Id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Phases 7 and 9 together: the roster survives a restart, and what comes back is the
|
||||
/// composition *after* the first-September intake. Generating from the seed again would
|
||||
/// rebuild the pre-intake roster, so comparing against it is what makes this test bite.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task ReloadAfterTheYearlyIntake_RestoresTheNewComposition()
|
||||
{
|
||||
using var client = fixture.App.CreateHttpClient("server");
|
||||
await SchoolApiTests.ResetAsync(client);
|
||||
|
||||
// Fifteen game minutes short of 1 September: the intake fires at midnight, and five game
|
||||
// minutes per real second gets the clock there in about three seconds.
|
||||
var school = await SchoolApiTests.CreateAsync(
|
||||
client,
|
||||
"Набор и рестарт",
|
||||
new DateTime(2012, 8, 31, 23, 45, 0, DateTimeKind.Utc));
|
||||
|
||||
var before = await GetPeopleAsync(client, school.Id, Everyone);
|
||||
var beforeIds = before.People.Select(person => person.Id).ToArray();
|
||||
Assert.NotEmpty(beforeIds);
|
||||
|
||||
var after = await WaitForIntakeAsync(client, school.Id, beforeIds);
|
||||
var afterIds = after.People.Select(person => person.Id).ToArray();
|
||||
|
||||
var pupil = after.People.First(person => person.Roles.Contains("student"));
|
||||
var cardBefore = await GetCardAsync(client, school.Id, pupil.Id);
|
||||
|
||||
using var reload = await client.PostAsync("/api/dev/reload-schools", content: null, TestContext.Current.CancellationToken);
|
||||
reload.EnsureSuccessStatusCode();
|
||||
|
||||
var restored = await GetPeopleAsync(client, school.Id, Everyone);
|
||||
Assert.Equal(after.Total, restored.Total);
|
||||
Assert.Equal(
|
||||
after.People.Select(person => (person.Id, person.FullName, person.ClassYear)),
|
||||
restored.People.Select(person => (person.Id, person.FullName, person.ClassYear)));
|
||||
Assert.NotEqual(beforeIds, restored.People.Select(person => person.Id).ToArray());
|
||||
|
||||
var cardAfter = await GetCardAsync(client, school.Id, pupil.Id);
|
||||
Assert.Equal(cardBefore.BirthDate, cardAfter.BirthDate);
|
||||
Assert.Equal(
|
||||
cardBefore.Body.Select(stat => (stat.Id, stat.Value)),
|
||||
cardAfter.Body.Select(stat => (stat.Id, stat.Value)));
|
||||
Assert.Equal(
|
||||
cardBefore.Skills.Select(stat => (stat.Id, stat.Value)),
|
||||
cardAfter.Skills.Select(stat => (stat.Id, stat.Value)));
|
||||
Assert.Equal(
|
||||
cardBefore.Traits.Select(trait => trait.DefName),
|
||||
cardAfter.Traits.Select(trait => trait.DefName));
|
||||
}
|
||||
|
||||
private const string Everyone = "sort=surname&pageSize=100";
|
||||
|
||||
/// <summary>Polls the published roster until the intake has replaced the graduating year.</summary>
|
||||
private static async Task<PeopleListResponse> WaitForIntakeAsync(
|
||||
HttpClient client,
|
||||
int schoolId,
|
||||
IReadOnlyList<string> beforeIds)
|
||||
{
|
||||
for (var attempt = 0; attempt < 60; attempt++)
|
||||
{
|
||||
var page = await GetPeopleAsync(client, schoolId, Everyone);
|
||||
if (!page.People.Select(person => person.Id).SequenceEqual(beforeIds))
|
||||
{
|
||||
return page;
|
||||
}
|
||||
|
||||
await Task.Delay(250, TestContext.Current.CancellationToken);
|
||||
}
|
||||
|
||||
throw new InvalidOperationException("The yearly intake never happened.");
|
||||
}
|
||||
|
||||
private static async Task<PersonCardResponse> GetCardAsync(HttpClient client, int schoolId, string personId)
|
||||
{
|
||||
var card = await client.GetFromJsonAsync<PersonCardResponse>(
|
||||
$"/api/schools/{schoolId}/people/{Uri.EscapeDataString(personId)}?lang=ru",
|
||||
TestContext.Current.CancellationToken);
|
||||
Assert.NotNull(card);
|
||||
return card;
|
||||
}
|
||||
|
||||
/// <summary>Mirrors the server's name order: Ё sorts as Е, everything else stays ordinal.</summary>
|
||||
private static string Alphabetised(string name) => name.Replace('Ё', 'Е').Replace('ё', 'е');
|
||||
|
||||
private static async Task<PeopleListResponse> GetPeopleAsync(HttpClient client, int schoolId, string query)
|
||||
{
|
||||
var page = await client.GetFromJsonAsync<PeopleListResponse>(
|
||||
|
||||
Reference in New Issue
Block a user