using System.Net;
using System.Net.Http.Json;
using Flaro.Api.Endpoints;
namespace Flaro.Api.Tests;
/// Was auf Prüfung wartet, sieht niemand ausser Administratoren.
///
///
///
/// Die Route-Aufzählung unten ist der eigentliche Test. Auf Filme und
/// Folgen wird an rund zwanzig Stellen zugegriffen — Mediathek, Suche,
/// Vorschläge, Weiterschauen, Ähnliches. Eine davon ohne Filter, und
/// unveröffentlichter Inhalt erscheint trotzdem; auffallen würde es nicht
/// beim Programmieren, sondern wenn jemand im Haushalt einen Titel sieht,
/// den er nicht sehen sollte.
///
///
/// Deshalb prüft dieser Test nicht eine Abfrage, sondern jede Route, die
/// Inhalt ausliefert — und zwar auf die Abwesenheit eines Titels. Eine
/// neue Route, die den Filter umgeht, fällt hier durch.
///
///
public class FreigabeTests
{
private string _bestand = "";
[SetUp]
public void Anlegen() => _bestand = Directory.CreateDirectory(
Path.Combine(Path.GetTempPath(), "flaro-freigabe-" + Guid.CreateVersion7().ToString("N"))).FullName;
[TearDown]
public void Aufraeumen()
{
try { Directory.Delete(_bestand, recursive: true); } catch (IOException) { }
}
private void Datei(string relativ)
{
var voll = Path.Combine(_bestand, relativ);
Directory.CreateDirectory(Path.GetDirectoryName(voll)!);
File.WriteAllBytes(voll, new byte[2048]);
}
/// Jede Route, die Titel ausliefert.
private static readonly string[] Inhaltsrouten =
[
"/api/v1/movies",
"/api/v1/movies?search=Geheim",
"/api/v1/movies?sort=added",
"/api/v1/series",
"/api/v1/home/continue",
"/api/v1/home/next",
"/api/v1/home/suggestions",
];
[Test]
public async Task Ungepruefte_Titel_sieht_nur_der_Administrator()
{
Datei("Geheimsache (2024).mkv");
using var server = new FlaroServer();
var admin = await server.SetUpAndSignIn();
var alsAdmin = server.AlsAngemeldeter(admin.Token);
var angelegt = await alsAdmin.PostAsJsonAsync("/api/v1/libraries",
new CreateLibraryRequest("Prüfbestand", "Movies", _bestand, ReviewNewItems: true));
angelegt.EnsureSuccessStatusCode();
var bibliothek = (await angelegt.Content.ReadFromJsonAsync())!;
await Scannen(alsAdmin, bibliothek.Id);
// Der Administrator sieht ihn — sonst könnte er ihn nicht prüfen.
var wartend = await alsAdmin.GetFromJsonAsync>("/api/v1/review");
Assert.That(wartend, Has.Count.EqualTo(1));
Assert.That(wartend![0].Title, Is.EqualTo("Geheimsache"));
// Ein gewöhnlicher Benutzer nirgends.
var gast = await server.NeuerBenutzer(alsAdmin, "gast", "gastpasswort");
foreach (var route in Inhaltsrouten)
{
var antwort = await gast.GetAsync(route);
Assert.That(antwort.StatusCode, Is.EqualTo(HttpStatusCode.OK), route);
var text = await antwort.Content.ReadAsStringAsync();
Assert.That(text, Does.Not.Contain("Geheimsache"),
$"„Geheimsache“ steht in {route} — dort fehlt der Freigabefilter.");
}
}
[Test]
public async Task Veroeffentlichen_macht_ihn_fuer_alle_sichtbar()
{
Datei("Geheimsache (2024).mkv");
using var server = new FlaroServer();
var admin = await server.SetUpAndSignIn();
var alsAdmin = server.AlsAngemeldeter(admin.Token);
var angelegt = await alsAdmin.PostAsJsonAsync("/api/v1/libraries",
new CreateLibraryRequest("Prüfbestand", "Movies", _bestand, ReviewNewItems: true));
var bibliothek = (await angelegt.Content.ReadFromJsonAsync())!;
await Scannen(alsAdmin, bibliothek.Id);
var gast = await server.NeuerBenutzer(alsAdmin, "gast", "gastpasswort");
Assert.That(await Titel(gast), Is.Empty);
var frei = await alsAdmin.PostAsJsonAsync("/api/v1/review/publish",
new FreigabeRequest(null, null));
frei.EnsureSuccessStatusCode();
Assert.That(await Titel(gast), Is.EqualTo(new[] { "Geheimsache" }));
}
/// Ein geplanter Titel ist sichtbar, aber noch nicht abspielbar.
///
///
///
/// Zwei Zustände, nicht einer. Ohne Zeitpunkt wartet ein Titel auf
/// eine Entscheidung — den soll niemand sehen. Mit einem Zeitpunkt in der
/// Zukunft ist die Entscheidung gefallen: er kommt, nur noch nicht. Ihn
/// dann zu verstecken hiesse, die Vorfreude zu unterschlagen, die der Sinn
/// des Planens ist.
///
///
/// Sichtbar heisst aber nicht abspielbar, und das ist der Teil, der
/// wirklich zählt: ohne den Riegel bei der Auslieferung wäre „Verfügbar ab
/// …" eine Zierde, und die Datei ginge trotzdem heraus.
///
///
[Test]
public async Task Ein_geplanter_Titel_ist_sichtbar_aber_nicht_abspielbar()
{
Datei("Geheimsache (2024).mkv");
using var server = new FlaroServer();
var admin = await server.SetUpAndSignIn();
var alsAdmin = server.AlsAngemeldeter(admin.Token);
var angelegt = await alsAdmin.PostAsJsonAsync("/api/v1/libraries",
new CreateLibraryRequest("Prüfbestand", "Movies", _bestand, ReviewNewItems: true));
var bibliothek = (await angelegt.Content.ReadFromJsonAsync())!;
await Scannen(alsAdmin, bibliothek.Id);
var gast = await server.NeuerBenutzer(alsAdmin, "gast", "gastpasswort");
// Auf morgen gelegt.
await alsAdmin.PostAsJsonAsync("/api/v1/review/publish",
new FreigabeRequest(null, DateTimeOffset.UtcNow.AddDays(1)));
Assert.That(await Titel(gast), Is.EqualTo(new[] { "Geheimsache" }),
"Ein geplanter Titel steht in der Mediathek — dafür plant man ihn.");
// **Aber die Datei geht nicht heraus.** Das ist der Teil, der zählt.
var filme = await gast.GetFromJsonAsync>("/api/v1/movies");
var datei = filme!.Items.Single().Files.Single();
var geladen = await gast.GetAsync($"/api/v1/files/{datei.Id}/download");
var gestroemt = await gast.PostAsJsonAsync($"/api/v1/files/{datei.Id}/stream",
new StreamRequest(["mp4"], ["h264"], ["aac"]));
Assert.Multiple(() =>
{
Assert.That(geladen.StatusCode, Is.EqualTo(HttpStatusCode.Forbidden));
Assert.That(gestroemt.StatusCode, Is.EqualTo(HttpStatusCode.Forbidden));
});
// **Auch für Administratoren.** Eine Hintertür für eine Rolle wäre
// eine Regel, die manchmal gilt; wer vorziehen will, hat den Knopf.
var alsAdminGeladen = await alsAdmin.GetAsync($"/api/v1/files/{datei.Id}/download");
Assert.That(alsAdminGeladen.StatusCode, Is.EqualTo(HttpStatusCode.Forbidden));
// Und er steht weiterhin in der Prüfliste — mit seinem Termin.
var wartend = await alsAdmin.GetFromJsonAsync>("/api/v1/review");
Assert.That(wartend, Has.Count.EqualTo(1));
Assert.That(wartend![0].PublishAt, Is.Not.Null);
// Auf gestern gelegt: sofort da, ohne dass irgendetwas umgeschaltet wurde.
await alsAdmin.PostAsJsonAsync("/api/v1/review/publish",
new FreigabeRequest(null, DateTimeOffset.UtcNow.AddDays(-1)));
Assert.That(await Titel(gast), Is.EqualTo(new[] { "Geheimsache" }));
var jetztGeladen = await gast.GetAsync($"/api/v1/files/{datei.Id}/download");
Assert.That(jetztGeladen.StatusCode, Is.Not.EqualTo(HttpStatusCode.Forbidden),
"ab dem Termin geht die Datei heraus");
}
/// Ohne Prüfung erscheint alles sofort — das ist die Vorgabe.
[Test]
public async Task Ohne_Pruefung_erscheint_alles_sofort()
{
Datei("Offen (2024).mkv");
using var server = new FlaroServer();
var admin = await server.SetUpAndSignIn();
var alsAdmin = server.AlsAngemeldeter(admin.Token);
var angelegt = await alsAdmin.PostAsJsonAsync("/api/v1/libraries",
new CreateLibraryRequest("Prüfbestand", "Movies", _bestand));
var bibliothek = (await angelegt.Content.ReadFromJsonAsync())!;
await Scannen(alsAdmin, bibliothek.Id);
var gast = await server.NeuerBenutzer(alsAdmin, "gast", "gastpasswort");
Assert.That(await Titel(gast), Is.EqualTo(new[] { "Offen" }));
Assert.That(await alsAdmin.GetFromJsonAsync>("/api/v1/review"), Is.Empty);
}
private static async Task Titel(HttpClient client) =>
(await client.GetFromJsonAsync>("/api/v1/movies"))!
.Items.Select(m => m.Title).ToArray();
private static async Task Scannen(HttpClient client, Guid bibliothek)
{
(await client.PostAsync($"/api/v1/libraries/{bibliothek}/scan", null))
.EnsureSuccessStatusCode();
for (var versuch = 0; versuch < 600; versuch++)
{
var stand = await client.GetFromJsonAsync($"/api/v1/libraries/{bibliothek}/scan");
if (stand!.State is "finished") return;
if (stand.State is "failed") Assert.Fail($"Der Scan ist gescheitert: {stand.Error}");
await Task.Delay(50);
}
Assert.Fail("Der Scan wurde nicht fertig.");
}
}