„Nächste Folge" führte ins Leere, und es lag weder am Scan noch am Auffrischen: die Kachel trug die Folgenkennung und öffnete damit eine Serienseite — die es unter dieser Kennung nie gibt. Heraus kam ausgerechnet dort „Diese Serie gibt es nicht mehr", wo man gerade weiterschauen wollte. Der Server kannte die Serie an beiden Stellen und schickte sie nur nicht mit. Wohin die Kachel führt und was sie misst, ist jetzt getrennt. Beides auf dieselbe Kennung zu legen wäre der nächste Fehler gewesen: „Als gesehen markieren" hätte die ganze Serie abgehakt statt der einen Folge, die man zur Hälfte gesehen hat. Dazu Streichen wie im Dateimanager: gedrückt halten und darüberfahren wählt aus, rückwärts über Gewähltes nimmt wieder weg. Ein kurzer Klick bleibt, was er war. Abgetastet wird die Strecke zwischen zwei Zeigerereignissen, nicht nur deren Endpunkte — gemessen: ein Zug über vier Kacheln erwischte sonst die erste und die letzte. Plakate lassen sich nicht mehr durch die Gegend schleppen. „Abwählen" ist ein Kreuz geworden; als vierter Knopf neben drei Aktionen sah es aus, als täte es etwas mit den Titeln. In der kleinen Leiste lag das Vorschaubild über dem Fortschrittsstrich und verdeckte ihn rechts. Und ein Klick genau darauf tat nichts, obwohl das die Stelle ist, auf die man zeigt — jetzt holt auch das Bild den Film zurück. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
751 lines
30 KiB
C#
751 lines
30 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);
|
|
});
|
|
}
|
|
|
|
/// <summary>Eine Folge in „Weiterschauen" nennt ihre Serie.</summary>
|
|
///
|
|
/// <remarks>
|
|
/// <b>Ohne diese Angabe führte die Kachel ins Leere.</b> Sie trug die
|
|
/// Folgenkennung und öffnete damit eine Serienseite — die es unter dieser
|
|
/// Kennung nie gibt. Heraus kam „Diese Serie gibt es nicht mehr",
|
|
/// ausgerechnet unter „Nächste Folge", wo man gerade weiterschauen wollte.
|
|
/// Es lag nicht am Scan und nicht am Auffrischen: die Kachel hat von
|
|
/// Anfang an auf die falsche Kennung gezeigt.
|
|
/// </remarks>
|
|
[Test]
|
|
public async Task Eine_angefangene_Folge_nennt_ihre_Serie()
|
|
{
|
|
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];
|
|
var folgen = await client.GetFromJsonAsync<Page<EpisodeResponse>>(
|
|
$"/api/v1/series/{serie.Id}/episodes");
|
|
|
|
// Mittendrin stehenbleiben — dann steht die Folge in „Weiterschauen".
|
|
await Melden(client, folgen!.Items[0].Id, 600, 7200);
|
|
|
|
var weiter = await client.GetFromJsonAsync<Page<ContinueEntry>>("/api/v1/home/continue");
|
|
var eintrag = weiter!.Items.Single();
|
|
|
|
Assert.Multiple(() =>
|
|
{
|
|
Assert.That(eintrag.Id, Is.EqualTo(folgen.Items[0].Id), "die Kennung bleibt die der Folge");
|
|
Assert.That(eintrag.SeriesId, Is.EqualTo(serie.Id), "und daneben steht, wohin sie gehört");
|
|
});
|
|
}
|
|
|
|
/// <summary>Dasselbe für „Nächste Folge".</summary>
|
|
[Test]
|
|
public async Task Die_naechste_Folge_nennt_ihre_Serie()
|
|
{
|
|
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];
|
|
var folgen = await client.GetFromJsonAsync<Page<EpisodeResponse>>(
|
|
$"/api/v1/series/{serie.Id}/episodes");
|
|
|
|
// Die erste ganz gesehen — dann ist die zweite „die nächste".
|
|
await client.PutAsJsonAsync($"/api/v1/items/{folgen!.Items[0].Id}/watched", new WatchedRequest(true));
|
|
|
|
var naechste = await client.GetFromJsonAsync<Page<ContinueEntry>>("/api/v1/home/next");
|
|
var eintrag = naechste!.Items.Single();
|
|
|
|
Assert.Multiple(() =>
|
|
{
|
|
Assert.That(eintrag.Id, Is.EqualTo(folgen.Items[1].Id));
|
|
Assert.That(eintrag.SeriesId, Is.EqualTo(serie.Id));
|
|
});
|
|
}
|
|
|
|
/// <summary>Ein Film hat keine Serie — und behauptet es auch nicht.</summary>
|
|
[Test]
|
|
public async Task Ein_angefangener_Film_nennt_keine_Serie()
|
|
{
|
|
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 weiter = await client.GetFromJsonAsync<Page<ContinueEntry>>("/api/v1/home/continue");
|
|
Assert.That(weiter!.Items.Single().SeriesId, Is.Null);
|
|
}
|
|
|
|
[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");
|
|
}
|
|
}
|
|
}
|