using System.Net; using System.Net.Http.Json; using Flaro.Api.Endpoints; namespace Flaro.Api.Tests; /// Mehrere Mediatheken nebeneinander — wie bei Plex und Jellyfin. /// /// /// Zwei Filmbibliotheken sind keine doppelte Filmbibliothek. „Filme" /// und „Dokumentationen" liegen in verschiedenen Ordnern, gehören in der /// Seitenleiste getrennt und dürfen sich in keiner Liste vermischen. Dass /// das Modell es kann, hiess bisher nicht, dass es geprüft war. /// public class MultipleLibraryTests { private readonly List _ordner = []; [TearDown] public void Aufraeumen() { foreach (var o in _ordner) { try { Directory.Delete(o, recursive: true); } catch (IOException) { /* Kür */ } } _ordner.Clear(); } /// Ein Ordner mit den genannten Dateien darin. private string Bestand(params string[] dateien) { var wurzel = Directory.CreateDirectory( Path.Combine(Path.GetTempPath(), "flaro-mehrere-" + Guid.CreateVersion7().ToString("N"))).FullName; _ordner.Add(wurzel); foreach (var relativ in dateien) { var voll = Path.Combine(wurzel, relativ); Directory.CreateDirectory(Path.GetDirectoryName(voll)!); File.WriteAllBytes(voll, new byte[2048]); } return wurzel; } private static async Task Anlegen( HttpClient client, string name, string art, string pfad) { var antwort = await client.PostAsJsonAsync("/api/v1/libraries", new CreateLibraryRequest(name, art, pfad)); antwort.EnsureSuccessStatusCode(); return (await antwort.Content.ReadFromJsonAsync())!; } private static async Task Scannen(HttpClient client, Guid bibliothek) { (await client.PostAsync($"/api/v1/libraries/{bibliothek}/scan", null)).EnsureSuccessStatusCode(); for (var versuch = 0; versuch < 100; 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 in fünf Sekunden nicht fertig."); } [Test] public async Task Zwei_Filmbibliotheken_bleiben_getrennt() { var spielfilme = Bestand("Matrix (1999)/Matrix (1999).mkv", "Inception (2010)/Inception (2010).mkv"); var dokus = Bestand("Winged Migration (2001)/Winged Migration (2001).mkv"); using var server = new FlaroServer(); var client = server.AlsAngemeldeter((await server.SetUpAndSignIn()).Token); var a = await Anlegen(client, "Filme", "Movies", spielfilme); var b = await Anlegen(client, "Dokumentationen", "Movies", dokus); await Scannen(client, a.Id); await Scannen(client, b.Id); var inA = await client.GetFromJsonAsync>($"/api/v1/movies?library={a.Id}"); var inB = await client.GetFromJsonAsync>($"/api/v1/movies?library={b.Id}"); var alle = await client.GetFromJsonAsync>("/api/v1/movies"); Assert.Multiple(() => { Assert.That(inA!.Total, Is.EqualTo(2)); Assert.That(inB!.Total, Is.EqualTo(1)); Assert.That(inB.Items[0].Title, Is.EqualTo("Winged Migration")); // Ohne Einschränkung alle zusammen — für die Suche quer durch alles. Assert.That(alle!.Total, Is.EqualTo(3)); }); } /// Die Seitenleiste braucht zu jeder Zeile eine Zahl. [Test] public async Task Die_Liste_nennt_je_Bibliothek_die_Anzahl() { var filme = Bestand("Matrix (1999)/Matrix (1999).mkv"); var serien = Bestand("Dark/Season 01/Dark - S01E01 - Geheimnisse.mkv", "Dark/Season 01/Dark - S01E02 - Luegen.mkv"); var leer = Bestand(); using var server = new FlaroServer(); var client = server.AlsAngemeldeter((await server.SetUpAndSignIn()).Token); var a = await Anlegen(client, "Filme", "Movies", filme); var b = await Anlegen(client, "Serien", "Shows", serien); await Anlegen(client, "Noch nichts drin", "Movies", leer); await Scannen(client, a.Id); await Scannen(client, b.Id); var liste = await client.GetFromJsonAsync>("/api/v1/libraries"); var nachNamen = liste!.ToDictionary(l => l.Name); Assert.Multiple(() => { Assert.That(nachNamen["Filme"].ItemCount, Is.EqualTo(1)); // **Serien, nicht Folgen.** Zwei Folgen einer Serie sind eine Zeile. Assert.That(nachNamen["Serien"].ItemCount, Is.EqualTo(1)); Assert.That(nachNamen["Noch nichts drin"].ItemCount, Is.Zero); }); } [Test] public async Task Eine_Bibliothek_laesst_sich_umbenennen() { using var server = new FlaroServer(); var client = server.AlsAngemeldeter((await server.SetUpAndSignIn()).Token); var angelegt = await Anlegen(client, "Flime", "Movies", Bestand()); var antwort = await client.PutAsJsonAsync($"/api/v1/libraries/{angelegt.Id}", new RenameLibraryRequest("Filme")); var geaendert = await antwort.Content.ReadFromJsonAsync(); Assert.Multiple(() => { Assert.That(geaendert!.Name, Is.EqualTo("Filme")); Assert.That(geaendert.Path, Is.EqualTo(angelegt.Path), "der Pfad bleibt, wo er war"); }); } [Test] public async Task Ein_leerer_Name_wird_abgelehnt() { using var server = new FlaroServer(); var client = server.AlsAngemeldeter((await server.SetUpAndSignIn()).Token); var angelegt = await Anlegen(client, "Filme", "Movies", Bestand()); var antwort = await client.PutAsJsonAsync($"/api/v1/libraries/{angelegt.Id}", new RenameLibraryRequest(" ")); Assert.That(antwort.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest)); } /// Entfernt wird der Eintrag — nicht der Film auf der Platte. /// /// /// Das ist die wichtigste Zusage dieses Servers. Ein Server, der /// beim Aufräumen in der Oberfläche fremde Mediendateien löscht, ist /// einer, den man nicht noch einmal benutzt. /// [Test] public async Task Loeschen_nimmt_die_Eintraege_und_laesst_die_Dateien_liegen() { var ordner = Bestand("Matrix (1999)/Matrix (1999).mkv"); var datei = Path.Combine(ordner, "Matrix (1999)", "Matrix (1999).mkv"); using var server = new FlaroServer(); var client = server.AlsAngemeldeter((await server.SetUpAndSignIn()).Token); var angelegt = await Anlegen(client, "Filme", "Movies", ordner); await Scannen(client, angelegt.Id); Assert.That((await client.GetFromJsonAsync>("/api/v1/movies"))!.Total, Is.EqualTo(1), "vorher ist er da"); var geloescht = await client.DeleteAsync($"/api/v1/libraries/{angelegt.Id}"); Assert.Multiple(() => { Assert.That(geloescht.StatusCode, Is.EqualTo(HttpStatusCode.NoContent)); Assert.That(File.Exists(datei), Is.True, "die Datei liegt noch da"); }); var danach = await client.GetFromJsonAsync>("/api/v1/movies"); var liste = await client.GetFromJsonAsync>("/api/v1/libraries"); Assert.Multiple(() => { Assert.That(danach!.Total, Is.Zero); Assert.That(liste, Is.Empty); }); } [Test] public async Task Nur_Administratoren_duerfen_loeschen_und_umbenennen() { using var server = new FlaroServer(); var admin = server.AlsAngemeldeter((await server.SetUpAndSignIn()).Token); var angelegt = await Anlegen(admin, "Filme", "Movies", Bestand()); (await admin.PostAsJsonAsync("/api/v1/users", new CreateUserRequest("mitbewohner", "ein-langes-passwort", false))).EnsureSuccessStatusCode(); var gast = server.AlsAngemeldeter( (await server.SignIn("mitbewohner", "ein-langes-passwort", "iPad", "geraet-2")).Token); var umbenannt = await gast.PutAsJsonAsync($"/api/v1/libraries/{angelegt.Id}", new RenameLibraryRequest("Meins jetzt")); var geloescht = await gast.DeleteAsync($"/api/v1/libraries/{angelegt.Id}"); Assert.Multiple(() => { Assert.That(umbenannt.StatusCode, Is.EqualTo(HttpStatusCode.Forbidden)); Assert.That(geloescht.StatusCode, Is.EqualTo(HttpStatusCode.Forbidden)); }); } /// „Zuletzt hinzugefügt" braucht eine andere Reihenfolge als A–Z. [Test] public async Task Nach_Aufnahme_sortiert_steht_das_Neueste_oben() { var ordner = Bestand("Zzz Zuletzt (2024)/Zzz Zuletzt (2024).mkv"); using var server = new FlaroServer(); var client = server.AlsAngemeldeter((await server.SetUpAndSignIn()).Token); var angelegt = await Anlegen(client, "Filme", "Movies", ordner); await Scannen(client, angelegt.Id); // Ein zweiter Film, nachweislich später aufgenommen. var voll = Path.Combine(ordner, "Aaa Zuerst (1999)", "Aaa Zuerst (1999).mkv"); Directory.CreateDirectory(Path.GetDirectoryName(voll)!); await File.WriteAllBytesAsync(voll, new byte[2048]); await Task.Delay(1100); await Scannen(client, angelegt.Id); var alphabetisch = await client.GetFromJsonAsync>("/api/v1/movies"); var nachAufnahme = await client.GetFromJsonAsync>("/api/v1/movies?sort=added"); Assert.Multiple(() => { Assert.That(alphabetisch!.Items.Select(m => m.Title), Is.EqualTo(new[] { "Aaa Zuerst", "Zzz Zuletzt" })); Assert.That(nachAufnahme!.Items[0].Title, Is.EqualTo("Aaa Zuerst"), "zuletzt gefunden steht oben"); }); } }