Die KI kann im Text `tier:<8hex>` / `kontakt:` / `wurf:` / `gehege:` schreiben; Frontend löst per POST /refs/resolve (Präfix-Match, DB-agnostisch) zu Name + internem Link auf. Reine KI-Schreibhilfe — kein Nutzer-Button. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
124 lines
5.6 KiB
C#
124 lines
5.6 KiB
C#
using System.Net.Http.Json;
|
|
using System.Text.Json;
|
|
using GerbilManagerWebAPI.Models;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
|
|
namespace GerbilManager.Tests;
|
|
|
|
/// <summary>
|
|
/// REFS: POST /refs/resolve löst getippte Kurz-Verweise (tier|kontakt|wurf|gehege) auf.
|
|
/// - exakter (volle GUID) und kurzer (Präfix) Treffer je Typ
|
|
/// - mehrdeutiges Präfix (mehrere Treffer) => id=null (nicht geraten)
|
|
/// - unbekannter Code / unbekannter Typ => id=null
|
|
/// Entitäten werden mit deterministischen GUIDs direkt in die SQLite-Test-DB geseedet,
|
|
/// damit Präfixe und Mehrdeutigkeit kontrolliert getestet werden können.
|
|
/// </summary>
|
|
public class RefsEndpointTests : IClassFixture<ApiFactory>
|
|
{
|
|
private readonly ApiFactory _factory;
|
|
public RefsEndpointTests(ApiFactory factory) => _factory = factory;
|
|
|
|
// Deterministische GUIDs: zwei Tiere mit GEMEINSAMEM 6-Hex-Präfix "abcdef" (mehrdeutig),
|
|
// sowie je eine eindeutige Entität pro Typ.
|
|
private static readonly Guid TierA = new("abcdef00-0000-0000-0000-000000000001");
|
|
private static readonly Guid TierB = new("abcdef00-0000-0000-0000-000000000002");
|
|
private static readonly Guid Kontakt = new("11112222-3333-4444-5555-666677778888");
|
|
private static readonly Guid Wurf = new("99990000-aaaa-bbbb-cccc-ddddeeeeffff");
|
|
private static readonly Guid Gehege = new("12345678-9abc-def0-1234-567890abcdef");
|
|
|
|
private async Task SeedAsync()
|
|
{
|
|
using var scope = _factory.Services.CreateScope();
|
|
var db = scope.ServiceProvider.GetRequiredService<ApplicationContext>();
|
|
if (db.Gerbils.Any(g => g.Id == TierA)) return; // einmalig (geteilte Fixture)
|
|
|
|
db.Gerbils.Add(new Gerbil { Id = TierA, Name = "Krümel", Gender = Gender.female });
|
|
db.Gerbils.Add(new Gerbil { Id = TierB, Name = "Möhre", Gender = Gender.male });
|
|
db.Contacts.Add(new Contact { Id = Kontakt, Name = "Erika Musterfrau" });
|
|
db.Litters.Add(new Litter { Id = Wurf, Name = "Wurf X", Date = new DateOnly(2026, 1, 1) });
|
|
db.Enclosures.Add(new Enclosure { Id = Gehege, Name = "Terrarium 1" });
|
|
await db.SaveChangesAsync();
|
|
}
|
|
|
|
private async Task<List<JsonElement>> Resolve(params object[] refs)
|
|
{
|
|
var resp = await _factory.CreateClient()
|
|
.PostAsJsonAsync("/refs/resolve", new { refs });
|
|
resp.EnsureSuccessStatusCode();
|
|
var doc = JsonDocument.Parse(await resp.Content.ReadAsStringAsync());
|
|
return doc.RootElement.EnumerateArray().ToList();
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Resolves_full_guid_per_type()
|
|
{
|
|
await SeedAsync();
|
|
var rows = await Resolve(
|
|
new { type = "tier", code = TierA.ToString() },
|
|
new { type = "kontakt", code = Kontakt.ToString() },
|
|
new { type = "wurf", code = Wurf.ToString() },
|
|
new { type = "gehege", code = Gehege.ToString() });
|
|
|
|
Assert.Equal(TierA.ToString(), rows[0].GetProperty("id").GetString());
|
|
Assert.Equal("Krümel", rows[0].GetProperty("name").GetString());
|
|
Assert.Equal(Kontakt.ToString(), rows[1].GetProperty("id").GetString());
|
|
Assert.Equal("Erika Musterfrau", rows[1].GetProperty("name").GetString());
|
|
Assert.Equal(Wurf.ToString(), rows[2].GetProperty("id").GetString());
|
|
Assert.Equal("Wurf X", rows[2].GetProperty("name").GetString());
|
|
Assert.Equal(Gehege.ToString(), rows[3].GetProperty("id").GetString());
|
|
Assert.Equal("Terrarium 1", rows[3].GetProperty("name").GetString());
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Resolves_short_prefix_when_unique()
|
|
{
|
|
await SeedAsync();
|
|
// 8-Hex-Kurz-ID des Kontakts ("11112222") ist eindeutig.
|
|
var rows = await Resolve(new { type = "kontakt", code = "11112222" });
|
|
Assert.Equal(Kontakt.ToString(), rows[0].GetProperty("id").GetString());
|
|
Assert.Equal("Erika Musterfrau", rows[0].GetProperty("name").GetString());
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Tolerates_dashes_and_case_in_code()
|
|
{
|
|
await SeedAsync();
|
|
// Gemischte Schreibweise + Bindestriche im Kurz-Code des Geheges.
|
|
var rows = await Resolve(new { type = "gehege", code = "1234-5678" });
|
|
Assert.Equal(Gehege.ToString(), rows[0].GetProperty("id").GetString());
|
|
Assert.Equal("Terrarium 1", rows[0].GetProperty("name").GetString());
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Ambiguous_prefix_resolves_to_null()
|
|
{
|
|
await SeedAsync();
|
|
// "abcdef" trifft TierA UND TierB => mehrdeutig => id/name null.
|
|
var rows = await Resolve(new { type = "tier", code = "abcdef" });
|
|
Assert.Equal(JsonValueKind.Null, rows[0].GetProperty("id").ValueKind);
|
|
Assert.Equal(JsonValueKind.Null, rows[0].GetProperty("name").ValueKind);
|
|
Assert.Equal("tier", rows[0].GetProperty("type").GetString());
|
|
Assert.Equal("abcdef", rows[0].GetProperty("code").GetString());
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Unknown_code_and_unknown_type_resolve_to_null()
|
|
{
|
|
await SeedAsync();
|
|
var rows = await Resolve(
|
|
new { type = "tier", code = "fffffffe" }, // unbekannter Code
|
|
new { type = "schubladen", code = "11112222" }); // unbekannter Typ
|
|
Assert.Equal(JsonValueKind.Null, rows[0].GetProperty("id").ValueKind);
|
|
Assert.Equal(JsonValueKind.Null, rows[1].GetProperty("id").ValueKind);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Too_short_code_resolves_to_null()
|
|
{
|
|
await SeedAsync();
|
|
// < 6 Hex-Zeichen wird nicht aufgelöst (auch wenn es eindeutig wäre).
|
|
var rows = await Resolve(new { type = "kontakt", code = "1111" });
|
|
Assert.Equal(JsonValueKind.Null, rows[0].GetProperty("id").ValueKind);
|
|
}
|
|
}
|