Files
Flaro/server/tests/Flaro.Api.Tests/PlaybackTests.cs
T
ScarriffleandClaude Opus 5 557708b769 Ein Klick, der nichts tat, und niemand sagte warum
Eine Serienkachel kannte der Server als Titel nicht: „als gesehen markieren"
lief in eine 404, und weil das Kontextmenü die Ablehnung verschluckte, geschah
sichtbar gar nichts. Dasselbe beim Favoriten. UserItemState bekommt eine
Serienkennung, das Häkchen zieht alle Folgen mit, und was wirft, erscheint als
Meldung statt in der Browserkonsole.

Dazu eine Mehrfachauswahl in der Mediathek — gesehen, ungesehen, Metadaten
neu holen für das Gewählte, in einer Anfrage statt in zweihundert. Ein neues
Plakat steht sofort auf der Kachel, und ein Titel, den es nicht mehr gibt,
endet nicht mehr in einer Sackgasse.

Gemessen: ein POST auf /refresh ohne Rumpf antwortete nach der Erweiterung mit
404 — also genau der Aufruf „alles auffrischen", den es länger gibt als die
Auswahl. Der Rumpf wird jetzt von Hand gelesen, und beide Formen stehen in
einer Prüfung.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-05 18:28:33 +02:00

671 lines
26 KiB
C#

