using System.Net.Http.Json; using System.Text.Json; using System.Text.Json.Nodes; namespace HSchool.AppHost.Tests; [Collection(AppHostCollection.Name)] public class InventoryApiTests(AppHostFixture fixture) { private static readonly DateTime Start = new(2012, 4, 3, 6, 0, 0, DateTimeKind.Utc); [Fact] public async Task Card_IncludesWornAndCarried_ListDoesNot() { using var client = fixture.App.CreateHttpClient("server"); await SchoolApiTests.ResetAsync(client); var school = await SchoolApiTests.CreateAsync(client, "Гардероб карточка", Start, seed: 31); var page = await client.GetFromJsonAsync( $"/api/schools/{school.Id}/people?role=student&year=5&pageSize=20", TestContext.Current.CancellationToken); Assert.NotNull(page); var girl = page.People.First(person => person.Female); using var listResponse = await client.GetAsync( $"/api/schools/{school.Id}/people?pageSize=1", TestContext.Current.CancellationToken); var listJson = await listResponse.Content.ReadAsStringAsync(TestContext.Current.CancellationToken); Assert.DoesNotContain("carryMass", listJson, StringComparison.Ordinal); Assert.DoesNotContain("\"worn\"", listJson, StringComparison.Ordinal); Assert.DoesNotContain("\"carried\"", listJson, StringComparison.Ordinal); var card = await client.GetFromJsonAsync( $"/api/schools/{school.Id}/people/{Uri.EscapeDataString(girl.Id)}?lang=ru", TestContext.Current.CancellationToken); Assert.NotNull(card); Assert.NotEmpty(card.Worn); Assert.Contains(card.Worn, item => item.Layers.Count > 0 && item.ColorLabel is { Length: > 0 }); Assert.True(card.CarryCapacity > 0); Assert.True(card.CarryMass <= card.CarryCapacity); Assert.Contains(card.Carried, item => item.Mass > 0); } [Fact] public async Task SameSeed_YieldsTheSameShirt() { using var client = fixture.App.CreateHttpClient("server"); await SchoolApiTests.ResetAsync(client); var first = await SchoolApiTests.CreateAsync(client, "Синяя рубашка 1", Start, seed: 31); var shirt = await FirstGirlTopAsync(client, first.Id); await SchoolApiTests.ResetAsync(client); var second = await SchoolApiTests.CreateAsync(client, "Синяя рубашка 2", Start, seed: 31); var again = await FirstGirlTopAsync(client, second.Id); Assert.Equal(shirt.DefName, again.DefName); Assert.Equal(shirt.Color, again.Color); } [Fact] public async Task PeopleJsonWithoutInventory_IsDressedOnReloadAndRewritten() { using var client = fixture.App.CreateHttpClient("server"); await SchoolApiTests.ResetAsync(client); var school = await SchoolApiTests.CreateAsync(client, "Старый сейв без одежды", Start, seed: 31); var before = await FirstGirlTopAsync(client, school.Id); var directory = await SavesDirectoryAsync(client); var path = Path.Combine(directory, $"{school.Id}.people.json"); StripItems(path); Assert.DoesNotContain("\"items\"", File.ReadAllText(path), StringComparison.Ordinal); using var reload = await client.PostAsync("/api/dev/reload-schools", content: null, TestContext.Current.CancellationToken); reload.EnsureSuccessStatusCode(); var after = await FirstGirlTopAsync(client, school.Id); Assert.Equal(before.DefName, after.DefName); Assert.Equal(before.Color, after.Color); Assert.Contains("\"items\"", File.ReadAllText(path), StringComparison.Ordinal); } private static async Task FirstGirlTopAsync(HttpClient client, int schoolId) { var page = await client.GetFromJsonAsync( $"/api/schools/{schoolId}/people?role=student&sex=female&pageSize=20", TestContext.Current.CancellationToken); Assert.NotNull(page); Assert.NotEmpty(page.People); var girl = page.People[0]; var card = await client.GetFromJsonAsync( $"/api/schools/{schoolId}/people/{Uri.EscapeDataString(girl.Id)}?lang=ru", TestContext.Current.CancellationToken); Assert.NotNull(card); var top = card.Worn.FirstOrDefault(item => item.Layers.Any(layer => layer.DefName.Equals("Top", StringComparison.Ordinal))); Assert.NotNull(top); return top; } private static async Task SavesDirectoryAsync(HttpClient client) { var payload = await client.GetFromJsonAsync( "/api/dev/saves-directory", TestContext.Current.CancellationToken); Assert.NotNull(payload); return payload.Path; } private static void StripItems(string path) { var root = JsonNode.Parse(File.ReadAllText(path)) ?? throw new InvalidOperationException("People file parsed to nothing."); foreach (var person in root["people"]?.AsArray() ?? []) { person?.AsObject().Remove("items"); } var applicants = root["applicants"]?["applicants"]?.AsArray(); if (applicants is not null) { foreach (var applicant in applicants) { applicant?["person"]?.AsObject()?.Remove("items"); } } File.WriteAllText( path, root.ToJsonString(new JsonSerializerOptions { WriteIndented = true })); } private sealed record SavesDirectoryResponse(string Path); private sealed record PeoplePage(IReadOnlyList People); private sealed record PersonRow(string Id, string FullName, bool Female); private sealed record Card( IReadOnlyList Worn, IReadOnlyList Carried, float CarryMass, float CarryCapacity); private sealed record WornItem( string DefName, string Label, string? Color, string? ColorLabel, IReadOnlyList Layers); private sealed record Layer(string DefName, string Label); private sealed record CarriedItem(string DefName, string Label, float Mass); }