using System.Net;
using System.Net.Http.Json;
using System.Text.Json;
namespace GerbilManager.Tests;
///
/// QOL-ELTERN: PUT /gerbils/{id}/parents — Eltern direkt an der Tier-Akte pflegen.
/// Das Datenmodell bleibt unverändert (Eltern hängen am Geburtswurf), der Endpoint
/// schreibt nur dorthin bzw. legt den Träger-Wurf an, wenn es noch keinen gibt.
///
public class GerbilParentsTests : IClassFixture
{
private readonly HttpClient _client;
public GerbilParentsTests(ApiFactory factory) => _client = factory.CreateClient();
private async Task CreateGerbil(string name, string gender, string? dob = null, string? litterId = null)
{
var resp = await _client.PostAsJsonAsync("/gerbils", new
{
name,
gender,
dateOfBirth = dob,
litterId,
});
resp.EnsureSuccessStatusCode();
return JsonDocument.Parse(await resp.Content.ReadAsStringAsync()).RootElement
.GetProperty("id").GetString()!;
}
private async Task CreateLitter(string name, string date, string? fatherId = null, string? motherId = null)
{
var resp = await _client.PostAsJsonAsync("/litters", new { name, date, fatherId, motherId });
resp.EnsureSuccessStatusCode();
return JsonDocument.Parse(await resp.Content.ReadAsStringAsync()).RootElement
.GetProperty("id").GetString()!;
}
private async Task SetParents(string gerbilId, string? fatherId, string? motherId)
{
var resp = await _client.PutAsJsonAsync($"/gerbils/{gerbilId}/parents", new { fatherId, motherId });
resp.EnsureSuccessStatusCode();
return JsonDocument.Parse(await resp.Content.ReadAsStringAsync()).RootElement;
}
private async Task GetLitter(string litterId) =>
JsonDocument.Parse(await _client.GetStringAsync($"/litters/{litterId}")).RootElement;
private static string? Str(JsonElement el, string prop) =>
el.TryGetProperty(prop, out var v) && v.ValueKind == JsonValueKind.String ? v.GetString() : null;
[Fact]
public async Task Parents_of_existing_litter_are_written_to_that_litter()
{
var father = await CreateGerbil("Vater A", "male", "2024-01-01");
var mother = await CreateGerbil("Mutter A", "female", "2024-01-01");
var litterId = await CreateLitter("A-Wurf", "2025-03-01");
var pup = await CreateGerbil("Jungtier A", "female", "2025-03-01", litterId);
var result = await SetParents(pup, father, mother);
Assert.Equal(litterId, Str(result, "litterId"));
Assert.False(result.GetProperty("litterCreated").GetBoolean());
Assert.False(result.GetProperty("litterAttached").GetBoolean());
var litter = await GetLitter(litterId);
Assert.Equal(father, Str(litter, "fatherId"));
Assert.Equal(mother, Str(litter, "motherId"));
}
[Fact]
public async Task SiblingCount_reports_the_other_pups_the_change_applies_to()
{
var litterId = await CreateLitter("B-Wurf", "2025-04-01");
var pup = await CreateGerbil("Jungtier B1", "female", "2025-04-01", litterId);
await CreateGerbil("Jungtier B2", "male", "2025-04-01", litterId);
await CreateGerbil("Jungtier B3", "male", "2025-04-01", litterId);
var mother = await CreateGerbil("Mutter B", "female", "2024-02-01");
var result = await SetParents(pup, null, mother);
Assert.Equal(2, result.GetProperty("siblingCount").GetInt32());
}
[Fact]
public async Task Gerbil_without_litter_gets_a_hidden_carrier_litter()
{
var father = await CreateGerbil("Vater C", "male", "2024-03-01");
var mother = await CreateGerbil("Mutter C", "female", "2024-03-01");
var orphan = await CreateGerbil("Waise C", "female", "2025-05-01");
var result = await SetParents(orphan, father, mother);
Assert.True(result.GetProperty("litterCreated").GetBoolean());
var litterId = Str(result, "litterId")!;
Assert.Equal("Wurf von Vater C + Mutter C", Str(result, "litterName"));
var litter = await GetLitter(litterId);
Assert.Equal(father, Str(litter, "fatherId"));
Assert.Equal(mother, Str(litter, "motherId"));
// Nur zur Abstammung — der Hilfs-Wurf darf die Wurfchronik nicht zumüllen.
Assert.False(litter.GetProperty("showInChronicle").GetBoolean());
// Wurfdatum = Geburtsdatum des Tiers.
Assert.Equal("2025-05-01", Str(litter, "date"));
// Das Tier hängt jetzt an diesem Wurf.
var gerbil = JsonDocument.Parse(await _client.GetStringAsync($"/gerbils/{orphan}")).RootElement;
Assert.Equal(litterId, Str(gerbil, "litterId"));
}
[Fact]
public async Task Same_parents_and_same_dob_attach_to_the_existing_litter_instead_of_duplicating()
{
var father = await CreateGerbil("Vater D", "male", "2024-04-01");
var mother = await CreateGerbil("Mutter D", "female", "2024-04-01");
var first = await CreateGerbil("Waise D1", "female", "2025-06-01");
var second = await CreateGerbil("Waise D2", "male", "2025-06-01");
var created = await SetParents(first, father, mother);
Assert.True(created.GetProperty("litterCreated").GetBoolean());
var attached = await SetParents(second, father, mother);
Assert.False(attached.GetProperty("litterCreated").GetBoolean());
Assert.True(attached.GetProperty("litterAttached").GetBoolean());
// Beide sind jetzt Geschwister im selben Wurf.
Assert.Equal(Str(created, "litterId"), Str(attached, "litterId"));
Assert.Equal(1, attached.GetProperty("siblingCount").GetInt32());
}
[Fact]
public async Task Clearing_parents_nulls_them_on_the_litter()
{
var father = await CreateGerbil("Vater E", "male", "2024-05-01");
var litterId = await CreateLitter("E-Wurf", "2025-07-01", father);
var pup = await CreateGerbil("Jungtier E", "female", "2025-07-01", litterId);
await SetParents(pup, null, null);
var litter = await GetLitter(litterId);
Assert.Equal(JsonValueKind.Null, litter.GetProperty("fatherId").ValueKind);
Assert.Equal(JsonValueKind.Null, litter.GetProperty("motherId").ValueKind);
}
[Fact]
public async Task No_litter_and_no_parents_is_a_no_op()
{
var orphan = await CreateGerbil("Waise F", "female", "2025-08-01");
var result = await SetParents(orphan, null, null);
Assert.Equal(JsonValueKind.Null, result.GetProperty("litterId").ValueKind);
Assert.False(result.GetProperty("litterCreated").GetBoolean());
var gerbil = JsonDocument.Parse(await _client.GetStringAsync($"/gerbils/{orphan}")).RootElement;
Assert.Equal(JsonValueKind.Null, gerbil.GetProperty("litterId").ValueKind);
}
[Fact]
public async Task Wrong_gender_parent_returns_400_InvalidParentGender()
{
var female = await CreateGerbil("Weibchen G", "female", "2024-06-01");
var pup = await CreateGerbil("Jungtier G", "male", "2025-09-01");
var resp = await _client.PutAsJsonAsync($"/gerbils/{pup}/parents",
new { fatherId = female, motherId = (string?)null });
Assert.Equal(HttpStatusCode.BadRequest, resp.StatusCode);
Assert.Contains("InvalidParentGender", await resp.Content.ReadAsStringAsync());
}
[Fact]
public async Task Self_as_parent_returns_400()
{
var g = await CreateGerbil("Selbstbezug H", "male", "2025-10-01");
var resp = await _client.PutAsJsonAsync($"/gerbils/{g}/parents",
new { fatherId = g, motherId = (string?)null });
Assert.Equal(HttpStatusCode.BadRequest, resp.StatusCode);
}
[Fact]
public async Task Unknown_gerbil_returns_404()
{
var resp = await _client.PutAsJsonAsync($"/gerbils/{Guid.NewGuid()}/parents",
new { fatherId = (string?)null, motherId = (string?)null });
Assert.Equal(HttpStatusCode.NotFound, resp.StatusCode);
}
}