using System.Net;
using System.Net.Http.Json;
using Flaro.Api.Endpoints;
using Flaro.Core.Playback;
namespace Flaro.Api.Tests;
/// <summary>Fortschritt, Gesehen-Status und die Startseite — je Benutzer.</summary>
public class PlaybackTests
{
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)
{
var voll = Path.Combine(_bestand, relativ);
Directory.CreateDirectory(Path.GetDirectoryName(voll)!);
File.WriteAllBytes(voll, new byte[2048]);
}
/// <summary>Legt eine Bibliothek an, scannt sie und meldet den Client.</summary>
private async Task<HttpClient> Eingelesen(FlaroServer server, string art = "Movies")
{
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<LibraryResponse>())!;
(await client.PostAsync($"/api/v1/libraries/{bibliothek.Id}/scan", null)).EnsureSuccessStatusCode();
for (var versuch = 0; versuch < 100; versuch++)
{
var stand = await client.GetFromJsonAsync<ScanStatus>($"/api/v1/libraries/{bibliothek.Id}/scan");
if (stand!.State is "finished") return client;
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!;
}
private static async Task<Guid> ErsterFilm(HttpClient client)
{
var seite = await client.GetFromJsonAsync<Page<MovieResponse>>("/api/v1/movies");
return seite!.Items[0].Id;
}
private static Task<HttpResponseMessage> Melden(HttpClient c, Guid id, double stelle, double? laenge) =>
c.PostAsJsonAsync($"/api/v1/items/{id}/progress", new ProgressRequest(stelle, laenge));
// ------------------------------------------------------------------
/// <summary>Der Fall, um dessentwillen der Server existiert.</summary>
///
/// <remarks>
/// Jellyfin verwirft bei zwei Stunden alles unterhalb von sechs Minuten —
/// <c>MinResumePct</c> steht auf 5. Hier kommt die Stelle zurück.
/// </remarks>
[Test]
public async Task Fuenf_Minuten_von_zwei_Stunden_bleiben_erhalten()
{
Datei("Der Untergang (2004)/Der Untergang (2004).mkv");
using var server = new FlaroServer();
var client = await Eingelesen(server);
var film = await ErsterFilm(client);
var antwort = await Melden(client, film, 300, 7200);
var stand = (await antwort.Content.ReadFromJsonAsync<ItemStateResponse>())!;
Assert.Multiple(() =>
{
Assert.That(stand.PositionSeconds, Is.EqualTo(300));
Assert.That(stand.Watched, Is.False);
});
}
[Test]
public async Task Eine_Sekunde_wird_schon_gemerkt()
{
Datei("Matrix (1999)/Matrix (1999).mkv");
using var server = new FlaroServer();
var client = await Eingelesen(server);
var film = await ErsterFilm(client);
await Melden(client, film, 1, 7200);
var seite = await client.GetFromJsonAsync<Page<MovieResponse>>("/api/v1/movies");
Assert.That(seite!.Items[0].State!.PositionSeconds, Is.EqualTo(1));
}
[Test]
public async Task Kurz_vor_Schluss_gilt_er_als_gesehen_und_behaelt_die_Stelle()
{
Datei("Matrix (1999)/Matrix (1999).mkv");
using var server = new FlaroServer();
var client = await Eingelesen(server);
var film = await ErsterFilm(client);
var antwort = await Melden(client, film, 7000, 7200);
var stand = (await antwort.Content.ReadFromJsonAsync<ItemStateResponse>())!;
Assert.Multiple(() =>
{
Assert.That(stand.Watched, Is.True);
Assert.That(stand.PositionSeconds, Is.EqualTo(7000));
Assert.That(stand.PlayCount, Is.EqualTo(1));
});
}
[Test]
public async Task Angefangenes_steht_in_Weiterschauen()
{
Datei("Matrix (1999)/Matrix (1999).mkv");
using var server = new FlaroServer();
var client = await Eingelesen(server);
var film = await ErsterFilm(client);
await Melden(client, film, 600, 7200);
var reihe = await client.GetFromJsonAsync<Page<ContinueEntry>>("/api/v1/home/continue");
Assert.Multiple(() =>
{
Assert.That(reihe!.Items, Has.Count.EqualTo(1));
Assert.That(reihe.Items[0].Title, Is.EqualTo("Matrix"));
Assert.That(reihe.Items[0].Kind, Is.EqualTo("movie"));
Assert.That(reihe.Items[0].State.PositionSeconds, Is.EqualTo(600));
});
}
[Test]
public async Task Durchgeschautes_verlaesst_Weiterschauen()
{
Datei("Matrix (1999)/Matrix (1999).mkv");
using var server = new FlaroServer();
var client = await Eingelesen(server);
var film = await ErsterFilm(client);
await Melden(client, film, 600, 7200);
await Melden(client, film, 7000, 7200);
var reihe = await client.GetFromJsonAsync<Page<ContinueEntry>>("/api/v1/home/continue");
Assert.That(reihe!.Items, Is.Empty);
}
/// <summary>Ein zweites Mal angefangen — mit Häkchen und Stelle zugleich.</summary>
[Test]
public async Task Ein_gesehener_Film_kommt_beim_zweiten_Anlauf_zurueck()
{
Datei("Matrix (1999)/Matrix (1999).mkv");
using var server = new FlaroServer();
var client = await Eingelesen(server);
var film = await ErsterFilm(client);
await Melden(client, film, 7000, 7200);
await Melden(client, film, 120, 7200);
var reihe = await client.GetFromJsonAsync<Page<ContinueEntry>>("/api/v1/home/continue");
Assert.Multiple(() =>
{
Assert.That(reihe!.Items, Has.Count.EqualTo(1));
Assert.That(reihe.Items[0].State.Watched, Is.True, "gesehen hat er ihn ja");
Assert.That(reihe.Items[0].State.PositionSeconds, Is.EqualTo(120));
});
}
[Test]
public async Task Weglegen_nimmt_aus_der_Reihe_ohne_gesehen_zu_behaupten()
{
Datei("Matrix (1999)/Matrix (1999).mkv");
using var server = new FlaroServer();
var client = await Eingelesen(server);
var film = await ErsterFilm(client);
await Melden(client, film, 1800, 7200);
(await client.PostAsync($"/api/v1/items/{film}/dismiss", null)).EnsureSuccessStatusCode();
var reihe = await client.GetFromJsonAsync<Page<ContinueEntry>>("/api/v1/home/continue");
var seite = await client.GetFromJsonAsync<Page<MovieResponse>>("/api/v1/movies");
Assert.Multiple(() =>
{
Assert.That(reihe!.Items, Is.Empty);
Assert.That(seite!.Items[0].State!.Watched, Is.False, "weggelegt heisst nicht gesehen");
Assert.That(seite.Items[0].State!.PositionSeconds, Is.EqualTo(1800), "die Stelle bleibt");
});
}
[Test]
public async Task Wer_doch_weiterschaut_holt_es_zurueck()
{
Datei("Matrix (1999)/Matrix (1999).mkv");
using var server = new FlaroServer();
var client = await Eingelesen(server);
var film = await ErsterFilm(client);
await Melden(client, film, 1800, 7200);
await client.PostAsync($"/api/v1/items/{film}/dismiss", null);
await Melden(client, film, 1900, 7200);
var reihe = await client.GetFromJsonAsync<Page<ContinueEntry>>("/api/v1/home/continue");
Assert.That(reihe!.Items, Has.Count.EqualTo(1));
}
[Test]
public async Task Von_Hand_als_gesehen_raeumt_die_Stelle_weg()
{
Datei("Matrix (1999)/Matrix (1999).mkv");
using var server = new FlaroServer();
var client = await Eingelesen(server);
var film = await ErsterFilm(client);
await Melden(client, film, 1800, 7200);
var antwort = await client.PutAsJsonAsync($"/api/v1/items/{film}/watched", new WatchedRequest(true));
var stand = (await antwort.Content.ReadFromJsonAsync<ItemStateResponse>())!;
var reihe = await client.GetFromJsonAsync<Page<ContinueEntry>>("/api/v1/home/continue");
Assert.Multiple(() =>
{
Assert.That(stand.Watched, Is.True);
Assert.That(stand.PositionSeconds, Is.Zero);
Assert.That(reihe!.Items, Is.Empty);
});
}
[Test]
public async Task Favorit_und_Bewertung_bleiben_stehen()
{
Datei("Matrix (1999)/Matrix (1999).mkv");
using var server = new FlaroServer();
var client = await Eingelesen(server);
var film = await ErsterFilm(client);
await client.PutAsJsonAsync($"/api/v1/items/{film}/favourite", new FavouriteRequest(true));
await client.PutAsJsonAsync($"/api/v1/items/{film}/rating", new RatingRequest(9));
var einzeln = await client.GetFromJsonAsync<MovieResponse>($"/api/v1/movies/{film}");
Assert.Multiple(() =>
{
Assert.That(einzeln!.State!.Favourite, Is.True);
Assert.That(einzeln.State!.Rating, Is.EqualTo(9));
});
}
[Test]
public async Task Eine_Bewertung_ausserhalb_von_eins_bis_zehn_wird_abgelehnt()
{
Datei("Matrix (1999)/Matrix (1999).mkv");
using var server = new FlaroServer();
var client = await Eingelesen(server);
var film = await ErsterFilm(client);
var antwort = await client.PutAsJsonAsync($"/api/v1/items/{film}/rating", new RatingRequest(11));
Assert.That(antwort.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest));
}
/// <summary>Zwei Leute im selben Haushalt stehen an verschiedenen Stellen.</summary>
[Test]
public async Task Der_Stand_gehoert_dem_Benutzer_nicht_dem_Server()
{
Datei("Matrix (1999)/Matrix (1999).mkv");
using var server = new FlaroServer();
var admin = await Eingelesen(server);
var film = await ErsterFilm(admin);
(await admin.PostAsJsonAsync("/api/v1/users",
new CreateUserRequest("mitbewohner", "ein-langes-passwort", false))).EnsureSuccessStatusCode();
var zweiter = server.AlsAngemeldeter(
(await server.SignIn("mitbewohner", "ein-langes-passwort", "iPad", "geraet-2")).Token);
await Melden(admin, film, 600, 7200);
await Melden(zweiter, film, 3000, 7200);
var seiteA = await admin.GetFromJsonAsync<Page<MovieResponse>>("/api/v1/movies");
var seiteB = await zweiter.GetFromJsonAsync<Page<MovieResponse>>("/api/v1/movies");
Assert.Multiple(() =>
{
Assert.That(seiteA!.Items[0].State!.PositionSeconds, Is.EqualTo(600));
Assert.That(seiteB!.Items[0].State!.PositionSeconds, Is.EqualTo(3000));
});
}
[Test]
public async Task Einen_Titel_den_es_nicht_gibt_kann_man_nicht_melden()
{
Datei("Matrix (1999)/Matrix (1999).mkv");
using var server = new FlaroServer();
var client = await Eingelesen(server);
var antwort = await Melden(client, Guid.CreateVersion7(), 60, 7200);
Assert.That(antwort.StatusCode, Is.EqualTo(HttpStatusCode.NotFound));
}
/// <summary>Ohne Angabe nimmt der Server die Laufzeit aus der Datei.</summary>
///
/// <remarks>
/// Die Prüfdateien haben keine lesbare Laufzeit — ffprobe findet nichts in
/// zweitausend Nullbytes. Ohne Laufzeit darf nichts vermutet werden, und
/// genau das wird hier festgehalten.
/// </remarks>
[Test]
public async Task Ohne_Laufzeit_wird_nichts_als_gesehen_vermutet()
{
Datei("Matrix (1999)/Matrix (1999).mkv");
using var server = new FlaroServer();
var client = await Eingelesen(server);
var film = await ErsterFilm(client);
var antwort = await Melden(client, film, 99999, null);
var stand = (await antwort.Content.ReadFromJsonAsync<ItemStateResponse>())!;
Assert.That(stand.Watched, Is.False);
}
// --- Serien --------------------------------------------------------
[Test]
public async Task Ungesehene_Folgen_werden_gezaehlt()
{
Datei("Dark/Season 01/Dark - S01E01 - Geheimnisse.mkv");
Datei("Dark/Season 01/Dark - S01E02 - Luegen.mkv");
Datei("Dark/Season 01/Dark - S01E03 - Gestern und heute.mkv");
using var server = new FlaroServer();
var client = await Eingelesen(server, "Shows");
var serien = await client.GetFromJsonAsync<Page<SeriesResponse>>("/api/v1/series");
var serie = serien!.Items[0];
Assert.That(serie.UnwatchedCount, Is.EqualTo(3));
var folgen = await client.GetFromJsonAsync<Page<EpisodeResponse>>($"/api/v1/series/{serie.Id}/episodes");
await client.PutAsJsonAsync($"/api/v1/items/{folgen!.Items[0].Id}/watched", new WatchedRequest(true));
var danach = await client.GetFromJsonAsync<SeriesResponse>($"/api/v1/series/{serie.Id}");
Assert.That(danach!.UnwatchedCount, Is.EqualTo(2));
}
/// <summary>Eine Serie als gesehen markieren hakt jede Folge ab.</summary>
///
/// <remarks>
/// <b>Der Fall, der vorher stumm scheiterte.</b> Der Server kannte zu
/// einer Serienkennung keinen Zustand und antwortete mit 404; die
/// Oberfläche verschluckte die Ablehnung, und der Klick tat sichtbar
/// nichts.
/// </remarks>
[Test]
public async Task Eine_Serie_als_gesehen_markieren_hakt_alle_Folgen_ab()
{
Datei("Dark/Season 01/Dark - S01E01 - Geheimnisse.mkv");
Datei("Dark/Season 01/Dark - S01E02 - Luegen.mkv");
Datei("Dark/Season 02/Dark - S02E01 - Beginnings.mkv");
using var server = new FlaroServer();
var client = await Eingelesen(server, "Shows");
var serien = await client.GetFromJsonAsync<Page<SeriesResponse>>("/api/v1/series");
var serie = serien!.Items[0];
var antwort = await client.PutAsJsonAsync(
$"/api/v1/items/{serie.Id}/watched", new WatchedRequest(true));
Assert.That(antwort.StatusCode, Is.EqualTo(HttpStatusCode.OK),
"eine Serienkennung ist eine gültige Titelkennung");
var danach = await client.GetFromJsonAsync<SeriesResponse>($"/api/v1/series/{serie.Id}");
var folgen = await client.GetFromJsonAsync<Page<EpisodeResponse>>(
$"/api/v1/series/{serie.Id}/episodes");
Assert.Multiple(() =>
{
Assert.That(danach!.UnwatchedCount, Is.Zero, "keine Folge steht mehr aus");
Assert.That(danach.State?.Watched, Is.True, "und die Serie selbst trägt das Häkchen");
Assert.That(folgen!.Items.Select(f => f.State?.Watched), Is.All.True,
"jede einzelne Folge ist abgehakt, nicht nur die Serie");
});
}
/// <summary>Und wieder zurück — ohne Rest.</summary>
[Test]
public async Task Eine_Serie_als_ungesehen_markieren_nimmt_jedes_Haekchen_weg()
{
Datei("Dark/Season 01/Dark - S01E01 - Geheimnisse.mkv");
Datei("Dark/Season 01/Dark - S01E02 - Luegen.mkv");
using var server = new FlaroServer();
var client = await Eingelesen(server, "Shows");
var serien = await client.GetFromJsonAsync<Page<SeriesResponse>>("/api/v1/series");
var serie = serien!.Items[0];
await client.PutAsJsonAsync($"/api/v1/items/{serie.Id}/watched", new WatchedRequest(true));
await client.PutAsJsonAsync($"/api/v1/items/{serie.Id}/watched", new WatchedRequest(false));
var danach = await client.GetFromJsonAsync<SeriesResponse>($"/api/v1/series/{serie.Id}");
var folgen = await client.GetFromJsonAsync<Page<EpisodeResponse>>(
$"/api/v1/series/{serie.Id}/episodes");
Assert.Multiple(() =>
{
Assert.That(danach!.UnwatchedCount, Is.EqualTo(2));
Assert.That(danach.State?.Watched, Is.False);
Assert.That(folgen!.Items.Select(f => f.State?.Watched), Is.All.False);
});
}
/// <summary>Favorit an einer Serie — vorher ebenfalls eine stille 404.</summary>
[Test]
public async Task Eine_Serie_laesst_sich_zu_den_Favoriten_legen()
{
Datei("Dark/Season 01/Dark - S01E01 - Geheimnisse.mkv");
using var server = new FlaroServer();
var client = await Eingelesen(server, "Shows");
var serien = await client.GetFromJsonAsync<Page<SeriesResponse>>("/api/v1/series");
var serie = serien!.Items[0];
var antwort = await client.PutAsJsonAsync(
$"/api/v1/items/{serie.Id}/favourite", new FavouriteRequest(true));
antwort.EnsureSuccessStatusCode();
var liste = await client.GetFromJsonAsync<Page<SeriesResponse>>("/api/v1/series");
Assert.That(liste!.Items[0].State?.Favourite, Is.True,
"auch die Liste führt den Zustand mit — sonst wüsste die Kachel es nicht");
}
/// <summary>Der Stand einer Serie gehört dem Benutzer, nicht dem Server.</summary>
[Test]
public async Task Der_Serienstand_gehoert_dem_Benutzer()
{
Datei("Dark/Season 01/Dark - S01E01 - Geheimnisse.mkv");
using var server = new FlaroServer();
var ich = await Eingelesen(server, "Shows");
var serien = await ich.GetFromJsonAsync<Page<SeriesResponse>>("/api/v1/series");
var serie = serien!.Items[0];
await ich.PutAsJsonAsync($"/api/v1/items/{serie.Id}/watched", new WatchedRequest(true));
(await ich.PostAsJsonAsync("/api/v1/users",
new CreateUserRequest("mitbewohner", "ein-langes-passwort", false))).EnsureSuccessStatusCode();
var andere = server.AlsAngemeldeter(
(await server.SignIn("mitbewohner", "ein-langes-passwort", "iPad", "geraet-2")).Token);
var seine = await andere.GetFromJsonAsync<SeriesResponse>($"/api/v1/series/{serie.Id}");
Assert.Multiple(() =>
{
Assert.That(seine!.State?.Watched, Is.Not.True);
Assert.That(seine.UnwatchedCount, Is.EqualTo(1));
});
}
/// <summary>Viele auf einmal — eine Anfrage, nicht zweihundert.</summary>
[Test]
public async Task Eine_Sammelmarkierung_hakt_alles_Gewaehlte_ab()
{
Datei("Matrix (1999)/Matrix (1999).mkv");
Datei("Alien (1979)/Alien (1979).mkv");
Datei("Heat (1995)/Heat (1995).mkv");
using var server = new FlaroServer();
var client = await Eingelesen(server);
var seite = await client.GetFromJsonAsync<Page<MovieResponse>>("/api/v1/movies");
var zwei = seite!.Items.Take(2).Select(m => m.Id).ToList();
var antwort = await client.PutAsJsonAsync("/api/v1/items/watched",
new BulkWatchedRequest(zwei, true));
var ergebnis = await antwort.Content.ReadFromJsonAsync<BulkWatchedResponse>();
var danach = await client.GetFromJsonAsync<Page<MovieResponse>>("/api/v1/movies");
Assert.Multiple(() =>
{
Assert.That(ergebnis!.Changed, Is.EqualTo(2));
Assert.That(ergebnis.Unknown, Is.Empty);
Assert.That(danach!.Items.Count(m => m.State?.Watched == true), Is.EqualTo(2),
"genau die zwei gewählten, nicht der dritte");
});
}
/// <summary>Was es nicht gibt, wird benannt statt verschwiegen.</summary>
[Test]
public async Task Eine_Sammelmarkierung_nennt_was_sie_nicht_gefunden_hat()
{
Datei("Matrix (1999)/Matrix (1999).mkv");
using var server = new FlaroServer();
var client = await Eingelesen(server);
var film = await ErsterFilm(client);
var erfunden = Guid.CreateVersion7();
var antwort = await client.PutAsJsonAsync("/api/v1/items/watched",
new BulkWatchedRequest([film, erfunden], true));
var ergebnis = await antwort.Content.ReadFromJsonAsync<BulkWatchedResponse>();
Assert.Multiple(() =>
{
Assert.That(ergebnis!.Changed, Is.EqualTo(1));
Assert.That(ergebnis.Unknown, Is.EqualTo(new[] { erfunden }));
});
}
/// <summary>Auch Serien gehören in eine Sammelmarkierung.</summary>
[Test]
public async Task Eine_Sammelmarkierung_zieht_die_Folgen_einer_Serie_mit()
{
Datei("Dark/Season 01/Dark - S01E01 - Geheimnisse.mkv");
Datei("Dark/Season 01/Dark - S01E02 - Luegen.mkv");
using var server = new FlaroServer();
var client = await Eingelesen(server, "Shows");
var serien = await client.GetFromJsonAsync<Page<SeriesResponse>>("/api/v1/series");
var serie = serien!.Items[0];
await client.PutAsJsonAsync("/api/v1/items/watched",
new BulkWatchedRequest([serie.Id], true));
var danach = await client.GetFromJsonAsync<SeriesResponse>($"/api/v1/series/{serie.Id}");
Assert.That(danach!.UnwatchedCount, Is.Zero);
}
[Test]
public async Task Eine_leere_Sammelmarkierung_aendert_nichts_und_scheitert_nicht()
{
Datei("Matrix (1999)/Matrix (1999).mkv");
using var server = new FlaroServer();
var client = await Eingelesen(server);
var antwort = await client.PutAsJsonAsync("/api/v1/items/watched",
new BulkWatchedRequest([], true));
var ergebnis = await antwort.Content.ReadFromJsonAsync<BulkWatchedResponse>();
Assert.Multiple(() =>
{
Assert.That(antwort.StatusCode, Is.EqualTo(HttpStatusCode.OK));
Assert.That(ergebnis!.Changed, Is.Zero);
});
}
[Test]
public async Task Die_naechste_Folge_richtet_sich_nach_Staffel_und_Nummer()
{
Datei("Dark/Season 01/Dark - S01E01 - Geheimnisse.mkv");
Datei("Dark/Season 01/Dark - S01E02 - Luegen.mkv");
using var server = new FlaroServer();
var client = await Eingelesen(server, "Shows");
var serien = await client.GetFromJsonAsync<Page<SeriesResponse>>("/api/v1/series");
var folgen = await client.GetFromJsonAsync<Page<EpisodeResponse>>(
$"/api/v1/series/{serien!.Items[0].Id}/episodes");
var naechste = await client.GetFromJsonAsync<EpisodeResponse>(
$"/api/v1/episodes/{folgen!.Items[0].Id}/next");
var hinterDerLetzten = await client.GetAsync($"/api/v1/episodes/{folgen.Items[1].Id}/next");
Assert.Multiple(() =>
{
Assert.That(naechste!.Episode, Is.EqualTo(2));
Assert.That(hinterDerLetzten.StatusCode, Is.EqualTo(HttpStatusCode.NoContent),
"nach der letzten Folge kommt keine — das ist kein Fehler");
});
}
[Test]
public async Task Die_erste_ungesehene_Folge_steht_unter_Naechste_Folge()
{
Datei("Dark/Season 01/Dark - S01E01 - Geheimnisse.mkv");
Datei("Dark/Season 01/Dark - S01E02 - Luegen.mkv");
using var server = new FlaroServer();
var client = await Eingelesen(server, "Shows");
var serien = await client.GetFromJsonAsync<Page<SeriesResponse>>("/api/v1/series");
var folgen = await client.GetFromJsonAsync<Page<EpisodeResponse>>(
$"/api/v1/series/{serien!.Items[0].Id}/episodes");
await client.PutAsJsonAsync($"/api/v1/items/{folgen!.Items[0].Id}/watched", new WatchedRequest(true));
var reihe = await client.GetFromJsonAsync<Page<ContinueEntry>>("/api/v1/home/next");
Assert.Multiple(() =>
{
Assert.That(reihe!.Items, Has.Count.EqualTo(1));
Assert.That(reihe.Items[0].Episode, Is.EqualTo(2));
Assert.That(reihe.Items[0].Title, Is.EqualTo("Dark"), "oben die Serie");
});
}
/// <summary>Was in „Weiterschauen" steht, steht nicht nochmal daneben.</summary>
[Test]
public async Task Eine_angefangene_Folge_erscheint_nur_in_einer_Reihe()
{
Datei("Dark/Season 01/Dark - S01E01 - Geheimnisse.mkv");
Datei("Dark/Season 01/Dark - S01E02 - Luegen.mkv");
using var server = new FlaroServer();
var client = await Eingelesen(server, "Shows");
var serien = await client.GetFromJsonAsync<Page<SeriesResponse>>("/api/v1/series");
var folgen = await client.GetFromJsonAsync<Page<EpisodeResponse>>(
$"/api/v1/series/{serien!.Items[0].Id}/episodes");
await Melden(client, folgen!.Items[0].Id, 400, 2700);
var weiter = await client.GetFromJsonAsync<Page<ContinueEntry>>("/api/v1/home/continue");
var naechste = await client.GetFromJsonAsync<Page<ContinueEntry>>("/api/v1/home/next");
Assert.Multiple(() =>
{
Assert.That(weiter!.Items, Has.Count.EqualTo(1));
Assert.That(weiter.Items[0].Episode, Is.EqualTo(1));
Assert.That(naechste!.Items, Is.Empty);
});
}
/// <summary>Die Spalte und die Regel dürfen nicht auseinanderlaufen.</summary>
///
/// <remarks>
/// <c>InProgress</c> ist das festgehaltene Ergebnis von
/// <see cref="ProgressRule.BelongsInContinueWatching"/>. Wer die Regel
/// ändert und das Nachführen vergisst, bekommt eine Startseite, die lügt —
/// und zwar lautlos. Deshalb hier gegeneinandergehalten.
/// </remarks>
[Test]
public async Task Die_Reihe_zeigt_genau_das_was_die_Regel_sagt()
{
Datei("Matrix (1999)/Matrix (1999).mkv");
using var server = new FlaroServer();
var client = await Eingelesen(server);
var film = await ErsterFilm(client);
foreach (var (stelle, laenge) in new (double, double?)[]
{ (1, 7200), (300, 7200), (7000, 7200), (120, 7200) })
{
await Melden(client, film, stelle, laenge);
var erwartet = ProgressRule.BelongsInContinueWatching(new UserItemState
{
UserId = Guid.Empty,
MovieId = film,
Position = TimeSpan.FromSeconds(stelle),
Runtime = laenge is { } l ? TimeSpan.FromSeconds(l) : null,
});
var reihe = await client.GetFromJsonAsync<Page<ContinueEntry>>("/api/v1/home/continue");
Assert.That(reihe!.Items.Count == 1, Is.EqualTo(erwartet),
$"bei {stelle}s von {laenge}s");
}
}
}