using System.Net;
using System.Net.Http.Json;
using Flaro.Api.Endpoints;
namespace Flaro.Api.Tests;
/// Melden darf jeder, lesen nur die Verwaltung — und die eigenen sieht man selbst.
///
///
/// Der Test, der hier wirklich zählt, ist der mit dem ungeprüften Titel.
/// Die Rechteprüfung beim Melden steht nirgends als `Where` — sie fällt aus dem
/// Freigabefilter heraus. Das ist elegant und genau deshalb leicht zu
/// zerstören: wer in den Endpunkt einmal `IgnoreQueryFilters` schreibt, um
/// „auch zurückgezogene Titel zu finden", hat sie weggenommen, ohne dass eine
/// Zeile danach aussieht.
///
public class MeldungTests
{
private string _bestand = "";
[SetUp]
public void Anlegen() => _bestand = Directory.CreateDirectory(
Path.Combine(Path.GetTempPath(), "flaro-meldung-" + 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]);
}
[Test]
public async Task Eine_Meldung_zu_einem_Film_landet_beim_Administrator()
{
Datei("Heat (1995).mkv");
using var server = new FlaroServer();
var alsAdmin = await Aufsetzen(server, "Filme", "Movies");
var gast = await server.NeuerBenutzer(alsAdmin, "gast", "gastpasswortlang");
var film = (await Filme(gast)).Single();
var gemeldet = await gast.PostAsJsonAsync($"/api/v1/items/{film.Id}/reports",
new MeldungRequest("ton", "Der deutsche Ton setzt aus.", "1:02:03"));
var liste = await alsAdmin.GetFromJsonAsync>("/api/v1/reports");
Assert.That(gemeldet.StatusCode, Is.EqualTo(HttpStatusCode.OK));
Assert.That(liste, Has.Count.EqualTo(1));
Assert.Multiple(() =>
{
Assert.That(liste![0].Target, Is.EqualTo("movie"));
Assert.That(liste[0].ItemId, Is.EqualTo(film.Id));
Assert.That(liste[0].Title, Is.EqualTo("Heat"));
Assert.That(liste[0].Kind, Is.EqualTo("ton"));
Assert.That(liste[0].PositionSeconds, Is.EqualTo(3723));
Assert.That(liste[0].ReportedBy, Is.EqualTo("gast"));
Assert.That(liste[0].ResolvedAt, Is.Null);
});
}
[Test]
public async Task Melden_darf_jeder_die_Verwaltung_bleibt_zu()
{
Datei("Heat (1995).mkv");
using var server = new FlaroServer();
var alsAdmin = await Aufsetzen(server, "Filme", "Movies");
var gast = await server.NeuerBenutzer(alsAdmin, "gast", "gastpasswortlang");
var film = (await Filme(gast)).Single();
var gemeldet = await gast.PostAsJsonAsync($"/api/v1/items/{film.Id}/reports",
new MeldungRequest("bild", "Das Plakat gehört zu einem anderen Film."));
var meine = await gast.GetAsync("/api/v1/reports/mine");
var fremd = await gast.GetAsync("/api/v1/reports");
var id = (await alsAdmin.GetFromJsonAsync>("/api/v1/reports"))![0].Id;
var abhaken = await gast.PostAsync($"/api/v1/reports/{id}/resolve", null);
Assert.Multiple(() =>
{
Assert.That(gemeldet.StatusCode, Is.EqualTo(HttpStatusCode.OK));
Assert.That(meine.StatusCode, Is.EqualTo(HttpStatusCode.OK));
Assert.That(fremd.StatusCode, Is.EqualTo(HttpStatusCode.Forbidden),
"Die Liste aller Meldungen ist Verwaltung.");
Assert.That(abhaken.StatusCode, Is.EqualTo(HttpStatusCode.Forbidden));
});
}
[Test]
public async Task Jeder_sieht_nur_die_eigenen_Meldungen()
{
Datei("Heat (1995).mkv");
using var server = new FlaroServer();
var alsAdmin = await Aufsetzen(server, "Filme", "Movies");
var eine = await server.NeuerBenutzer(alsAdmin, "eine", "einepasswortlang");
var andere = await server.NeuerBenutzer(alsAdmin, "andere", "anderepasswortlang");
var film = (await Filme(eine)).Single();
await eine.PostAsJsonAsync($"/api/v1/items/{film.Id}/reports",
new MeldungRequest("ton", "Von mir."));
await andere.PostAsJsonAsync($"/api/v1/items/{film.Id}/reports",
new MeldungRequest("bild", "Von jemand anderem."));
var meine = await eine.GetFromJsonAsync>("/api/v1/reports/mine");
var alle = await alsAdmin.GetFromJsonAsync>("/api/v1/reports");
Assert.Multiple(() =>
{
Assert.That(meine, Has.Count.EqualTo(1));
Assert.That(meine![0].Comment, Is.EqualTo("Von mir."));
Assert.That(alle, Has.Count.EqualTo(2), "Der Administrator sieht beide.");
});
}
[Test]
public async Task Erledigt_heisst_abgehakt_und_nicht_geloescht()
{
Datei("Heat (1995).mkv");
using var server = new FlaroServer();
var alsAdmin = await Aufsetzen(server, "Filme", "Movies");
var gast = await server.NeuerBenutzer(alsAdmin, "gast", "gastpasswortlang");
var film = (await Filme(gast)).Single();
await gast.PostAsJsonAsync($"/api/v1/items/{film.Id}/reports",
new MeldungRequest("untertitel", "Die deutsche Spur ist leer."));
var id = (await alsAdmin.GetFromJsonAsync>("/api/v1/reports"))![0].Id;
(await alsAdmin.PostAsync($"/api/v1/reports/{id}/resolve", null)).EnsureSuccessStatusCode();
var offen = await alsAdmin.GetFromJsonAsync>("/api/v1/reports");
var erledigt = await alsAdmin.GetFromJsonAsync>("/api/v1/reports?erledigt=true");
var beimMelder = await gast.GetFromJsonAsync>("/api/v1/reports/mine");
Assert.Multiple(() =>
{
Assert.That(offen, Is.Empty);
Assert.That(erledigt, Has.Count.EqualTo(1));
Assert.That(erledigt![0].ResolvedAt, Is.Not.Null);
Assert.That(beimMelder, Has.Count.EqualTo(1),
"Der Melder sieht seine Meldung weiterhin — mitsamt dem Stand.");
Assert.That(beimMelder![0].ResolvedAt, Is.Not.Null);
});
// Ein Fehlgriff beim Abhaken darf kein Dauerzustand sein.
(await alsAdmin.PostAsync($"/api/v1/reports/{id}/reopen", null)).EnsureSuccessStatusCode();
var wiederOffen = await alsAdmin.GetFromJsonAsync>("/api/v1/reports");
Assert.That(wiederOffen, Has.Count.EqualTo(1));
}
[Test]
public async Task Was_man_nicht_sieht_kann_man_nicht_melden()
{
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 wartend = await alsAdmin.GetFromJsonAsync>("/api/v1/review");
var id = wartend![0].Id;
var gast = await server.NeuerBenutzer(alsAdmin, "gast", "gastpasswortlang");
var abgelehnt = await gast.PostAsJsonAsync($"/api/v1/items/{id}/reports",
new MeldungRequest("bild", "Sieht komisch aus."));
var vomAdmin = await alsAdmin.PostAsJsonAsync($"/api/v1/items/{id}/reports",
new MeldungRequest("bild", "Sieht komisch aus."));
Assert.Multiple(() =>
{
Assert.That(abgelehnt.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest));
Assert.That(vomAdmin.StatusCode, Is.EqualTo(HttpStatusCode.OK),
"Der Administrator sieht den Titel und darf ihn deshalb melden.");
});
}
[Test]
public async Task Eine_Staffel_wird_ueber_Serie_und_Nummer_gemeldet()
{
Datei("Dark/Season 01/Dark - S01E01.mkv");
Datei("Dark/Season 02/Dark - S02E01.mkv");
using var server = new FlaroServer();
var alsAdmin = await Aufsetzen(server, "Serien", "Shows");
var gast = await server.NeuerBenutzer(alsAdmin, "gast", "gastpasswortlang");
var serien = await gast.GetFromJsonAsync>("/api/v1/series");
var serie = serien!.Items.Single();
var gut = await gast.PostAsJsonAsync($"/api/v1/items/{serie.Id}/reports",
new MeldungRequest("ton", "Ab Folge zwei kein Ton.", null, SeasonNumber: 2));
var falscheStaffel = await gast.PostAsJsonAsync($"/api/v1/items/{serie.Id}/reports",
new MeldungRequest("ton", "Gibt es nicht.", null, SeasonNumber: 9));
var liste = await alsAdmin.GetFromJsonAsync>("/api/v1/reports");
Assert.Multiple(() =>
{
Assert.That(gut.StatusCode, Is.EqualTo(HttpStatusCode.OK));
Assert.That(falscheStaffel.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest));
Assert.That(liste, Has.Count.EqualTo(1));
Assert.That(liste![0].Target, Is.EqualTo("season"));
Assert.That(liste[0].ItemId, Is.EqualTo(serie.Id));
Assert.That(liste[0].SubTitle, Is.EqualTo("Staffel 2"));
});
}
[Test]
public async Task Eine_Folge_meldet_sich_unter_ihrer_eigenen_Kennung()
{
Datei("Dark/Season 01/Dark - S01E01.mkv");
using var server = new FlaroServer();
var alsAdmin = await Aufsetzen(server, "Serien", "Shows");
var gast = await server.NeuerBenutzer(alsAdmin, "gast", "gastpasswortlang");
var serie = (await gast.GetFromJsonAsync>("/api/v1/series"))!.Items.Single();
var folgen = await gast.GetFromJsonAsync>(
$"/api/v1/series/{serie.Id}/episodes");
var folge = folgen!.Items.Single();
await gast.PostAsJsonAsync($"/api/v1/items/{folge.Id}/reports",
new MeldungRequest("bild", "Bei 0:34 zwei Minuten schwarz.", "0:34:00"));
var liste = await alsAdmin.GetFromJsonAsync>("/api/v1/reports");
Assert.That(liste, Has.Count.EqualTo(1));
Assert.Multiple(() =>
{
Assert.That(liste![0].Target, Is.EqualTo("episode"));
Assert.That(liste[0].ItemId, Is.EqualTo(serie.Id),
"Die Detailseite gehört der Serie, nicht der Folge.");
Assert.That(liste[0].SubTitle, Does.StartWith("S1 · F1"));
Assert.That(liste[0].PositionSeconds, Is.EqualTo(2040));
});
}
[Test]
public async Task Ohne_Beschreibung_gibt_es_nichts_nachzusehen()
{
Datei("Heat (1995).mkv");
using var server = new FlaroServer();
var alsAdmin = await Aufsetzen(server, "Filme", "Movies");
var gast = await server.NeuerBenutzer(alsAdmin, "gast", "gastpasswortlang");
var film = (await Filme(gast)).Single();
var leer = await gast.PostAsJsonAsync($"/api/v1/items/{film.Id}/reports",
new MeldungRequest("ton", " "));
var zuLang = await gast.PostAsJsonAsync($"/api/v1/items/{film.Id}/reports",
new MeldungRequest("ton", new string('a', 2001)));
var unbekannt = await gast.PostAsJsonAsync($"/api/v1/items/{film.Id}/reports",
new MeldungRequest("geruch", "Riecht komisch."));
Assert.Multiple(() =>
{
Assert.That(leer.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest));
Assert.That(zuLang.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest));
Assert.That(unbekannt.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest));
});
}
/// „2:03" sind zwei Minuten, nicht zwei Stunden.
///
///
/// TimeSpan.Parse läse es als zwei Stunden und drei Minuten. Wer
/// neben einem Film „2:03" schreibt, meint das, was unter dem
/// Fortschrittsbalken steht.
///
[TestCase("1:02:03", 3723)]
[TestCase("2:03", 123)]
[TestCase("90", 90)]
public async Task Die_Stelle_kommt_als_Zeit_zurueck(string geschrieben, double sekunden)
{
Datei("Heat (1995).mkv");
using var server = new FlaroServer();
var alsAdmin = await Aufsetzen(server, "Filme", "Movies");
var film = (await Filme(alsAdmin)).Single();
await alsAdmin.PostAsJsonAsync($"/api/v1/items/{film.Id}/reports",
new MeldungRequest("bild", "Hier.", geschrieben));
var liste = await alsAdmin.GetFromJsonAsync>("/api/v1/reports");
Assert.That(liste![0].PositionSeconds, Is.EqualTo(sekunden));
}
[Test]
public async Task Eine_unleserliche_Stelle_wird_abgewiesen()
{
Datei("Heat (1995).mkv");
using var server = new FlaroServer();
var alsAdmin = await Aufsetzen(server, "Filme", "Movies");
var film = (await Filme(alsAdmin)).Single();
var krumm = await alsAdmin.PostAsJsonAsync($"/api/v1/items/{film.Id}/reports",
new MeldungRequest("bild", "Hier.", "Mitte"));
var ohne = await alsAdmin.PostAsJsonAsync($"/api/v1/items/{film.Id}/reports",
new MeldungRequest("bild", "Betrifft den ganzen Film.", ""));
var liste = await alsAdmin.GetFromJsonAsync>("/api/v1/reports");
Assert.Multiple(() =>
{
Assert.That(krumm.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest));
Assert.That(ohne.StatusCode, Is.EqualTo(HttpStatusCode.OK));
Assert.That(liste, Has.Count.EqualTo(1));
Assert.That(liste![0].PositionSeconds, Is.Null);
});
}
/* ---------- Gerüst ---------- */
private async Task Aufsetzen(FlaroServer server, string name, string art)
{
var admin = await server.SetUpAndSignIn();
var alsAdmin = server.AlsAngemeldeter(admin.Token);
var angelegt = await alsAdmin.PostAsJsonAsync("/api/v1/libraries",
new CreateLibraryRequest(name, art, _bestand, ReviewNewItems: false));
angelegt.EnsureSuccessStatusCode();
var bibliothek = (await angelegt.Content.ReadFromJsonAsync())!;
await Scannen(alsAdmin, bibliothek.Id);
return alsAdmin;
}
private static async Task> Filme(HttpClient client) =>
(await client.GetFromJsonAsync>("/api/v1/movies"))!.Items;
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.");
}
}