using System.Net; using System.Net.Http.Json; using System.Text.Json; using System.Text.RegularExpressions; using GerbilManagerWebAPI.Models; using Microsoft.Extensions.DependencyInjection; namespace GerbilManager.Tests; /// profilePhotoUrl: computed field on GerbilDto — first photo by sortOrder, or null. public class GerbilProfilePhotoTests : IClassFixture { private readonly HttpClient _client; private readonly ApiFactory _factory; public GerbilProfilePhotoTests(ApiFactory factory) { _factory = factory; _client = factory.CreateClient(); } private static Guid ExtractId(string json) => Guid.Parse(Regex.Match(json, "\"id\":\"([^\"]+)\"").Groups[1].Value); private static string? GetStr(JsonElement el, string prop) => el.TryGetProperty(prop, out var v) && v.ValueKind == JsonValueKind.String ? v.GetString() : null; private async Task CreateGerbil(string name) { var resp = await _client.PostAsync("/gerbils", JsonContent.Create(new { name, gender = "female" })); Assert.Equal(HttpStatusCode.Created, resp.StatusCode); return ExtractId(await resp.Content.ReadAsStringAsync()); } private void InsertPhotos(Guid gerbilId, params (string fileName, int sortOrder)[] photos) { using var scope = _factory.Services.CreateScope(); var db = scope.ServiceProvider.GetRequiredService(); foreach (var (fn, so) in photos) db.GerbilPhotos.Add(new GerbilPhoto { Id = Guid.NewGuid(), GerbilId = gerbilId, FileName = fn, SortOrder = so, CreatedAt = DateTimeOffset.UtcNow, }); db.SaveChanges(); } [Fact] public async Task GetById_without_photos_returns_null_profilePhotoUrl() { var id = await CreateGerbil("NoPhoto"); var json = await _client.GetStringAsync($"/gerbils/{id}"); var el = JsonDocument.Parse(json).RootElement; Assert.True(el.TryGetProperty("profilePhotoUrl", out var v)); Assert.Equal(JsonValueKind.Null, v.ValueKind); } [Fact] public async Task GetById_with_photos_returns_lowest_sortOrder_url() { var id = await CreateGerbil("PhotoGerbil"); InsertPhotos(id, ("second.jpg", 1), ("first.jpg", 0)); var json = await _client.GetStringAsync($"/gerbils/{id}"); var el = JsonDocument.Parse(json).RootElement; Assert.Equal("/photos/files/first.jpg", GetStr(el, "profilePhotoUrl")); } [Fact] public async Task GetList_returns_profilePhotoUrl_without_n_plus_1() { var id = await CreateGerbil("ListPhoto"); InsertPhotos(id, ("list-photo.jpg", 0)); var json = await _client.GetStringAsync("/gerbils"); var root = JsonDocument.Parse(json).RootElement; var items = root.GetProperty("items").EnumerateArray().ToList(); var gerbil = items.FirstOrDefault(x => GetStr(x, "id") == id.ToString()); Assert.NotEqual(default, gerbil); Assert.Equal("/photos/files/list-photo.jpg", GetStr(gerbil, "profilePhotoUrl")); } }