using System.Net; using System.Text; namespace Flaro.Metadata.Tests; /// Ein HTTP-Gegenüber, das antwortet, was der Test vorgibt. /// /// /// Damit die Prüfungen ohne Netz und ohne Schlüssel laufen. Ein /// Projekt, dessen Tests einen fremden Dienst brauchen, hat am Ende keine /// Tests: sie scheitern, wenn das WLAN hakt, und irgendwann schaltet sie /// jemand ab. /// public sealed class Attrappe(Func antwort) : HttpMessageHandler { public List Aufrufe { get; } = []; /// Die Authorization-Kopfzeile je Aufruf — oder null. public List Kopfzeilen { get; } = []; protected override Task SendAsync(HttpRequestMessage anfrage, CancellationToken ct) { // AbsoluteUri und nicht ToString(): Letzteres entschlüsselt zur // Anzeige wieder, und dann sähe eine richtig kodierte Anfrage falsch // aus — der Test prüfte eine Darstellung statt dessen, was hinausgeht. Aufrufe.Add(anfrage.RequestUri!.AbsoluteUri); Kopfzeilen.Add(anfrage.Headers.Authorization?.ToString()); return Task.FromResult(antwort(anfrage)); } public static Attrappe MitJson(string json, HttpStatusCode code = HttpStatusCode.OK) => new(_ => new HttpResponseMessage(code) { Content = new StringContent(json, Encoding.UTF8, "application/json"), }); /// Antwortet je nach Pfad verschieden. /// /// /// Nötig, seit ein Treffer zwei Anfragen kostet. Die Suche liefert /// nur eine Kennung; Laufzeit, Genres, Besetzung und Schriftzug stehen /// erst im Einzelabruf. Eine Attrappe mit einer Antwort für alles gäbe /// dem Einzelabruf die Trefferliste zurück — und der läse daraus eine /// Kennung von null. /// /// Der erste Eintrag, dessen Schlüssel im Pfad vorkommt, gewinnt. /// public static Attrappe NachPfad(params (string Teil, string Json)[] antworten) => new(anfrage => { var pfad = anfrage.RequestUri!.AbsoluteUri; var treffer = antworten.FirstOrDefault(a => pfad.Contains(a.Teil, StringComparison.Ordinal)); return new HttpResponseMessage(treffer.Json is null ? HttpStatusCode.NotFound : HttpStatusCode.OK) { Content = new StringContent(treffer.Json ?? "{}", Encoding.UTF8, "application/json"), }; }); public static Attrappe MitStatus(HttpStatusCode code) => new(_ => new HttpResponseMessage(code)); public static Attrappe DieWirft(Exception fehler) => new(_ => throw fehler); } public sealed record FesteEinstellungen(TmdbSettings Werte) : ITmdbSettingsSource { public Task GetAsync(CancellationToken ct = default) => Task.FromResult(Werte); public static FesteEinstellungen MitSchluessel(string sprache = "de-DE") => new(new TmdbSettings("ein-schluessel", sprache)); public static readonly FesteEinstellungen Ohne = new(TmdbSettings.Unconfigured); }