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));
// **Dreissig Sekunden, nicht fünf.** Die alte Grenze war für zwei,
// drei Dateien bemessen. Der Test über 120 Dateien schaffte sie
// allein, fiel aber durch, sobald die ganze Suite nebenher lief —
// und das war kein Fehler im Scan, sondern eine zu knappe Geduld.
// Teurer wird es nur, wenn wirklich etwas hängt.
const int schritte = 600, abstand = 50;
for (var versuch = 0; versuch < schritte; 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(abstand);
}
Assert.Fail($"Der Scan wurde in {schritte * abstand / 1000} 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));
}
/// Eine verschwundene Datei wird gemeldet, nicht stillschweigend gelöscht.
///
///
/// Vorher fiel hier der Titel samt Sehfortschritt. Das ist für eine
/// wirklich gelöschte Datei richtig — und falsch für jeden anderen Grund,
/// aus dem eine Datei kurz nicht da ist. Beides sieht für den Scan gleich
/// aus, also entscheidet jetzt der Administrator.
///
[Test]
public async Task Eine_verschwundene_Datei_wird_gemeldet_statt_geloescht()
{
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.Missing, Is.EqualTo(1));
Assert.That((await client.GetFromJsonAsync>("/api/v1/movies"))!.Total,
Is.EqualTo(2), "Der Titel bleibt stehen, bis jemand ihn entfernt.");
var vermisst = await client.GetFromJsonAsync>("/api/v1/libraries/missing");
Assert.That(vermisst, Has.Count.EqualTo(1));
Assert.That(vermisst![0].Title, Is.EqualTo("Inception"));
// Und erst auf Ansage ist er wirklich weg.
(await client.DeleteAsync("/api/v1/libraries/missing")).EnsureSuccessStatusCode();
var filme = await client.GetFromJsonAsync>("/api/v1/movies");
Assert.That(filme!.Items.Select(m => m.Title), Is.EqualTo(new[] { "The Matrix" }));
}
/// Kommt die Datei zurück, verschwindet die Markierung von selbst.
[Test]
public async Task Eine_zurueckgekehrte_Datei_gilt_wieder_als_da()
{
// Zwei Dateien, damit der Ordner nicht leer wird — sonst greift die
// Notbremse aus dem Test darunter, und zwar zu Recht.
Datei("Inception (2010).mkv");
Datei("The Matrix (1999).mkv");
using var server = new FlaroServer();
var (client, bibliothek) = await MitBibliothek(server, "Movies");
await Scannen(client, bibliothek);
var pfad = Path.Combine(_bestand, "Inception (2010).mkv");
var inhalt = await File.ReadAllBytesAsync(pfad);
File.Delete(pfad);
await Scannen(client, bibliothek);
Assert.That(await client.GetFromJsonAsync>("/api/v1/libraries/missing"),
Has.Count.EqualTo(1));
await File.WriteAllBytesAsync(pfad, inhalt);
await Scannen(client, bibliothek);
Assert.That(await client.GetFromJsonAsync>("/api/v1/libraries/missing"),
Is.Empty);
}
/// Die Notbremse: ein leerer Ordner bricht ab, statt alles zu verwerfen.
///
///
/// Der teuerste denkbare Fehler. Ist das Volume beim Start des NAS
/// noch nicht eingehängt, liegt der Ordner leer da — und ohne diese Prüfung
/// gälte der gesamte Bestand als verschwunden.
///
[Test]
public async Task Ein_ploetzlich_leerer_Ordner_bricht_den_Scan_ab()
{
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);
foreach (var d in Directory.EnumerateFiles(_bestand, "*", SearchOption.AllDirectories))
File.Delete(d);
var angestossen = await client.PostAsync($"/api/v1/libraries/{bibliothek}/scan", null);
Assert.That(angestossen.StatusCode, Is.EqualTo(HttpStatusCode.Accepted));
ScanStatus? stand = null;
for (var versuch = 0; versuch < 600; versuch++)
{
stand = await client.GetFromJsonAsync($"/api/v1/libraries/{bibliothek}/scan");
if (stand!.State is "failed" or "finished") break;
await Task.Delay(50);
}
Assert.That(stand!.State, Is.EqualTo("failed"));
Assert.That(stand.Error, Does.Contain("abgebrochen"));
Assert.That((await client.GetFromJsonAsync>("/api/v1/movies"))!.Total,
Is.EqualTo(2), "Es darf nichts verändert worden sein.");
Assert.That(await client.GetFromJsonAsync>("/api/v1/libraries/missing"),
Is.Empty, "Auch nicht als vermisst markiert.");
}
/// Das Aufräumen darf nicht wegräumen, was derselbe Lauf schrieb.
///
///
/// Mehr als fünfzig Dateien, und das ist der Punkt. Seit der Scan
/// alle fünfzig Dateien zwischenspeichert, liegt ein Teil des Bestands
/// schon in der Datenbank, während der Lauf noch läuft. Direkt danach
/// löscht er alles, was eine ältere SeenAt-Zeit trägt als dieser
/// Lauf — würde die Zeit irgendwo neu genommen statt vor der Schleife
/// festgehalten, risse dieser Schritt den halben Bestand mit.
///
/// Die Dateien hier sind Nullbytes, an denen ffprobe scheitert. Damit
/// greift das Zwischenspeichern aus der Schleife und nicht das aus der
/// Untersuchung — also genau der Weg, der sonst ungeprüft bliebe.
///
[Test]
public async Task Ein_grosser_Bestand_ueberlebt_das_Aufraeumen()
{
for (var i = 1; i <= 120; i++) Datei($"Prüffilm {i:000} (2024).mkv");
using var server = new FlaroServer();
var (client, bibliothek) = await MitBibliothek(server, "Movies");
var erster = await Scannen(client, bibliothek);
Assert.That(erster.Added, Is.EqualTo(120));
Assert.That(erster.Removed, Is.Zero, "Ein erster Lauf darf nichts entfernen.");
Assert.That((await client.GetFromJsonAsync>("/api/v1/movies"))!.Total,
Is.EqualTo(120));
// Und ein zweiter Lauf bleibt ebenso vollständig.
var zweiter = await Scannen(client, bibliothek);
Assert.That(zweiter.Removed, Is.Zero);
Assert.That((await client.GetFromJsonAsync>("/api/v1/movies"))!.Total,
Is.EqualTo(120));
}
/// Schreibvarianten desselben Titels ergeben einen Eintrag.
///
///
/// Der Fall, der die Mediathek doppelt aussehen liess. Der Scan
/// verglich den Titel aus dem Dateinamen zeichengenau — ein Punkt
/// Unterschied, und es wurden zwei Filme, die anschliessend beide
/// denselben TMDB-Eintrag bekamen und ununterscheidbar nebeneinander
/// standen.
///
[Test]
public async Task Schreibvarianten_ergeben_einen_Film()
{
Datei("Dr. Dolittle 2 (2001).mkv");
Datei("Dr Dolittle 2.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));
Assert.That(filme.Items[0].Year, Is.EqualTo(2001),
"Das Jahr der einen Datei gilt auch, wenn die andere keines trägt.");
}
/// Eine mobile Fassung ist kein eigener Film.
[Test]
public async Task Eine_optimierte_Fassung_bleibt_beim_Film()
{
Datei("The Matrix (1999).mkv");
Datei("The Matrix (1999) - Mobil.mp4");
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), "Sonst stünde ein zweiter Film ‚The Matrix - Mobil‘ daneben.");
Assert.That(filme.Items[0].Title, Is.EqualTo("The Matrix"));
Assert.That(filme.Items[0].Files, Has.Count.EqualTo(2));
}
/// Gleicher Name, anderes Jahr — zwei Filme, und das muss so bleiben.
[Test]
public async Task Zwei_Verfilmungen_bleiben_zwei_Filme()
{
Datei("Dune (1984).mkv");
Datei("Dune (2021).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(2));
}
/// Eine Serie, die ihren Namen verliert, bleibt nicht als Leiche stehen.
///
///
/// Der Weg, den ein vorhandener Bestand nach dem Update geht. Ohne
/// Ordner benennt der Teil vor der Folgennummer die Serie. Wandert die
/// Datei in einen Ordner, gehört sie zu diesem — und der alte Eintrag muss
/// verschwinden, statt als leere Serie in der Mediathek zu bleiben.
///
[Test]
public async Task Eine_verwaiste_Serie_verschwindet_beim_naechsten_Lauf()
{
Datei("Tatortreiniger S01E01 Kapitel 1.mkv");
using var server = new FlaroServer();
var (client, bibliothek) = await MitBibliothek(server, "Shows");
await Scannen(client, bibliothek);
var vorher = await client.GetFromJsonAsync>("/api/v1/series");
Assert.That(vorher!.Total, Is.EqualTo(1), "Ohne Ordner zählt der Titel vor der Folgennummer.");
var alterName = vorher.Items[0].Title;
// **Verschieben, nicht löschen und neu anlegen.** Beim Verschieben
// bleibt der Zeitstempel erhalten — und genau daran erkennt der Scan,
// dass es dieselbe Datei ist. Ein Test, der sie neu anlegt, prüft
// etwas anderes als das, was ein Mensch tut.
var alt = Path.Combine(_bestand, "Tatortreiniger S01E01 Kapitel 1.mkv");
var neuerPfad = Path.Combine(_bestand, "Nachtstreife", "Tatortreiniger S01E01 Kapitel 1.mkv");
Directory.CreateDirectory(Path.GetDirectoryName(neuerPfad)!);
File.Move(alt, neuerPfad);
await Scannen(client, bibliothek);
var nachher = await client.GetFromJsonAsync>("/api/v1/series");
Assert.That(nachher!.Total, Is.EqualTo(1), "Die alte Serie darf nicht danebenstehen.");
Assert.That(nachher.Items[0].Title, Is.EqualTo("Nachtstreife"));
Assert.That(nachher.Items[0].Title, Is.Not.EqualTo(alterName));
Assert.That(nachher.Items[0].EpisodeCount, Is.EqualTo(1));
}
/// Aus einem nackten Dateinamen wird keine Serie erfunden.
///
///
/// Der teuerste Fehler des Projekts. Lag eine Datei direkt in der
/// Wurzel und trug vor der Folgennummer nichts, wurde der ganze Dateiname
/// zum Serientitel — samt Folgentitel. Aus 95 Serien wurden 737, jede mit
/// genau einer Folge, und das bei jedem Lauf aufs Neue. Lieber nichts
/// behaupten als eine Geisterserie anlegen.
///
[Test]
public async Task Ein_nackter_Dateiname_erfindet_keine_Serie()
{
Datei("S01E01.mkv");
Datei("S01E02.mkv");
Datei("S02E01.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(0),
"Drei namenlose Dateien ergaben vorher drei Serien.");
}
/// Lose Dateien derselben Serie landen unter einer Serie.
///
///
/// Ohne Ordner trägt der Teil vor der Folgennummer den Serientitel. Dass
/// dahinter bei jeder Datei ein anderer Folgentitel steht, darf daran
/// nichts ändern — genau daraus entstanden die Dutzende Einträge
/// „In höchster Not · …", jeder mit einer Folge.
///
[Test]
public async Task Lose_Dateien_mit_Folgentiteln_ergeben_eine_Serie()
{
Datei("In höchster Not - S01E01 - Bergrettung am Grat.mkv");
Datei("In höchster Not - S01E02 - Lawine im Tal.mkv");
Datei("In höchster Not - S01E03 - Sturz in die Klamm.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(1));
Assert.That(serien.Items[0].EpisodeCount, Is.EqualTo(3));
}
/// Derselbe Pfad zweimal bringt den ganzen Lauf nicht mehr um.
///
///
/// Ein doppelt eingehängter Ordner oder eine Verknüpfung zurück in die
/// Mediathek liefert denselben Pfad ein zweites Mal. Vorher entstand dabei
/// eine zweite Zeile mit gleichem (LibraryId, Path), der
/// Eindeutigkeitsindex brach, und der ganze Scan endete mit
/// „An error occurred while saving the entity changes".
///
[Test]
public async Task Ein_doppelter_Pfad_bricht_den_Scan_nicht_ab()
{
Datei("Pingu/Pingu - S01E01.mkv");
// Eine Verknüpfung auf den Serienordner — derselbe Inhalt, zweiter Weg.
Directory.CreateSymbolicLink(
Path.Combine(_bestand, "Pingu-nochmal"), Path.Combine(_bestand, "Pingu"));
using var server = new FlaroServer();
var (client, bibliothek) = await MitBibliothek(server, "Shows");
await Scannen(client, bibliothek);
var zustand = await client.GetFromJsonAsync(
$"/api/v1/libraries/{bibliothek}/scan");
Assert.That(zustand!.State, Is.Not.EqualTo("failed"), zustand.Error ?? "");
var serien = await client.GetFromJsonAsync>("/api/v1/series");
Assert.That(serien!.Total, Is.EqualTo(1), "Zwei Wege, eine Serie.");
}
/// 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 — aber auch nicht verschwiegen.
///
///
/// Beide Hälften gehören zusammen. Eine erfundene Staffel 1 wäre
/// falsch; die Datei lautlos zu überspringen war aber genauso falsch. Bei
/// 3559 von 5884 Dateien verschwand so die halbe Mediathek, ohne dass
/// irgendwo eine Zahl davon erzählte. Added zählt Dateizeilen, und
/// eine bekommt sie jetzt — zugeordnet ist sie deshalb nicht.
///
[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(2), "Beide Dateien bekommen eine Zeile.");
// Die Serie hat genau eine Folge — der Mitschnitt hängt an keiner.
var serien = await client.GetFromJsonAsync>("/api/v1/series");
Assert.That(serien!.Total, Is.EqualTo(1));
Assert.That(serien.Items[0].EpisodeCount, Is.EqualTo(1));
// Und er ist auffindbar, statt lautlos zu verschwinden.
var offen = await client.GetFromJsonAsync(
$"/api/v1/libraries/{bibliothek}/unassigned");
Assert.That(offen!.Gesamt, Is.EqualTo(1));
Assert.That(offen.Dateien[0].Pfad, Does.EndWith("Irgendein Mitschnitt.mkv"));
Assert.That(offen.Dateien[0].Grund, Does.Contain("keine Folgennummer"));
}
[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));
}
}