using System.Net; using System.Net.Http.Json; using Flaro.Api.Endpoints; namespace Flaro.Api.Tests; /// Scan gegen einen echten Ordner mit echten Dateien. public class ScanTests { private string _bestand = ""; [SetUp] public void Anlegen() => _bestand = Directory.CreateDirectory( Path.Combine(Path.GetTempPath(), "flaro-medien-" + Guid.CreateVersion7().ToString("N"))).FullName; [TearDown] public void Aufraeumen() { try { Directory.Delete(_bestand, recursive: true); } catch (IOException) { } } private void Datei(string relativ, int bytes = 2048) { var voll = Path.Combine(_bestand, relativ); Directory.CreateDirectory(Path.GetDirectoryName(voll)!); File.WriteAllBytes(voll, new byte[bytes]); } private async Task<(HttpClient Admin, Guid LibraryId)> MitBibliothek(FlaroServer server, string art) { var admin = await server.SetUpAndSignIn(); var client = server.AlsAngemeldeter(admin.Token); var angelegt = await client.PostAsJsonAsync("/api/v1/libraries", new CreateLibraryRequest("Prüfbestand", art, _bestand)); angelegt.EnsureSuccessStatusCode(); var bibliothek = (await angelegt.Content.ReadFromJsonAsync())!; return (client, bibliothek.Id); } /// Stösst an und wartet, bis der Hintergrundlauf fertig ist. private static async Task Scannen(HttpClient client, Guid bibliothek) { var angestossen = await client.PostAsync($"/api/v1/libraries/{bibliothek}/scan", null); Assert.That(angestossen.StatusCode, Is.EqualTo(HttpStatusCode.Accepted)); for (var versuch = 0; versuch < 100; versuch++) { var stand = await client.GetFromJsonAsync($"/api/v1/libraries/{bibliothek}/scan"); if (stand!.State is "finished") return stand; if (stand.State is "failed") Assert.Fail($"Der Scan ist gescheitert: {stand.Error}"); await Task.Delay(50); } Assert.Fail("Der Scan wurde in fünf Sekunden nicht fertig."); return null!; } [Test] public async Task Filme_werden_gefunden_und_benannt() { Datei("The.Matrix.1999.1080p.BluRay.x264-GRP.mkv"); Datei("Der Untergang (2004).mkv"); Datei("The Matrix (1999).srt"); // zählt nicht Datei("@eaDir/The.Matrix.1999.mkv"); // zählt auch nicht using var server = new FlaroServer(); var (client, bibliothek) = await MitBibliothek(server, "Movies"); var stand = await Scannen(client, bibliothek); Assert.That(stand.FilesFound, Is.EqualTo(2)); Assert.That(stand.Added, Is.EqualTo(2)); var filme = await client.GetFromJsonAsync>("/api/v1/movies"); Assert.That(filme!.Total, Is.EqualTo(2)); // Sortiert ohne Artikel: „Matrix" vor „Untergang". Assert.That(filme.Items.Select(m => m.Title), Is.EqualTo(new[] { "The Matrix", "Der Untergang" })); Assert.That(filme.Items[0].Year, Is.EqualTo(1999)); } /// Zweimal scannen muss dasselbe ergeben wie einmal. /// /// /// Sonst wächst die Mediathek bei jedem Lauf. Das ist der Fehler, /// den man erst nach Wochen bemerkt — wenn jeder Film fünfmal dasteht. /// [Test] public async Task Ein_zweiter_Lauf_aendert_nichts() { Datei("The Matrix (1999).mkv"); Datei("Inception (2010).mkv"); using var server = new FlaroServer(); var (client, bibliothek) = await MitBibliothek(server, "Movies"); await Scannen(client, bibliothek); var zweiter = await Scannen(client, bibliothek); Assert.That(zweiter.Added, Is.EqualTo(0)); Assert.That(zweiter.Removed, Is.EqualTo(0)); Assert.That((await client.GetFromJsonAsync>("/api/v1/movies"))!.Total, Is.EqualTo(2)); } [Test] public async Task Eine_geloeschte_Datei_verschwindet_aus_der_Mediathek() { Datei("The Matrix (1999).mkv"); Datei("Inception (2010).mkv"); using var server = new FlaroServer(); var (client, bibliothek) = await MitBibliothek(server, "Movies"); await Scannen(client, bibliothek); File.Delete(Path.Combine(_bestand, "Inception (2010).mkv")); var danach = await Scannen(client, bibliothek); Assert.That(danach.Removed, Is.EqualTo(1)); var filme = await client.GetFromJsonAsync>("/api/v1/movies"); Assert.That(filme!.Items.Select(m => m.Title), Is.EqualTo(new[] { "The Matrix" })); } /// 4K und 1080p sind ein Film mit zwei Dateien, nicht zwei Filme. [Test] public async Task Zwei_Fassungen_desselben_Films_bleiben_ein_Eintrag() { Datei("The Matrix (1999) - 1080p.mkv"); Datei("The Matrix (1999) - 2160p.mkv"); using var server = new FlaroServer(); var (client, bibliothek) = await MitBibliothek(server, "Movies"); await Scannen(client, bibliothek); var filme = await client.GetFromJsonAsync>("/api/v1/movies"); Assert.That(filme!.Total, Is.EqualTo(1)); Assert.That(filme.Items[0].Files, Has.Count.EqualTo(2)); } [Test] public async Task Serien_werden_in_Staffeln_und_Folgen_zerlegt() { Datei("Dark/Staffel 1/Dark - S01E01 - Geheimnisse.mkv"); Datei("Dark/Staffel 1/Dark - S01E02 - Lügen.mkv"); Datei("Dark/Staffel 2/Dark - S02E01 - Anfänge.mkv"); Datei("Notruf.Hafenkante.S01E01.German.1080p.mkv"); using var server = new FlaroServer(); var (client, bibliothek) = await MitBibliothek(server, "Shows"); await Scannen(client, bibliothek); var serien = await client.GetFromJsonAsync>("/api/v1/series"); Assert.That(serien!.Total, Is.EqualTo(2)); var dark = serien.Items.Single(s => s.Title == "Dark"); Assert.That(dark.EpisodeCount, Is.EqualTo(3)); Assert.That(dark.Seasons, Is.EqualTo(new[] { 1, 2 })); var folgen = await client.GetFromJsonAsync>($"/api/v1/series/{dark.Id}/episodes"); Assert.That(folgen!.Items.Select(f => f.Title), Is.EqualTo(new[] { "Geheimnisse", "Lügen", "Anfänge" })); var nurStaffel2 = await client.GetFromJsonAsync>( $"/api/v1/series/{dark.Id}/episodes?season=2"); Assert.That(nurStaffel2!.Items, Has.Count.EqualTo(1)); } /// Was keine Folgennummer trägt, wird nicht geraten. [Test] public async Task Dateien_ohne_Folgennummer_werden_gezaehlt_aber_nicht_zugeordnet() { Datei("Dark/Dark - S01E01.mkv"); Datei("Dark/Irgendein Mitschnitt.mkv"); using var server = new FlaroServer(); var (client, bibliothek) = await MitBibliothek(server, "Shows"); var stand = await Scannen(client, bibliothek); Assert.That(stand.FilesFound, Is.EqualTo(2)); Assert.That(stand.Unrecognised, Is.EqualTo(1)); Assert.That(stand.Added, Is.EqualTo(1)); } [Test] public async Task Die_Suche_grenzt_ein() { Datei("The Matrix (1999).mkv"); Datei("The Matrix Reloaded (2003).mkv"); Datei("Inception (2010).mkv"); using var server = new FlaroServer(); var (client, bibliothek) = await MitBibliothek(server, "Movies"); await Scannen(client, bibliothek); var treffer = await client.GetFromJsonAsync>("/api/v1/movies?search=matrix"); Assert.That(treffer!.Total, Is.EqualTo(2)); } /// Eine Anfrage darf sich die Seitengrösse nicht selbst aussuchen. [Test] public async Task Die_Seitengroesse_ist_gedeckelt() { for (var i = 1; i <= 5; i++) Datei($"Film {i} (200{i}).mkv"); using var server = new FlaroServer(); var (client, bibliothek) = await MitBibliothek(server, "Movies"); await Scannen(client, bibliothek); var seite = await client.GetFromJsonAsync>("/api/v1/movies?take=100000"); Assert.That(seite!.Total, Is.EqualTo(5), "Die Gesamtzahl bleibt richtig"); Assert.That(seite.Items, Has.Count.LessThanOrEqualTo(200)); var zweiteSeite = await client.GetFromJsonAsync>("/api/v1/movies?skip=2&take=2"); Assert.That(zweiteSeite!.Items, Has.Count.EqualTo(2)); Assert.That(zweiteSeite.Total, Is.EqualTo(5)); } [Test] public async Task Vor_dem_ersten_Lauf_steht_der_Scan_auf_untaetig() { using var server = new FlaroServer(); var (client, bibliothek) = await MitBibliothek(server, "Movies"); var stand = await client.GetFromJsonAsync($"/api/v1/libraries/{bibliothek}/scan"); Assert.That(stand!.State, Is.EqualTo("idle")); } [Test] public async Task Ein_gewoehnlicher_Benutzer_stoesst_keinen_Scan_an() { using var server = new FlaroServer(); var (admin, bibliothek) = await MitBibliothek(server, "Movies"); await admin.PostAsJsonAsync("/api/v1/users", new CreateUserRequest("gast", "auch-ein-langes-passwort", false)); var gast = await server.SignIn("gast", "auch-ein-langes-passwort", "iPad", "g-9"); var versuch = await server.AlsAngemeldeter(gast.Token) .PostAsync($"/api/v1/libraries/{bibliothek}/scan", null); Assert.That(versuch.StatusCode, Is.EqualTo(HttpStatusCode.Forbidden)); } [Test] public async Task Eine_Bibliothek_die_es_nicht_gibt_laesst_sich_nicht_scannen() { using var server = new FlaroServer(); var (client, _) = await MitBibliothek(server, "Movies"); var versuch = await client.PostAsync($"/api/v1/libraries/{Guid.CreateVersion7()}/scan", null); Assert.That(versuch.StatusCode, Is.EqualTo(HttpStatusCode.NotFound)); } }