Beim Anlegen einer Mediathek verlangte das Feld den Pfad im Container — nicht den auf dem NAS. Diese Unterscheidung kennt nur, wer die Compose-Datei selbst geschrieben hat, und ein Tippfehler darin ergibt eine leere Mediathek ohne erkennbaren Grund. Neben dem Feld steht jetzt ein Knopf, der die Ordner des Servers im selben Dialog aufklappt: Pfadzeile in anklickbaren Stücken, Unterordner darunter, unten der Knopf mit dem gewählten Pfad darauf. Im selben Dialog und nicht in einem zweiten darüber: zwei gestapelte Dialoge sind beim Schliessen immer eine Frage zu viel. Das Feld bleibt tippbar. Wer den Pfad kennt, ist damit schneller, und bei einem tiefen Baum ist es die einzige geduldschonende Möglichkeit. An der Wurzel bleiben /proc, /sys, /usr und Verwandte aussen vor. Das ist keine Absperrung — wer den Pfad kennt, tippt ihn weiterhin ein —, sondern eine Liste aus zwanzig Einträgen, von denen achtzehn nie eine Mediathek enthalten, ist unbrauchbar. Ein Ordner ohne Leserecht wird gemeldet, nicht geworfen. Ohne das flöge eine UnauthorizedAccessException bis in die Antwort und würde zur 500, während die ehrliche Auskunft lautet: den darfst du nicht lesen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
152 lines
5.4 KiB
C#
152 lines
5.4 KiB
C#
using Flaro.Api;
|
|
|
|
namespace Flaro.Api.Tests;
|
|
|
|
/// <summary>Das Durchklicken durch das Dateisystem.</summary>
|
|
///
|
|
/// <remarks>
|
|
/// <b>Alles unter <see cref="Path.GetTempPath"/>, nicht unter festen Pfaden.</b>
|
|
/// Diese Tests laufen auf dem Arbeitsrechner, wo es weder <c>/media</c> noch
|
|
/// <c>/volume1</c> gibt — ein Test, der das voraussetzt, prüft nur, auf
|
|
/// welchem Rechner er gerade läuft.
|
|
/// </remarks>
|
|
public class OrdnerbaumTests
|
|
{
|
|
private string _wurzel = "";
|
|
|
|
[SetUp]
|
|
public void Anlegen() =>
|
|
_wurzel = Directory.CreateDirectory(
|
|
Path.Combine(Path.GetTempPath(), "flaro-baum-" + Guid.CreateVersion7().ToString("N"))).FullName;
|
|
|
|
[TearDown]
|
|
public void Aufraeumen()
|
|
{
|
|
// Rechte zurücksetzen, sonst bleibt das gesperrte Verzeichnis liegen.
|
|
if (!OperatingSystem.IsWindows() && Directory.Exists(_wurzel))
|
|
{
|
|
foreach (var d in Directory.EnumerateDirectories(_wurzel, "*", SearchOption.AllDirectories))
|
|
File.SetUnixFileMode(d, UnixFileMode.UserRead | UnixFileMode.UserWrite | UnixFileMode.UserExecute);
|
|
}
|
|
|
|
if (Directory.Exists(_wurzel)) Directory.Delete(_wurzel, recursive: true);
|
|
}
|
|
|
|
[Test]
|
|
public void ListetUnterordnerAlphabetisch()
|
|
{
|
|
Directory.CreateDirectory(Path.Combine(_wurzel, "Serien"));
|
|
Directory.CreateDirectory(Path.Combine(_wurzel, "filme"));
|
|
Directory.CreateDirectory(Path.Combine(_wurzel, "Dokus"));
|
|
|
|
var baum = Ordnerbaum.Lesen(_wurzel);
|
|
|
|
Assert.That(baum, Is.Not.Null);
|
|
// Ohne Rücksicht auf Gross- und Kleinschreibung — sonst stünde
|
|
// „filme" hinter „Serien", was in einer Liste niemand sucht.
|
|
Assert.That(baum!.Entries.Select(e => e.Name), Is.EqualTo(new[] { "Dokus", "filme", "Serien" }));
|
|
Assert.That(baum.Note, Is.Null);
|
|
}
|
|
|
|
/// <summary>Dateien gehören nicht in eine Ordnerauswahl.</summary>
|
|
[Test]
|
|
public void ZeigtNurOrdnerKeineDateien()
|
|
{
|
|
Directory.CreateDirectory(Path.Combine(_wurzel, "Filme"));
|
|
File.WriteAllText(Path.Combine(_wurzel, "Dune.mkv"), "");
|
|
|
|
var baum = Ordnerbaum.Lesen(_wurzel);
|
|
|
|
Assert.That(baum!.Entries.Select(e => e.Name), Is.EqualTo(new[] { "Filme" }));
|
|
}
|
|
|
|
[Test]
|
|
public void KenntDenUebergeordnetenOrdner()
|
|
{
|
|
var unten = Directory.CreateDirectory(Path.Combine(_wurzel, "Filme", "Tief")).FullName;
|
|
|
|
var baum = Ordnerbaum.Lesen(unten);
|
|
|
|
Assert.That(baum!.Parent, Is.EqualTo(Path.Combine(_wurzel, "Filme")));
|
|
}
|
|
|
|
/// <summary>„/media/../etc" wird zu „/etc" — der angezeigte Pfad ist der, den man tippen würde.</summary>
|
|
[Test]
|
|
public void LoestPunktPunktAuf()
|
|
{
|
|
Directory.CreateDirectory(Path.Combine(_wurzel, "Filme"));
|
|
var verschlungen = Path.Combine(_wurzel, "Filme", "..", "Filme");
|
|
|
|
var baum = Ordnerbaum.Lesen(verschlungen);
|
|
|
|
Assert.That(baum!.Path, Is.EqualTo(Path.Combine(_wurzel, "Filme")));
|
|
}
|
|
|
|
/// <summary><c>null</c> heisst „diesen Pfad gibt es nicht" — nicht „leer".</summary>
|
|
[Test]
|
|
public void GibtNullFuerEinenPfadDenEsNichtGibt() =>
|
|
Assert.That(Ordnerbaum.Lesen(Path.Combine(_wurzel, "gibtesnicht")), Is.Null);
|
|
|
|
/// <summary>Eine Datei ist kein Ordner, auch wenn es sie gibt.</summary>
|
|
[Test]
|
|
public void GibtNullFuerEineDatei()
|
|
{
|
|
var datei = Path.Combine(_wurzel, "Dune.mkv");
|
|
File.WriteAllText(datei, "");
|
|
|
|
Assert.That(Ordnerbaum.Lesen(datei), Is.Null);
|
|
}
|
|
|
|
[Test]
|
|
public void SagtWennEsNichtsDarunterGibt()
|
|
{
|
|
var baum = Ordnerbaum.Lesen(_wurzel);
|
|
|
|
Assert.That(baum!.Entries, Is.Empty);
|
|
Assert.That(baum.Note, Is.EqualTo("Keine Unterordner."));
|
|
}
|
|
|
|
/// <summary>Ein Ordner ohne Leserecht ist eine Auskunft, kein Serverfehler.</summary>
|
|
///
|
|
/// <remarks>
|
|
/// Ohne diesen Fall flöge eine <see cref="UnauthorizedAccessException"/> bis
|
|
/// in die Antwort und würde zu einer 500 — während die ehrliche Antwort
|
|
/// lautet: den darfst du nicht lesen.
|
|
/// </remarks>
|
|
[Test]
|
|
public void MeldetFehlendesLeserechtStattZuWerfen()
|
|
{
|
|
// Mit `return` statt nur `Assert.Ignore`: der Plattform-Analysator
|
|
// kennt `Assert.Ignore` nicht als Abbruch und hielte den Rest sonst
|
|
// unter Windows für erreichbar — CA1416.
|
|
if (OperatingSystem.IsWindows())
|
|
{
|
|
Assert.Ignore("Unix-Rechte gibt es dort nicht.");
|
|
return;
|
|
}
|
|
|
|
|
|
var gesperrt = Directory.CreateDirectory(Path.Combine(_wurzel, "gesperrt")).FullName;
|
|
Directory.CreateDirectory(Path.Combine(gesperrt, "darin"));
|
|
File.SetUnixFileMode(gesperrt, UnixFileMode.None);
|
|
|
|
var baum = Ordnerbaum.Lesen(gesperrt);
|
|
|
|
Assert.That(baum, Is.Not.Null);
|
|
Assert.That(baum!.Entries, Is.Empty);
|
|
Assert.That(baum.Note, Is.EqualTo("Kein Leserecht für diesen Ordner."));
|
|
}
|
|
|
|
/// <summary>Ohne Angabe fängt die Auswahl irgendwo Sinnvollem an — und existiert.</summary>
|
|
[Test]
|
|
public void FaengtBeiEinemVorhandenenOrdnerAn()
|
|
{
|
|
var anfang = Ordnerbaum.Anfang();
|
|
|
|
Assert.That(anfang, Is.Not.Empty);
|
|
Assert.That(Directory.Exists(anfang), $"Der Anfang „{anfang}\" müsste es geben.");
|
|
Assert.That(Ordnerbaum.Lesen(null), Is.Not.Null);
|
|
Assert.That(Ordnerbaum.Lesen(" "), Is.Not.Null, "Leerzeichen zählen wie keine Angabe.");
|
|
}
|
|
}
|