using System.Net.Http.Json; namespace HSchool.AppHost.Tests; [Collection(AppHostCollection.Name)] public class PeopleApiTests(AppHostFixture fixture) { private static readonly DateTime Start = new(2012, 4, 3, 6, 0, 0, DateTimeKind.Utc); [Fact] public async Task List_FiltersByRoleAndYear() { using var client = fixture.App.CreateHttpClient("server"); await SchoolApiTests.ResetAsync(client); var school = await SchoolApiTests.CreateAsync(client, "Люди фильтр", Start); var page = await GetPeopleAsync(client, school.Id, "role=student&year=5&pageSize=100"); Assert.NotEmpty(page.People); Assert.Equal(page.People.Count, page.Total); Assert.All(page.People, person => { Assert.Contains("student", person.Roles); Assert.Equal(5, person.ClassYear); }); Assert.Contains(5, page.Filters.Years); } [Fact] public async Task List_SortsBySurname() { using var client = fixture.App.CreateHttpClient("server"); await SchoolApiTests.ResetAsync(client); var school = await SchoolApiTests.CreateAsync(client, "Люди сорт", Start); 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); } [Fact] public async Task List_PageBounds() { using var client = fixture.App.CreateHttpClient("server"); await SchoolApiTests.ResetAsync(client); var school = await SchoolApiTests.CreateAsync(client, "Люди страницы", Start); var first = await GetPeopleAsync(client, school.Id, "page=1&pageSize=10"); Assert.True(first.Total > 10); Assert.Equal(10, first.People.Count); Assert.Equal(1, first.Page); Assert.Equal(10, first.PageSize); var past = await GetPeopleAsync(client, school.Id, "page=999&pageSize=10"); Assert.Equal(first.Total, past.Total); Assert.Empty(past.People); using var zero = await client.GetAsync($"/api/schools/{school.Id}/people?page=0", TestContext.Current.CancellationToken); Assert.Equal(HttpStatusCode.BadRequest, zero.StatusCode); Assert.Equal("invalid-query", await ProblemCodeAsync(zero)); using var huge = await client.GetAsync($"/api/schools/{school.Id}/people?pageSize=101", TestContext.Current.CancellationToken); Assert.Equal(HttpStatusCode.BadRequest, huge.StatusCode); Assert.Equal("invalid-query", await ProblemCodeAsync(huge)); } [Fact] public async Task List_UnknownSchool_IsNotFound() { using var client = fixture.App.CreateHttpClient("server"); using var response = await client.GetAsync("/api/schools/999999/people", TestContext.Current.CancellationToken); Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); Assert.Equal("unknown-school", await ProblemCodeAsync(response)); } [Fact] public async Task Card_UnknownPerson_IsNotFound() { using var client = fixture.App.CreateHttpClient("server"); await SchoolApiTests.ResetAsync(client); var school = await SchoolApiTests.CreateAsync(client, "Люди карточка", Start); using var response = await client.GetAsync( $"/api/schools/{school.Id}/people/nobody", TestContext.Current.CancellationToken); Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); Assert.Equal("unknown-person", await ProblemCodeAsync(response)); } [Fact] public async Task Card_IncludesFamilyAndLiveNeeds() { using var client = fixture.App.CreateHttpClient("server"); await SchoolApiTests.ResetAsync(client); 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( $"/api/schools/{school.Id}/people/{Uri.EscapeDataString(pupil.Id)}?lang=ru", TestContext.Current.CancellationToken); 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); var mother = card.Family.Parents.Single(parent => parent.Female); var motherCard = await client.GetFromJsonAsync( $"/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); } private static async Task GetPeopleAsync(HttpClient client, int schoolId, string query) { var page = await client.GetFromJsonAsync( $"/api/schools/{schoolId}/people?{query}", TestContext.Current.CancellationToken); Assert.NotNull(page); return page; } private static async Task ProblemCodeAsync(HttpResponseMessage response) { var problem = await response.Content.ReadFromJsonAsync(TestContext.Current.CancellationToken); return problem?.Code; } private sealed record ProblemResponse(string? Code); private sealed record PeopleListResponse( int Total, int Page, int PageSize, IReadOnlyList People, PeopleFilterOptionsResponse Filters); private sealed record PeopleFilterOptionsResponse( IReadOnlyList Years, IReadOnlyList Letters, IReadOnlyList Positions); private sealed record DefLabelResponse(string DefName, string Label); private sealed record PersonListItemResponse( string Id, string FullName, string Surname, string Given, string Patronymic, bool Female, int Age, IReadOnlyList Roles, int? ClassYear, string? ClassLetter, string? Position, string? PositionLabel); private sealed record PersonCardResponse( string Id, string FullName, bool Female, int Age, DateTime BirthDate, IReadOnlyList Roles, int? ClassYear, string? ClassLetter, IReadOnlyList Body, IReadOnlyList Skills, IReadOnlyList Traits, IReadOnlyList Needs, PersonFamilyResponse Family); private sealed record LabeledStatResponse(string Id, string Label, string Value); private sealed record NeedStatResponse(string Id, string Label, float Value); private sealed record PersonFamilyResponse( IReadOnlyList Parents, IReadOnlyList Children, IReadOnlyList Siblings, IReadOnlyList Partners); private sealed record PersonRelResponse(string Id, string FullName, bool Female); }