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."); } }