using Microsoft.Extensions.Logging.Abstractions;
namespace Flaro.Metadata.Tests;
/// Womit sich Flaro bei TMDB ausweist.
public class ZugangTests
{
private const string LeseToken = "eyJhbGciOiJIUzI1NiJ9.beispiel.unterschrift";
private const string AelterSchluessel = "0123456789abcdef0123456789abcdef";
private static (TmdbClient Client, Attrappe Attrappe) Mit(string zugang)
{
var attrappe = Attrappe.MitJson("""{"results":[{"id":1,"title":"Egal"}]}""");
return (new TmdbClient(
new HttpClient(attrappe) { BaseAddress = new Uri(TmdbClient.BaseAddress) },
new FesteEinstellungen(new TmdbSettings(zugang, "de-DE")),
NullLogger.Instance), attrappe);
}
/// Ein Lese-Token gehört in die Kopfzeile, nicht in die Adresse.
///
///
/// Eine Adresse steht in jedem Zugriffsprotokoll. In jedem Reverse
/// Proxy, in jeder Fehlermeldung, die eine URL zitiert. Eine Kopfzeile tut
/// das nicht — und bei einem Schlüssel, der in einem offenen Projekt
/// mitgeliefert wird, zählt jede Stelle, an der er nicht auftaucht.
///
[Test]
public async Task Der_Lese_Token_geht_als_Bearer_und_nicht_in_die_Adresse()
{
var (client, attrappe) = Mit(LeseToken);
await client.FindMovieAsync("Egal", null);
// **Jede** Anfrage, nicht nur die erste: seit der IMDb-Nachschlag
// dazukam, gehen zwei hinaus, und beide müssen es richtig machen.
Assert.That(attrappe.Kopfzeilen, Is.All.EqualTo($"Bearer {LeseToken}"));
Assert.That(attrappe.Aufrufe, Is.All.Not.Contains("api_key"));
Assert.That(attrappe.Aufrufe, Is.All.Not.Contains(LeseToken));
}
/// Der ältere Schlüssel kann nur in die Adresse — also tut er das.
[Test]
public async Task Der_aeltere_Schluessel_geht_weiterhin_als_Abfrageteil()
{
var (client, attrappe) = Mit(AelterSchluessel);
await client.FindMovieAsync("Egal", null);
Assert.That(attrappe.Kopfzeilen, Is.All.Null);
Assert.That(attrappe.Aufrufe, Is.All.Contains($"api_key={AelterSchluessel}"));
}
[Test]
public async Task Auch_bei_Folgen_wird_der_Bearer_mitgeschickt()
{
var (client, attrappe) = Mit(LeseToken);
await client.FindEpisodeAsync(70523, 1, 1);
Assert.That(attrappe.Kopfzeilen, Is.All.StartsWith("Bearer "));
Assert.That(attrappe.Aufrufe, Is.All.Not.Contains("api_key"));
}
}
/// Welcher Schlüssel gilt, wenn mehrere in Frage kommen.
public class SchluesselwahlTests
{
[Test]
public void Ein_eigener_Schluessel_sticht_den_mitgelieferten()
{
var gewählt = TmdbSettings.Resolve("mein-eigener", "de-DE");
Assert.That(gewählt.ApiKey, Is.EqualTo("mein-eigener"));
Assert.That(gewählt.UsingProjectKey, Is.False);
}
[Test]
public void Ohne_eigenen_greift_der_mitgelieferte()
{
var gewählt = TmdbSettings.Resolve(null, null);
if (TmdbSettings.ProjectApiKey.Length == 0)
{
Assert.That(gewählt.ApiKey, Is.Null, "Ohne mitgelieferten Schlüssel bleibt es dabei");
Assert.That(gewählt.UsingProjectKey, Is.False);
return;
}
Assert.That(gewählt.ApiKey, Is.EqualTo(TmdbSettings.ProjectApiKey));
Assert.That(gewählt.UsingProjectKey, Is.True);
}
[TestCase("")]
[TestCase(" ")]
public void Ein_leerer_eigener_Schluessel_zaehlt_als_keiner(string leer)
{
Assert.That(TmdbSettings.Resolve(leer, null).UsingProjectKey,
Is.EqualTo(TmdbSettings.ProjectApiKey.Length > 0));
}
[Test]
public void Leerraum_um_den_Schluessel_wird_abgeschnitten()
{
Assert.That(TmdbSettings.Resolve(" meiner ", " de-DE ").ApiKey, Is.EqualTo("meiner"));
Assert.That(TmdbSettings.Resolve("meiner", " de-DE ").Language, Is.EqualTo("de-DE"));
}
[Test]
public void Ohne_Sprachangabe_gilt_Englisch()
{
Assert.That(TmdbSettings.Resolve("meiner", null).Language, Is.EqualTo("en-US"));
}
/// Der mitgelieferte Schlüssel darf nur lesen dürfen.
///
///
/// Er steht öffentlich im Quellcode. Ein Lese-Token kann nichts ändern —
/// ein älterer API-Schlüssel schon. Diese Prüfung hält fest, dass hier
/// keiner der falschen Art hineingerät.
///
[Test]
public void Der_mitgelieferte_Schluessel_ist_ein_Lese_Token()
{
if (TmdbSettings.ProjectApiKey.Length == 0) Assert.Pass("Kein Schlüssel mitgeliefert.");
Assert.That(TmdbSettings.ProjectApiKey, Does.StartWith("eyJ"),
"Erwartet wird ein API Read Access Token, kein älterer API-Schlüssel");
}
}