Wer etwas sieht, kann es jetzt auch sagen
Wer einen Server für andere betreibt, erfährt von Fehlern zuletzt: dass die deutsche Tonspur nach einer halben Stunde aussetzt, merkt der, der den Film sieht, und nicht der, der ihn eingeräumt hat. Bisher gab es dafür keinen Weg ausser dem Zuruf im Wohnzimmer. Gemeldet wird an einem Film, einer Serie, einer einzelnen Staffel und einer einzelnen Folge: Art (Bild, Ton, Untertitel, Angaben, Sonstiges), eine freiwillige Stelle wie 0:34:12 und der Kommentar. Pflicht ist nur der Kommentar — die beiden anderen machen aus einer Liste von Sätzen etwas, das man sortieren und nachstellen kann. **Eine Staffel gibt es als Zeile nicht**, und das prägt den Entwurf. Sie ist Serie plus Nummer, so wie überall sonst in diesem Server; die Meldung trägt deshalb drei Fremdschlüssel und daneben eine Staffelnummer. Dass die Nummer auf eine Staffel zeigt, die es gibt, prüft der Endpunkt beim Schreiben — die Datenbank kann es nicht, es gibt nichts, worauf sie zeigen könnte. **Die Rechteprüfung steht nirgends.** Wer einen Titel nicht sehen darf, findet ihn beim Melden nicht — das erledigt der Freigabefilter, ohne ein einziges `Where`. Elegant und genau deshalb leicht zu zerstören, darum steht dafür ein Test da. **Die eigene Liste überlebt fremde Entscheidungen.** Titel werden nachgeschlagen, nicht verbunden: zieht ein Administrator einen Titel zurück, nachdem jemand ihn gemeldet hat, bleibt die Meldung beim Melder stehen — nur ohne Namen. Ein Join hätte sie verschwinden lassen, und das wäre jemandes eigene Eingabe, weggenommen wegen einer fremden Entscheidung. `IgnoreQueryFilters` wäre die bequeme Abkürzung und gäbe den Namen eines unveröffentlichten Titels über einen Weg heraus, an den beim nächsten Mal niemand denkt. **Erledigt heisst abgehakt, nicht gelöscht**, als Zeitpunkt und nicht als Haken — wie `PublishAt` und `ChangedSince`. Daneben steht „Wieder öffnen"; ein Fehlgriff wäre sonst endgültig. Das Archiv wird erst auf Zuruf geholt: es wächst nur, und für den seltenen Blick dauernd zu zahlen lohnt nicht. **Der Abschnitt steht neben der Prüfliste, nicht darin.** Die versteckt sich, sobald nichts auf Freigabe wartet — das ist der Normalfall, und die Meldungen verschwänden mit ihr. Umgekehrt bleibt der Meldungsabschnitt stehen, auch wenn nichts drinsteht: ein Kanal, der sich versteckt, solange ihn niemand benutzt hat, bringt niemandem bei, dass es ihn gibt. Die Zahl an der Leiste holt sich nur, wer sie nicht ohnehin hat: steht „Zu prüfen" offen, meldet der Abschnitt sie selbst und nach jedem Abhaken. Nebenbei zwei Dinge am Dialog, die hier auffielen. Es gab **kein mehrzeiliges Feld** — und das Einsammeln der Werte hätte ein `<textarea>` still verschluckt: es ist kein `HTMLInputElement` und wäre ins Auffangnetz für Auswahlfelder gelaufen, also als `undefined` hinausgegangen. Der Server hätte „ohne Beschreibung" geantwortet, auf einem Dialog, in dem sichtbar etwas steht. Und `dialog()` meldete sich als einziger nicht beim Seitenwechsel an: ein Dialog von der Detailseite überlebte den Zurück-Knopf und stand danach über der Mediathek. Neun Testfälle auf dem Server, zwölf Läufe. Für den Web-Client gibt es in diesem Projekt keine Tests; dort ist von Hand geprüft. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
02978e482c
commit
7cd48f5528
@@ -0,0 +1,360 @@
|
||||
using System.Net;
|
||||
using System.Net.Http.Json;
|
||||
using Flaro.Api.Endpoints;
|
||||
|
||||
namespace Flaro.Api.Tests;
|
||||
|
||||
/// <summary>Melden darf jeder, lesen nur die Verwaltung — und die eigenen sieht man selbst.</summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// <b>Der Test, der hier wirklich zählt, ist der mit dem ungeprüften Titel.</b>
|
||||
/// Die Rechteprüfung beim Melden steht nirgends als `Where` — sie fällt aus dem
|
||||
/// Freigabefilter heraus. Das ist elegant und genau deshalb leicht zu
|
||||
/// zerstören: wer in den Endpunkt einmal `IgnoreQueryFilters` schreibt, um
|
||||
/// „auch zurückgezogene Titel zu finden", hat sie weggenommen, ohne dass eine
|
||||
/// Zeile danach aussieht.
|
||||
/// </remarks>
|
||||
public class MeldungTests
|
||||
{
|
||||
private string _bestand = "";
|
||||
|
||||
[SetUp]
|
||||
public void Anlegen() => _bestand = Directory.CreateDirectory(
|
||||
Path.Combine(Path.GetTempPath(), "flaro-meldung-" + 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]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Eine_Meldung_zu_einem_Film_landet_beim_Administrator()
|
||||
{
|
||||
Datei("Heat (1995).mkv");
|
||||
|
||||
using var server = new FlaroServer();
|
||||
var alsAdmin = await Aufsetzen(server, "Filme", "Movies");
|
||||
var gast = await server.NeuerBenutzer(alsAdmin, "gast", "gastpasswortlang");
|
||||
|
||||
var film = (await Filme(gast)).Single();
|
||||
|
||||
var gemeldet = await gast.PostAsJsonAsync($"/api/v1/items/{film.Id}/reports",
|
||||
new MeldungRequest("ton", "Der deutsche Ton setzt aus.", "1:02:03"));
|
||||
|
||||
var liste = await alsAdmin.GetFromJsonAsync<List<Meldungseintrag>>("/api/v1/reports");
|
||||
|
||||
Assert.That(gemeldet.StatusCode, Is.EqualTo(HttpStatusCode.OK));
|
||||
Assert.That(liste, Has.Count.EqualTo(1));
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(liste![0].Target, Is.EqualTo("movie"));
|
||||
Assert.That(liste[0].ItemId, Is.EqualTo(film.Id));
|
||||
Assert.That(liste[0].Title, Is.EqualTo("Heat"));
|
||||
Assert.That(liste[0].Kind, Is.EqualTo("ton"));
|
||||
Assert.That(liste[0].PositionSeconds, Is.EqualTo(3723));
|
||||
Assert.That(liste[0].ReportedBy, Is.EqualTo("gast"));
|
||||
Assert.That(liste[0].ResolvedAt, Is.Null);
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Melden_darf_jeder_die_Verwaltung_bleibt_zu()
|
||||
{
|
||||
Datei("Heat (1995).mkv");
|
||||
|
||||
using var server = new FlaroServer();
|
||||
var alsAdmin = await Aufsetzen(server, "Filme", "Movies");
|
||||
var gast = await server.NeuerBenutzer(alsAdmin, "gast", "gastpasswortlang");
|
||||
var film = (await Filme(gast)).Single();
|
||||
|
||||
var gemeldet = await gast.PostAsJsonAsync($"/api/v1/items/{film.Id}/reports",
|
||||
new MeldungRequest("bild", "Das Plakat gehört zu einem anderen Film."));
|
||||
var meine = await gast.GetAsync("/api/v1/reports/mine");
|
||||
var fremd = await gast.GetAsync("/api/v1/reports");
|
||||
|
||||
var id = (await alsAdmin.GetFromJsonAsync<List<Meldungseintrag>>("/api/v1/reports"))![0].Id;
|
||||
var abhaken = await gast.PostAsync($"/api/v1/reports/{id}/resolve", null);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(gemeldet.StatusCode, Is.EqualTo(HttpStatusCode.OK));
|
||||
Assert.That(meine.StatusCode, Is.EqualTo(HttpStatusCode.OK));
|
||||
Assert.That(fremd.StatusCode, Is.EqualTo(HttpStatusCode.Forbidden),
|
||||
"Die Liste aller Meldungen ist Verwaltung.");
|
||||
Assert.That(abhaken.StatusCode, Is.EqualTo(HttpStatusCode.Forbidden));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Jeder_sieht_nur_die_eigenen_Meldungen()
|
||||
{
|
||||
Datei("Heat (1995).mkv");
|
||||
|
||||
using var server = new FlaroServer();
|
||||
var alsAdmin = await Aufsetzen(server, "Filme", "Movies");
|
||||
var eine = await server.NeuerBenutzer(alsAdmin, "eine", "einepasswortlang");
|
||||
var andere = await server.NeuerBenutzer(alsAdmin, "andere", "anderepasswortlang");
|
||||
var film = (await Filme(eine)).Single();
|
||||
|
||||
await eine.PostAsJsonAsync($"/api/v1/items/{film.Id}/reports",
|
||||
new MeldungRequest("ton", "Von mir."));
|
||||
await andere.PostAsJsonAsync($"/api/v1/items/{film.Id}/reports",
|
||||
new MeldungRequest("bild", "Von jemand anderem."));
|
||||
|
||||
var meine = await eine.GetFromJsonAsync<List<Meldungseintrag>>("/api/v1/reports/mine");
|
||||
var alle = await alsAdmin.GetFromJsonAsync<List<Meldungseintrag>>("/api/v1/reports");
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(meine, Has.Count.EqualTo(1));
|
||||
Assert.That(meine![0].Comment, Is.EqualTo("Von mir."));
|
||||
Assert.That(alle, Has.Count.EqualTo(2), "Der Administrator sieht beide.");
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Erledigt_heisst_abgehakt_und_nicht_geloescht()
|
||||
{
|
||||
Datei("Heat (1995).mkv");
|
||||
|
||||
using var server = new FlaroServer();
|
||||
var alsAdmin = await Aufsetzen(server, "Filme", "Movies");
|
||||
var gast = await server.NeuerBenutzer(alsAdmin, "gast", "gastpasswortlang");
|
||||
var film = (await Filme(gast)).Single();
|
||||
|
||||
await gast.PostAsJsonAsync($"/api/v1/items/{film.Id}/reports",
|
||||
new MeldungRequest("untertitel", "Die deutsche Spur ist leer."));
|
||||
var id = (await alsAdmin.GetFromJsonAsync<List<Meldungseintrag>>("/api/v1/reports"))![0].Id;
|
||||
|
||||
(await alsAdmin.PostAsync($"/api/v1/reports/{id}/resolve", null)).EnsureSuccessStatusCode();
|
||||
|
||||
var offen = await alsAdmin.GetFromJsonAsync<List<Meldungseintrag>>("/api/v1/reports");
|
||||
var erledigt = await alsAdmin.GetFromJsonAsync<List<Meldungseintrag>>("/api/v1/reports?erledigt=true");
|
||||
var beimMelder = await gast.GetFromJsonAsync<List<Meldungseintrag>>("/api/v1/reports/mine");
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(offen, Is.Empty);
|
||||
Assert.That(erledigt, Has.Count.EqualTo(1));
|
||||
Assert.That(erledigt![0].ResolvedAt, Is.Not.Null);
|
||||
Assert.That(beimMelder, Has.Count.EqualTo(1),
|
||||
"Der Melder sieht seine Meldung weiterhin — mitsamt dem Stand.");
|
||||
Assert.That(beimMelder![0].ResolvedAt, Is.Not.Null);
|
||||
});
|
||||
|
||||
// Ein Fehlgriff beim Abhaken darf kein Dauerzustand sein.
|
||||
(await alsAdmin.PostAsync($"/api/v1/reports/{id}/reopen", null)).EnsureSuccessStatusCode();
|
||||
var wiederOffen = await alsAdmin.GetFromJsonAsync<List<Meldungseintrag>>("/api/v1/reports");
|
||||
Assert.That(wiederOffen, Has.Count.EqualTo(1));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Was_man_nicht_sieht_kann_man_nicht_melden()
|
||||
{
|
||||
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<LibraryResponse>())!;
|
||||
await Scannen(alsAdmin, bibliothek.Id);
|
||||
|
||||
var wartend = await alsAdmin.GetFromJsonAsync<List<Pruefeintrag>>("/api/v1/review");
|
||||
var id = wartend![0].Id;
|
||||
|
||||
var gast = await server.NeuerBenutzer(alsAdmin, "gast", "gastpasswortlang");
|
||||
var abgelehnt = await gast.PostAsJsonAsync($"/api/v1/items/{id}/reports",
|
||||
new MeldungRequest("bild", "Sieht komisch aus."));
|
||||
var vomAdmin = await alsAdmin.PostAsJsonAsync($"/api/v1/items/{id}/reports",
|
||||
new MeldungRequest("bild", "Sieht komisch aus."));
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(abgelehnt.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest));
|
||||
Assert.That(vomAdmin.StatusCode, Is.EqualTo(HttpStatusCode.OK),
|
||||
"Der Administrator sieht den Titel und darf ihn deshalb melden.");
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Eine_Staffel_wird_ueber_Serie_und_Nummer_gemeldet()
|
||||
{
|
||||
Datei("Dark/Season 01/Dark - S01E01.mkv");
|
||||
Datei("Dark/Season 02/Dark - S02E01.mkv");
|
||||
|
||||
using var server = new FlaroServer();
|
||||
var alsAdmin = await Aufsetzen(server, "Serien", "Shows");
|
||||
var gast = await server.NeuerBenutzer(alsAdmin, "gast", "gastpasswortlang");
|
||||
|
||||
var serien = await gast.GetFromJsonAsync<Page<SeriesResponse>>("/api/v1/series");
|
||||
var serie = serien!.Items.Single();
|
||||
|
||||
var gut = await gast.PostAsJsonAsync($"/api/v1/items/{serie.Id}/reports",
|
||||
new MeldungRequest("ton", "Ab Folge zwei kein Ton.", null, SeasonNumber: 2));
|
||||
var falscheStaffel = await gast.PostAsJsonAsync($"/api/v1/items/{serie.Id}/reports",
|
||||
new MeldungRequest("ton", "Gibt es nicht.", null, SeasonNumber: 9));
|
||||
|
||||
var liste = await alsAdmin.GetFromJsonAsync<List<Meldungseintrag>>("/api/v1/reports");
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(gut.StatusCode, Is.EqualTo(HttpStatusCode.OK));
|
||||
Assert.That(falscheStaffel.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest));
|
||||
Assert.That(liste, Has.Count.EqualTo(1));
|
||||
Assert.That(liste![0].Target, Is.EqualTo("season"));
|
||||
Assert.That(liste[0].ItemId, Is.EqualTo(serie.Id));
|
||||
Assert.That(liste[0].SubTitle, Is.EqualTo("Staffel 2"));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Eine_Folge_meldet_sich_unter_ihrer_eigenen_Kennung()
|
||||
{
|
||||
Datei("Dark/Season 01/Dark - S01E01.mkv");
|
||||
|
||||
using var server = new FlaroServer();
|
||||
var alsAdmin = await Aufsetzen(server, "Serien", "Shows");
|
||||
var gast = await server.NeuerBenutzer(alsAdmin, "gast", "gastpasswortlang");
|
||||
|
||||
var serie = (await gast.GetFromJsonAsync<Page<SeriesResponse>>("/api/v1/series"))!.Items.Single();
|
||||
var folgen = await gast.GetFromJsonAsync<Page<EpisodeResponse>>(
|
||||
$"/api/v1/series/{serie.Id}/episodes");
|
||||
var folge = folgen!.Items.Single();
|
||||
|
||||
await gast.PostAsJsonAsync($"/api/v1/items/{folge.Id}/reports",
|
||||
new MeldungRequest("bild", "Bei 0:34 zwei Minuten schwarz.", "0:34:00"));
|
||||
|
||||
var liste = await alsAdmin.GetFromJsonAsync<List<Meldungseintrag>>("/api/v1/reports");
|
||||
|
||||
Assert.That(liste, Has.Count.EqualTo(1));
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(liste![0].Target, Is.EqualTo("episode"));
|
||||
Assert.That(liste[0].ItemId, Is.EqualTo(serie.Id),
|
||||
"Die Detailseite gehört der Serie, nicht der Folge.");
|
||||
Assert.That(liste[0].SubTitle, Does.StartWith("S1 · F1"));
|
||||
Assert.That(liste[0].PositionSeconds, Is.EqualTo(2040));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Ohne_Beschreibung_gibt_es_nichts_nachzusehen()
|
||||
{
|
||||
Datei("Heat (1995).mkv");
|
||||
|
||||
using var server = new FlaroServer();
|
||||
var alsAdmin = await Aufsetzen(server, "Filme", "Movies");
|
||||
var gast = await server.NeuerBenutzer(alsAdmin, "gast", "gastpasswortlang");
|
||||
var film = (await Filme(gast)).Single();
|
||||
|
||||
var leer = await gast.PostAsJsonAsync($"/api/v1/items/{film.Id}/reports",
|
||||
new MeldungRequest("ton", " "));
|
||||
var zuLang = await gast.PostAsJsonAsync($"/api/v1/items/{film.Id}/reports",
|
||||
new MeldungRequest("ton", new string('a', 2001)));
|
||||
var unbekannt = await gast.PostAsJsonAsync($"/api/v1/items/{film.Id}/reports",
|
||||
new MeldungRequest("geruch", "Riecht komisch."));
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(leer.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest));
|
||||
Assert.That(zuLang.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest));
|
||||
Assert.That(unbekannt.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest));
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>„2:03" sind zwei Minuten, nicht zwei Stunden.</summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// <c>TimeSpan.Parse</c> läse es als zwei Stunden und drei Minuten. Wer
|
||||
/// neben einem Film „2:03" schreibt, meint das, was unter dem
|
||||
/// Fortschrittsbalken steht.
|
||||
/// </remarks>
|
||||
[TestCase("1:02:03", 3723)]
|
||||
[TestCase("2:03", 123)]
|
||||
[TestCase("90", 90)]
|
||||
public async Task Die_Stelle_kommt_als_Zeit_zurueck(string geschrieben, double sekunden)
|
||||
{
|
||||
Datei("Heat (1995).mkv");
|
||||
|
||||
using var server = new FlaroServer();
|
||||
var alsAdmin = await Aufsetzen(server, "Filme", "Movies");
|
||||
var film = (await Filme(alsAdmin)).Single();
|
||||
|
||||
await alsAdmin.PostAsJsonAsync($"/api/v1/items/{film.Id}/reports",
|
||||
new MeldungRequest("bild", "Hier.", geschrieben));
|
||||
var liste = await alsAdmin.GetFromJsonAsync<List<Meldungseintrag>>("/api/v1/reports");
|
||||
|
||||
Assert.That(liste![0].PositionSeconds, Is.EqualTo(sekunden));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Eine_unleserliche_Stelle_wird_abgewiesen()
|
||||
{
|
||||
Datei("Heat (1995).mkv");
|
||||
|
||||
using var server = new FlaroServer();
|
||||
var alsAdmin = await Aufsetzen(server, "Filme", "Movies");
|
||||
var film = (await Filme(alsAdmin)).Single();
|
||||
|
||||
var krumm = await alsAdmin.PostAsJsonAsync($"/api/v1/items/{film.Id}/reports",
|
||||
new MeldungRequest("bild", "Hier.", "Mitte"));
|
||||
var ohne = await alsAdmin.PostAsJsonAsync($"/api/v1/items/{film.Id}/reports",
|
||||
new MeldungRequest("bild", "Betrifft den ganzen Film.", ""));
|
||||
|
||||
var liste = await alsAdmin.GetFromJsonAsync<List<Meldungseintrag>>("/api/v1/reports");
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(krumm.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest));
|
||||
Assert.That(ohne.StatusCode, Is.EqualTo(HttpStatusCode.OK));
|
||||
Assert.That(liste, Has.Count.EqualTo(1));
|
||||
Assert.That(liste![0].PositionSeconds, Is.Null);
|
||||
});
|
||||
}
|
||||
|
||||
/* ---------- Gerüst ---------- */
|
||||
|
||||
private async Task<HttpClient> Aufsetzen(FlaroServer server, string name, string art)
|
||||
{
|
||||
var admin = await server.SetUpAndSignIn();
|
||||
var alsAdmin = server.AlsAngemeldeter(admin.Token);
|
||||
|
||||
var angelegt = await alsAdmin.PostAsJsonAsync("/api/v1/libraries",
|
||||
new CreateLibraryRequest(name, art, _bestand, ReviewNewItems: false));
|
||||
angelegt.EnsureSuccessStatusCode();
|
||||
var bibliothek = (await angelegt.Content.ReadFromJsonAsync<LibraryResponse>())!;
|
||||
|
||||
await Scannen(alsAdmin, bibliothek.Id);
|
||||
return alsAdmin;
|
||||
}
|
||||
|
||||
private static async Task<IReadOnlyList<MovieResponse>> Filme(HttpClient client) =>
|
||||
(await client.GetFromJsonAsync<Page<MovieResponse>>("/api/v1/movies"))!.Items;
|
||||
|
||||
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<ScanStatus>($"/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.");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user