Halo heisst ab hier Flaro — die Marke stand im Weg

Der Upload zu App Store Connect scheiterte am Namen. „Halo" ist eine
eingetragene Marke von Microsoft; im App Store heisst zwar keine App exakt
so, aber reservierte und markenrechtlich belegte Namen tauchen in keiner
Suche auf. Der einzige verlässliche Test war die Eingabe dort, und die ging
daneben.

**Der Name wurde diesmal vorher geprüft.** Zwanzig Kandidaten gegen den
deutschen und amerikanischen App Store, auf exakte Treffer und auf ähnliche
Namen. „Kavo" wäre dieselbe Wand gewesen — exakt belegt in beiden Stores, in
Deutschland von KaVo Dental GmbH. „Flare" ist mit 42 ähnlichen Apps je Store
der am dichtesten besetzte Name der ganzen Runde. **Flaro** ist in beiden
frei und liest sich als eigenes Wort, nicht als Flare mit Tippfehler.

Das bleibt eine Store-Suche und keine Markenrecherche — reservierte Namen
ohne veröffentlichte App sieht sie nicht. Aber es ist mehr Prüfung, als beim
letzten Mal stattfand.

---

2160 Textvorkommen in 273 Dateien, 49 Pfade. Betroffen ist diesmal auch der
Server: `Flaro.Api`, `Flaro.Core`, `Flaro.Data`, `Flaro.Media`,
`Flaro.Metadata`, `FlaroContext`, `FlaroPaths`, `flaro.db`, die Variablen
`FLARO_DATA`, `FLARO_CACHE`, `FLARO_TRANSCODE`, `FLARO_WRITE_SIDECARS`, und
das Image.

**Vor dem Ersetzen geprüft**, ob „halo" irgendwo als Teil eines anderen
Wortes steckt — und ob es im TMDB-Token vorkommt. Tat es nicht; ein blindes
Ersetzen hätte den sonst zerlegt.

`docs/superpowers/specs/` bleibt wie beim letzten Mal unangetastet: datierte
Entwurfsdokumente sind ein Protokoll getroffener Entscheidungen. Das
Server-Dokument hat stattdessen einen Nachtrag bekommen, damit es niemanden
in die Irre führt.

**Die Bundle-ID wandert mit**, zum zweiten Mal an einem Tag:
`com.scarriffleservices.flaro`. Wer die vorige Fassung installiert hat, hat
jetzt zwei Karteileichen auf dem Gerät — beide löschen, dann ist Ruhe.

308 Server-Tests grün, 368 im Swift-Paket, null Warnungen. iOS und tvOS
bauen, das Image baut, und ein frischer Container läuft durch Einrichtung
und Anmeldung.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-10-03 19:38:27 +02:00
co-authored by Claude Opus 5
parent f98b995050
commit a7cf6c5def
390 changed files with 2078 additions and 2069 deletions
@@ -1,6 +1,6 @@
using Halo.Core.Metadata;
using Flaro.Core.Metadata;
namespace Halo.Api.Tests;
namespace Flaro.Api.Tests;
/// <summary>Ein Anbieter, der nichts weiss und nichts fragt.</summary>
///
@@ -1,19 +1,19 @@
using System.Net;
using System.Net.Http.Json;
using Halo.Api.Endpoints;
using Flaro.Api.Endpoints;
namespace Halo.Api.Tests;
namespace Flaro.Api.Tests;
/// <summary>Anmelden am Fernseher, ohne ein Passwort zu tippen.</summary>
public class CodeSignInTests
{
private static Task<HttpResponseMessage> CodeAnfordern(HaloServer server) =>
private static Task<HttpResponseMessage> CodeAnfordern(FlaroServer server) =>
server.Client.PostAsJsonAsync("/api/v1/auth/code", new CodeRequest("Wohnzimmer", "tv-1"));
[Test]
public async Task Der_ganze_Weg_vom_Bildschirm_zum_Token()
{
using var server = new HaloServer();
using var server = new FlaroServer();
var telefon = await server.SetUpAndSignIn();
// 1. Der Fernseher fordert an und zeigt den Code.
@@ -52,7 +52,7 @@ public class CodeSignInTests
[Test]
public async Task Ein_fremder_Abholschein_bekommt_nichts()
{
using var server = new HaloServer();
using var server = new FlaroServer();
var telefon = await server.SetUpAndSignIn();
var echt = await (await CodeAnfordern(server)).Content.ReadFromJsonAsync<CodeResponse>();
@@ -69,7 +69,7 @@ public class CodeSignInTests
[Test]
public async Task Der_Code_ist_kein_Abholschein()
{
using var server = new HaloServer();
using var server = new FlaroServer();
var telefon = await server.SetUpAndSignIn();
var echt = await (await CodeAnfordern(server)).Content.ReadFromJsonAsync<CodeResponse>();
@@ -82,7 +82,7 @@ public class CodeSignInTests
[Test]
public async Task Ein_abgeholtes_Token_gibt_es_kein_zweites_Mal()
{
using var server = new HaloServer();
using var server = new FlaroServer();
var telefon = await server.SetUpAndSignIn();
var angefordert = await (await CodeAnfordern(server)).Content.ReadFromJsonAsync<CodeResponse>();
@@ -100,7 +100,7 @@ public class CodeSignInTests
[Test]
public async Task Ein_Unangemeldeter_kann_nichts_bestaetigen()
{
using var server = new HaloServer();
using var server = new FlaroServer();
await server.SetUpAndSignIn();
var angefordert = await (await CodeAnfordern(server)).Content.ReadFromJsonAsync<CodeResponse>();
@@ -114,7 +114,7 @@ public class CodeSignInTests
[Test]
public async Task Ein_erfundener_Code_laesst_sich_nicht_bestaetigen()
{
using var server = new HaloServer();
using var server = new FlaroServer();
var telefon = await server.SetUpAndSignIn();
var versuch = await server.AlsAngemeldeter(telefon.Token).PostAsync("/api/v1/auth/code/000000", null);
@@ -125,7 +125,7 @@ public class CodeSignInTests
[Test]
public async Task Die_Bestaetigung_nennt_das_Geraet()
{
using var server = new HaloServer();
using var server = new FlaroServer();
var telefon = await server.SetUpAndSignIn();
var angefordert = await (await CodeAnfordern(server)).Content.ReadFromJsonAsync<CodeResponse>();
@@ -2,9 +2,9 @@ using System.Diagnostics;
using System.Net;
using System.Net.Http.Headers;
using System.Net.Http.Json;
using Halo.Api.Endpoints;
using Flaro.Api.Endpoints;
namespace Halo.Api.Tests;
namespace Flaro.Api.Tests;
/// <summary>Der Weg vom Scan bis zur heruntergeladenen Datei.</summary>
public class DeliveryTests
@@ -21,7 +21,7 @@ public class DeliveryTests
{
if (Werkzeug("ffmpeg") is null) Assert.Ignore("ffmpeg ist auf diesem Rechner nicht installiert.");
_bestand = Directory.CreateDirectory(
Path.Combine(Path.GetTempPath(), "halo-liefern-" + Guid.CreateVersion7().ToString("N"))).FullName;
Path.Combine(Path.GetTempPath(), "flaro-liefern-" + Guid.CreateVersion7().ToString("N"))).FullName;
}
[TearDown]
@@ -56,7 +56,7 @@ public class DeliveryTests
private async Task<(HttpClient Client, MediaFileResponse Datei)> Eingelesen()
{
var server = _server = new HaloServer();
var server = _server = new FlaroServer();
var admin = await server.SetUpAndSignIn();
var client = server.AlsAngemeldeter(admin.Token);
@@ -78,7 +78,7 @@ public class DeliveryTests
return (client, filme.Items[0].Files.Single());
}
private HaloServer? _server;
private FlaroServer? _server;
[TearDown]
public void ServerSchliessen() { _server?.Dispose(); _server = null; }
@@ -22,9 +22,9 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Halo.Api\Halo.Api.csproj" />
<ProjectReference Include="..\..\src\Halo.Data\Halo.Data.csproj" />
<ProjectReference Include="..\..\src\Halo.Core\Halo.Core.csproj" />
<ProjectReference Include="..\..\src\Flaro.Api\Flaro.Api.csproj" />
<ProjectReference Include="..\..\src\Flaro.Data\Flaro.Data.csproj" />
<ProjectReference Include="..\..\src\Flaro.Core\Flaro.Core.csproj" />
</ItemGroup>
</Project>
@@ -1,9 +1,9 @@
namespace Halo.Api.Tests;
namespace Flaro.Api.Tests;
/// <summary>Was beim allerersten Start passiert, bevor irgendetwas anderes greift.</summary>
public class HaloPathsTests
public class FlaroPathsTests
{
private static readonly string[] Variablen = ["HALO_DATA", "HALO_CACHE", "HALO_TRANSCODE"];
private static readonly string[] Variablen = ["FLARO_DATA", "FLARO_CACHE", "FLARO_TRANSCODE"];
private string _wurzel = "";
private Dictionary<string, string?> _vorher = [];
@@ -26,7 +26,7 @@ public class HaloPathsTests
{
_vorher = Variablen.ToDictionary(v => v, Environment.GetEnvironmentVariable);
_wurzel = Directory.CreateDirectory(
Path.Combine(Path.GetTempPath(), "halo-pfade-" + Guid.CreateVersion7().ToString("N"))).FullName;
Path.Combine(Path.GetTempPath(), "flaro-pfade-" + Guid.CreateVersion7().ToString("N"))).FullName;
}
[TearDown]
@@ -43,7 +43,7 @@ public class HaloPathsTests
foreach (var (name, wert) in _vorher) Environment.SetEnvironmentVariable(name, wert);
}
private HaloPaths Pfade(string? daten = null) => new()
private FlaroPaths Pfade(string? daten = null) => new()
{
Data = daten ?? Path.Combine(_wurzel, "config"),
Cache = Path.Combine(_wurzel, "cache"),
@@ -90,7 +90,7 @@ public class HaloPathsTests
var fehler = Assert.Throws<InvalidOperationException>(() => Pfade(daten: gesperrt).EnsureExists());
Assert.That(fehler!.Message, Does.Contain("HALO_DATA"), "Welche Einstellung gemeint ist");
Assert.That(fehler!.Message, Does.Contain("FLARO_DATA"), "Welche Einstellung gemeint ist");
Assert.That(fehler.Message, Does.Contain(gesperrt), "Welches Verzeichnis");
Assert.That(fehler.Message, Does.Contain("uid="), "Als wer der Server läuft");
Assert.That(fehler.Message, Does.Contain("chown"), "Was zu tun ist");
@@ -99,20 +99,20 @@ public class HaloPathsTests
[Test]
public void Ohne_Umgebungsvariablen_liegt_alles_neben_dem_Programm()
{
foreach (var name in new[] { "HALO_DATA", "HALO_CACHE", "HALO_TRANSCODE" })
foreach (var name in new[] { "FLARO_DATA", "FLARO_CACHE", "FLARO_TRANSCODE" })
Environment.SetEnvironmentVariable(name, null);
var pfade = HaloPaths.FromEnvironment(_wurzel);
var pfade = FlaroPaths.FromEnvironment(_wurzel);
Assert.That(pfade.Data, Is.EqualTo(Path.Combine(_wurzel, "data")));
Assert.That(pfade.DatabaseFile, Does.EndWith("halo.db"));
Assert.That(pfade.DatabaseFile, Does.EndWith("flaro.db"));
}
[Test]
public void Die_Umgebung_sticht_die_Voreinstellung()
{
Environment.SetEnvironmentVariable("HALO_DATA", "/woanders");
Assert.That(HaloPaths.FromEnvironment(_wurzel).Data, Is.EqualTo("/woanders"));
Environment.SetEnvironmentVariable("FLARO_DATA", "/woanders");
Assert.That(FlaroPaths.FromEnvironment(_wurzel).Data, Is.EqualTo("/woanders"));
// Zurückgesetzt wird im TearDown, für alle drei Variablen gemeinsam.
}
}
@@ -1,12 +1,12 @@
using System.Net.Http.Headers;
using System.Net.Http.Json;
using Halo.Api.Endpoints;
using Flaro.Api.Endpoints;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Halo.Core.Metadata;
using Flaro.Core.Metadata;
namespace Halo.Api.Tests;
namespace Flaro.Api.Tests;
/// <summary>Ein frisch gestarteter Server mit leerer Datenbank.</summary>
///
@@ -16,7 +16,7 @@ namespace Halo.Api.Tests;
/// Migration wandern lassen, Einrichtung anbieten. Genau diese Kette sieht ein
/// Fremder als Erstes — und genau sie würde ein Ersatzspeicher überspringen.
/// </remarks>
public sealed class HaloServer : IDisposable
public sealed class FlaroServer : IDisposable
{
private readonly WebApplicationFactory<Program> _grundlage;
private readonly WebApplicationFactory<Program> _fabrik;
@@ -24,16 +24,16 @@ public sealed class HaloServer : IDisposable
public HttpClient Client { get; }
public HaloServer(Action<IServiceCollection>? dienste = null)
public FlaroServer(Action<IServiceCollection>? dienste = null)
{
_verzeichnis = Path.Combine(Path.GetTempPath(), "halo-test-" + Guid.CreateVersion7().ToString("N"));
Environment.SetEnvironmentVariable("HALO_DATA", Path.Combine(_verzeichnis, "config"));
Environment.SetEnvironmentVariable("HALO_CACHE", Path.Combine(_verzeichnis, "cache"));
Environment.SetEnvironmentVariable("HALO_TRANSCODE", Path.Combine(_verzeichnis, "transcode"));
_verzeichnis = Path.Combine(Path.GetTempPath(), "flaro-test-" + Guid.CreateVersion7().ToString("N"));
Environment.SetEnvironmentVariable("FLARO_DATA", Path.Combine(_verzeichnis, "config"));
Environment.SetEnvironmentVariable("FLARO_CACHE", Path.Combine(_verzeichnis, "cache"));
Environment.SetEnvironmentVariable("FLARO_TRANSCODE", Path.Combine(_verzeichnis, "transcode"));
// Die Zugriffsgrenze zählt je Adresse, und im Test kommt alles von
// derselben. Ohne das Hochsetzen prüfte man hier die Grenze statt der
// Anmeldung.
Environment.SetEnvironmentVariable("HALO_RATELIMIT_SIGNIN", "10000");
Environment.SetEnvironmentVariable("FLARO_RATELIMIT_SIGNIN", "10000");
_grundlage = new WebApplicationFactory<Program>();
_fabrik = _grundlage.WithWebHostBuilder(b => b.ConfigureServices(sammlung =>
@@ -1,15 +1,15 @@
using System.Net;
using System.Net.Http.Json;
using Halo.Api.Endpoints;
using Flaro.Api.Endpoints;
namespace Halo.Api.Tests;
namespace Flaro.Api.Tests;
public class ManagementTests
{
[Test]
public async Task Ein_Administrator_legt_Benutzer_an()
{
using var server = new HaloServer();
using var server = new FlaroServer();
var admin = await server.SetUpAndSignIn();
var client = server.AlsAngemeldeter(admin.Token);
@@ -25,7 +25,7 @@ public class ManagementTests
[Test]
public async Task Ein_gewoehnlicher_Benutzer_darf_keine_Benutzer_anlegen()
{
using var server = new HaloServer();
using var server = new FlaroServer();
var admin = await server.SetUpAndSignIn();
await server.AlsAngemeldeter(admin.Token).PostAsJsonAsync("/api/v1/users",
new CreateUserRequest("mitbewohner", "auch-ein-langes-passwort", false));
@@ -40,7 +40,7 @@ public class ManagementTests
[Test]
public async Task Derselbe_Benutzername_zweimal_wird_abgelehnt()
{
using var server = new HaloServer();
using var server = new FlaroServer();
var admin = await server.SetUpAndSignIn();
var client = server.AlsAngemeldeter(admin.Token);
@@ -60,7 +60,7 @@ public class ManagementTests
[Test]
public async Task Eine_Bibliothek_auf_einem_Pfad_den_es_nicht_gibt_wird_abgelehnt()
{
using var server = new HaloServer();
using var server = new FlaroServer();
var admin = await server.SetUpAndSignIn();
var versuch = await server.AlsAngemeldeter(admin.Token).PostAsJsonAsync("/api/v1/libraries",
@@ -73,7 +73,7 @@ public class ManagementTests
[Test]
public async Task Eine_Bibliothek_auf_einem_vorhandenen_Pfad_wird_angelegt()
{
using var server = new HaloServer();
using var server = new FlaroServer();
var admin = await server.SetUpAndSignIn();
var client = server.AlsAngemeldeter(admin.Token);
var pfad = Path.GetTempPath();
@@ -89,7 +89,7 @@ public class ManagementTests
[Test]
public async Task Eine_unbekannte_Art_wird_abgelehnt()
{
using var server = new HaloServer();
using var server = new FlaroServer();
var admin = await server.SetUpAndSignIn();
var versuch = await server.AlsAngemeldeter(admin.Token).PostAsJsonAsync("/api/v1/libraries",
@@ -1,20 +1,20 @@
using System.Diagnostics;
using System.Net;
using System.Net.Http.Json;
using Halo.Api.Endpoints;
using Halo.Core.Library;
using Halo.Core.Metadata;
using Halo.Media;
using Flaro.Api.Endpoints;
using Flaro.Core.Library;
using Flaro.Core.Metadata;
using Flaro.Media;
using Microsoft.Extensions.DependencyInjection;
namespace Halo.Api.Tests;
namespace Flaro.Api.Tests;
/// <summary>Eine falsche Zuordnung richtigstellen — und sie festschreiben.</summary>
public class MatchCorrectionTests
{
private string _bestand = "";
private string _filmOrdner = "";
private HaloServer? _server;
private FlaroServer? _server;
private static string? Werkzeug(string name) =>
(Environment.GetEnvironmentVariable("PATH") ?? "").Split(Path.PathSeparator)
@@ -26,7 +26,7 @@ public class MatchCorrectionTests
{
if (Werkzeug("ffmpeg") is null) Assert.Ignore("ffmpeg ist auf diesem Rechner nicht installiert.");
_bestand = Directory.CreateDirectory(
Path.Combine(Path.GetTempPath(), "halo-korrektur-" + Guid.CreateVersion7().ToString("N"))).FullName;
Path.Combine(Path.GetTempPath(), "flaro-korrektur-" + Guid.CreateVersion7().ToString("N"))).FullName;
_filmOrdner = Directory.CreateDirectory(Path.Combine(_bestand, "Dark (2017)")).FullName;
var start = new ProcessStartInfo(Werkzeug("ffmpeg")!) { RedirectStandardError = true, UseShellExecute = false };
@@ -87,7 +87,7 @@ public class MatchCorrectionTests
private async Task<(HttpClient Client, MovieResponse Film)> Eingelesen(bool beiwerkSchreiben)
{
_server = new HaloServer(dienste =>
_server = new FlaroServer(dienste =>
{
dienste.AddScoped<IMetadataProvider, ZweiTreffer>();
dienste.AddSingleton<ISidecarStore>(
@@ -213,13 +213,13 @@ public class MatchCorrectionTests
(await client.PutAsJsonAsync($"/api/v1/movies/{film.Id}/match", new ApplyMatchRequest(613)))
.EnsureSuccessStatusCode();
var bibliotheken = await client.GetFromJsonAsync<List<LibraryResponse>>("/api/v1/libraries");
(await client.PostAsync($"/api/v1/libraries/{bibliotheken!.Single().Id}/scan", null))
var bibliothek = (await client.GetFromJsonAsync<List<LibraryResponse>>("/api/v1/libraries"))!.Single();
(await client.PostAsync($"/api/v1/libraries/{bibliothek.Id}/scan", null))
.EnsureSuccessStatusCode();
for (var i = 0; i < 150; i++)
{
var stand = await client.GetFromJsonAsync<ScanStatus>(
$"/api/v1/libraries/{bibliotheken.Single().Id}/scan");
$"/api/v1/libraries/{bibliothek.Id}/scan");
if (stand!.State == "finished") break;
await Task.Delay(50);
}
@@ -1,10 +1,10 @@
using System.Net;
using System.Net.Http.Json;
using Halo.Api.Endpoints;
using Halo.Core.Metadata;
using Flaro.Api.Endpoints;
using Flaro.Core.Metadata;
using Microsoft.Extensions.DependencyInjection;
namespace Halo.Api.Tests;
namespace Flaro.Api.Tests;
/// <summary>Ein Anbieter, der antwortet, was der Test vorgibt.</summary>
public sealed class VorgegebenerAnbieter : IMetadataProvider
@@ -84,7 +84,7 @@ public class MetadataTests
[SetUp]
public void Anlegen() => _bestand = Directory.CreateDirectory(
Path.Combine(Path.GetTempPath(), "halo-meta-" + Guid.CreateVersion7().ToString("N"))).FullName;
Path.Combine(Path.GetTempPath(), "flaro-meta-" + Guid.CreateVersion7().ToString("N"))).FullName;
[TearDown]
public void Aufraeumen()
@@ -99,14 +99,14 @@ public class MetadataTests
File.WriteAllBytes(voll, new byte[1024]);
}
private HaloServer MitAnbieter(VorgegebenerAnbieter anbieter, VorgegebeneBilder? bilder = null) =>
private FlaroServer MitAnbieter(VorgegebenerAnbieter anbieter, VorgegebeneBilder? bilder = null) =>
new(dienste =>
{
dienste.AddScoped<IMetadataProvider>(_ => anbieter);
dienste.AddScoped<IImageStore>(_ => bilder ?? new VorgegebeneBilder());
});
private async Task<(HttpClient Admin, Guid Library)> Einrichten(HaloServer server, string art)
private async Task<(HttpClient Admin, Guid Library)> Einrichten(FlaroServer server, string art)
{
var admin = await server.SetUpAndSignIn();
var client = server.AlsAngemeldeter(admin.Token);
@@ -282,7 +282,7 @@ public class MetadataSettingsTests
[Test]
public async Task Der_Schluessel_kommt_nicht_zurueck()
{
using var server = new HaloServer();
using var server = new FlaroServer();
var admin = await server.SetUpAndSignIn();
var client = server.AlsAngemeldeter(admin.Token);
@@ -308,12 +308,12 @@ public class MetadataSettingsTests
[Test]
public async Task Ohne_eigenen_Schluessel_greift_der_mitgelieferte()
{
using var server = new HaloServer();
using var server = new FlaroServer();
var admin = await server.SetUpAndSignIn();
var stand = await server.AlsAngemeldeter(admin.Token)
.GetFromJsonAsync<MetadataSettingsResponse>("/api/v1/settings/metadata");
var mitgeliefert = Halo.Metadata.TmdbSettings.ProjectApiKey.Length > 0;
var mitgeliefert = Flaro.Metadata.TmdbSettings.ProjectApiKey.Length > 0;
Assert.That(stand!.HasApiKey, Is.EqualTo(mitgeliefert));
Assert.That(stand.UsingProjectKey, Is.EqualTo(mitgeliefert));
Assert.That(stand.Language, Is.EqualTo("en-US"));
@@ -322,7 +322,7 @@ public class MetadataSettingsTests
[Test]
public async Task Ein_eigener_Schluessel_loest_den_mitgelieferten_ab()
{
using var server = new HaloServer();
using var server = new FlaroServer();
var admin = await server.SetUpAndSignIn();
var client = server.AlsAngemeldeter(admin.Token);
@@ -338,7 +338,7 @@ public class MetadataSettingsTests
[Test]
public async Task Der_Pflichthinweis_zu_TMDB_steht_in_der_Systemauskunft()
{
using var server = new HaloServer();
using var server = new FlaroServer();
var info = await server.Client.GetFromJsonAsync<SystemInfo>("/api/v1/system/info");
var tmdb = info!.Attributions.Single();
@@ -352,7 +352,7 @@ public class MetadataSettingsTests
[TestCase("de-de")]
public async Task Eine_unsinnige_Sprachkennung_wird_abgelehnt(string sprache)
{
using var server = new HaloServer();
using var server = new FlaroServer();
var admin = await server.SetUpAndSignIn();
var antwort = await server.AlsAngemeldeter(admin.Token)
@@ -364,7 +364,7 @@ public class MetadataSettingsTests
[Test]
public async Task Ein_gewoehnlicher_Benutzer_sieht_die_Einstellungen_nicht()
{
using var server = new HaloServer();
using var server = new FlaroServer();
var admin = await server.SetUpAndSignIn();
await server.AlsAngemeldeter(admin.Token).PostAsJsonAsync("/api/v1/users",
new CreateUserRequest("gast", "auch-ein-langes-passwort", false));
@@ -380,7 +380,7 @@ public class ImageEndpointTests
[Test]
public async Task Bilder_gibt_es_nur_fuer_Angemeldete()
{
using var server = new HaloServer();
using var server = new FlaroServer();
await server.SetUpAndSignIn();
var antwort = await server.Client.GetAsync($"/api/v1/images/{new string('a', 64)}");
@@ -388,12 +388,12 @@ public class ImageEndpointTests
}
/// <summary>Der Weg, der sonst die Datenbank herausgäbe.</summary>
[TestCase("..%2F..%2Fconfig%2Fhalo.db")]
[TestCase("..%2F..%2Fconfig%2Fflaro.db")]
[TestCase("nichtsechzigvierzeichen")]
[TestCase("ZZZZ567890123456789012345678901234567890123456789012345678901234")]
public async Task Ein_Schluessel_der_keiner_ist_fuehrt_ins_Leere(string versuch)
{
using var server = new HaloServer();
using var server = new FlaroServer();
var admin = await server.SetUpAndSignIn();
var antwort = await server.AlsAngemeldeter(admin.Token).GetAsync($"/api/v1/images/{versuch}");
@@ -1,8 +1,8 @@
using System.Net;
using System.Net.Http.Json;
using Halo.Api.Endpoints;
using Flaro.Api.Endpoints;
namespace Halo.Api.Tests;
namespace Flaro.Api.Tests;
/// <summary>Scan gegen einen echten Ordner mit echten Dateien.</summary>
public class ScanTests
@@ -11,7 +11,7 @@ public class ScanTests
[SetUp]
public void Anlegen() => _bestand = Directory.CreateDirectory(
Path.Combine(Path.GetTempPath(), "halo-medien-" + Guid.CreateVersion7().ToString("N"))).FullName;
Path.Combine(Path.GetTempPath(), "flaro-medien-" + Guid.CreateVersion7().ToString("N"))).FullName;
[TearDown]
public void Aufraeumen()
@@ -26,7 +26,7 @@ public class ScanTests
File.WriteAllBytes(voll, new byte[bytes]);
}
private async Task<(HttpClient Admin, Guid LibraryId)> MitBibliothek(HaloServer server, string art)
private async Task<(HttpClient Admin, Guid LibraryId)> MitBibliothek(FlaroServer server, string art)
{
var admin = await server.SetUpAndSignIn();
var client = server.AlsAngemeldeter(admin.Token);
@@ -62,7 +62,7 @@ public class ScanTests
Datei("The Matrix (1999).srt"); // zählt nicht
Datei("@eaDir/The.Matrix.1999.mkv"); // zählt auch nicht
using var server = new HaloServer();
using var server = new FlaroServer();
var (client, bibliothek) = await MitBibliothek(server, "Movies");
var stand = await Scannen(client, bibliothek);
@@ -89,7 +89,7 @@ public class ScanTests
Datei("The Matrix (1999).mkv");
Datei("Inception (2010).mkv");
using var server = new HaloServer();
using var server = new FlaroServer();
var (client, bibliothek) = await MitBibliothek(server, "Movies");
await Scannen(client, bibliothek);
@@ -106,7 +106,7 @@ public class ScanTests
Datei("The Matrix (1999).mkv");
Datei("Inception (2010).mkv");
using var server = new HaloServer();
using var server = new FlaroServer();
var (client, bibliothek) = await MitBibliothek(server, "Movies");
await Scannen(client, bibliothek);
@@ -125,7 +125,7 @@ public class ScanTests
Datei("The Matrix (1999) - 1080p.mkv");
Datei("The Matrix (1999) - 2160p.mkv");
using var server = new HaloServer();
using var server = new FlaroServer();
var (client, bibliothek) = await MitBibliothek(server, "Movies");
await Scannen(client, bibliothek);
@@ -142,7 +142,7 @@ public class ScanTests
Datei("Dark/Staffel 2/Dark - S02E01 - Anfänge.mkv");
Datei("Notruf.Hafenkante.S01E01.German.1080p.mkv");
using var server = new HaloServer();
using var server = new FlaroServer();
var (client, bibliothek) = await MitBibliothek(server, "Shows");
await Scannen(client, bibliothek);
@@ -169,7 +169,7 @@ public class ScanTests
Datei("Dark/Dark - S01E01.mkv");
Datei("Dark/Irgendein Mitschnitt.mkv");
using var server = new HaloServer();
using var server = new FlaroServer();
var (client, bibliothek) = await MitBibliothek(server, "Shows");
var stand = await Scannen(client, bibliothek);
@@ -185,7 +185,7 @@ public class ScanTests
Datei("The Matrix Reloaded (2003).mkv");
Datei("Inception (2010).mkv");
using var server = new HaloServer();
using var server = new FlaroServer();
var (client, bibliothek) = await MitBibliothek(server, "Movies");
await Scannen(client, bibliothek);
@@ -199,7 +199,7 @@ public class ScanTests
{
for (var i = 1; i <= 5; i++) Datei($"Film {i} (200{i}).mkv");
using var server = new HaloServer();
using var server = new FlaroServer();
var (client, bibliothek) = await MitBibliothek(server, "Movies");
await Scannen(client, bibliothek);
@@ -215,7 +215,7 @@ public class ScanTests
[Test]
public async Task Vor_dem_ersten_Lauf_steht_der_Scan_auf_untaetig()
{
using var server = new HaloServer();
using var server = new FlaroServer();
var (client, bibliothek) = await MitBibliothek(server, "Movies");
var stand = await client.GetFromJsonAsync<ScanStatus>($"/api/v1/libraries/{bibliothek}/scan");
@@ -225,7 +225,7 @@ public class ScanTests
[Test]
public async Task Ein_gewoehnlicher_Benutzer_stoesst_keinen_Scan_an()
{
using var server = new HaloServer();
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));
@@ -240,7 +240,7 @@ public class ScanTests
[Test]
public async Task Eine_Bibliothek_die_es_nicht_gibt_laesst_sich_nicht_scannen()
{
using var server = new HaloServer();
using var server = new FlaroServer();
var (client, _) = await MitBibliothek(server, "Movies");
var versuch = await client.PostAsync($"/api/v1/libraries/{Guid.CreateVersion7()}/scan", null);
@@ -1,15 +1,15 @@
using System.Net;
using System.Net.Http.Json;
using Halo.Api.Endpoints;
using Flaro.Api.Endpoints;
namespace Halo.Api.Tests;
namespace Flaro.Api.Tests;
public class SetupTests
{
[Test]
public async Task Ein_frischer_Server_verlangt_Einrichtung()
{
using var server = new HaloServer();
using var server = new FlaroServer();
var info = await server.Client.GetFromJsonAsync<SystemInfo>("/api/v1/system/info");
Assert.That(info!.NeedsSetup, Is.True);
}
@@ -17,7 +17,7 @@ public class SetupTests
[Test]
public async Task Die_Einrichtung_legt_einen_Administrator_an()
{
using var server = new HaloServer();
using var server = new FlaroServer();
var antwort = await server.Client.PostAsJsonAsync("/api/v1/system/setup",
new SetupRequest("guido", "ein-langes-passwort"));
@@ -40,7 +40,7 @@ public class SetupTests
[Test]
public async Task Ein_zweites_Mal_Einrichten_wird_abgelehnt()
{
using var server = new HaloServer();
using var server = new FlaroServer();
await server.SetUpAndSignIn();
var zweiterVersuch = await server.Client.PostAsJsonAsync("/api/v1/system/setup",
@@ -52,7 +52,7 @@ public class SetupTests
[Test]
public async Task Ein_zu_kurzes_Passwort_wird_abgelehnt()
{
using var server = new HaloServer();
using var server = new FlaroServer();
var antwort = await server.Client.PostAsJsonAsync("/api/v1/system/setup",
new SetupRequest("guido", "kurz"));
@@ -64,7 +64,7 @@ public class SetupTests
[Test]
public async Task Die_Gesundheitsfrage_braucht_keine_Anmeldung()
{
using var server = new HaloServer();
using var server = new FlaroServer();
var antwort = await server.Client.GetAsync("/health");
Assert.That(antwort.StatusCode, Is.EqualTo(HttpStatusCode.OK));
}
@@ -1,16 +1,16 @@
using System.Net;
using System.Net.Http.Headers;
using System.Net.Http.Json;
using Halo.Api.Endpoints;
using Flaro.Api.Endpoints;
namespace Halo.Api.Tests;
namespace Flaro.Api.Tests;
public class SignInTests
{
[Test]
public async Task Mit_richtigem_Passwort_gibt_es_ein_Token()
{
using var server = new HaloServer();
using var server = new FlaroServer();
var anmeldung = await server.SetUpAndSignIn();
Assert.That(anmeldung.Token, Is.Not.Empty);
@@ -20,7 +20,7 @@ public class SignInTests
[Test]
public async Task Mit_falschem_Passwort_gibt_es_keines()
{
using var server = new HaloServer();
using var server = new FlaroServer();
await server.SetUpAndSignIn();
var antwort = await server.Client.PostAsJsonAsync("/api/v1/auth/login",
@@ -33,7 +33,7 @@ public class SignInTests
[Test]
public async Task Ein_unbekannter_Name_antwortet_wie_ein_falsches_Passwort()
{
using var server = new HaloServer();
using var server = new FlaroServer();
await server.SetUpAndSignIn();
var unbekannt = await server.Client.PostAsJsonAsync("/api/v1/auth/login",
@@ -55,7 +55,7 @@ public class SignInTests
[Test]
public async Task Mit_Token_beantwortet_der_Server_wer_man_ist()
{
using var server = new HaloServer();
using var server = new FlaroServer();
var anmeldung = await server.SetUpAndSignIn();
var ich = await server.AlsAngemeldeter(anmeldung.Token)
@@ -68,7 +68,7 @@ public class SignInTests
[Test]
public async Task Ohne_Token_antwortet_er_nicht()
{
using var server = new HaloServer();
using var server = new FlaroServer();
await server.SetUpAndSignIn();
var antwort = await server.Client.GetAsync("/api/v1/auth/me");
@@ -78,7 +78,7 @@ public class SignInTests
[Test]
public async Task Ein_erfundenes_Token_gilt_nicht()
{
using var server = new HaloServer();
using var server = new FlaroServer();
await server.SetUpAndSignIn();
var antwort = await server.AlsAngemeldeter("ausgedacht-aber-lang-genug").GetAsync("/api/v1/auth/me");
@@ -95,7 +95,7 @@ public class SignInTests
[Test]
public async Task Abmelden_entwertet_das_Token_augenblicklich()
{
using var server = new HaloServer();
using var server = new FlaroServer();
var anmeldung = await server.SetUpAndSignIn();
var client = server.AlsAngemeldeter(anmeldung.Token);
@@ -109,7 +109,7 @@ public class SignInTests
[Test]
public async Task Eine_erneute_Anmeldung_desselben_Geraets_entwertet_das_alte_Token()
{
using var server = new HaloServer();
using var server = new FlaroServer();
var erste = await server.SetUpAndSignIn();
var zweite = await server.SignIn("guido", "ein-langes-passwort", "iPhone", "geraet-1");
@@ -124,7 +124,7 @@ public class SignInTests
[Test]
public async Task Ein_anderes_Geraet_laesst_das_erste_in_Ruhe()
{
using var server = new HaloServer();
using var server = new FlaroServer();
var telefon = await server.SetUpAndSignIn();
var fernseher = await server.SignIn("guido", "ein-langes-passwort", "Apple TV", "geraet-2");
@@ -1,6 +1,6 @@
using Halo.Core.Auth;
using Flaro.Core.Auth;
namespace Halo.Core.Tests;
namespace Flaro.Core.Tests;
public class SecretsTests
{
@@ -1,7 +1,7 @@
using Halo.Core.Delivery;
using Halo.Core.Library;
using Flaro.Core.Delivery;
using Flaro.Core.Library;
namespace Halo.Core.Tests;
namespace Flaro.Core.Tests;
public class DeliveryDeciderTests
{
@@ -1,6 +1,6 @@
using Halo.Core.Library;
using Flaro.Core.Library;
namespace Halo.Core.Tests;
namespace Flaro.Core.Tests;
public class FilmNamenTests
{
@@ -21,7 +21,7 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Halo.Core\Halo.Core.csproj" />
<ProjectReference Include="..\..\src\Flaro.Core\Flaro.Core.csproj" />
</ItemGroup>
</Project>
@@ -1,6 +1,6 @@
using Halo.Core.Library;
using Flaro.Core.Library;
namespace Halo.Core.Tests;
namespace Flaro.Core.Tests;
public class LibraryWalkerTests
{
@@ -8,7 +8,7 @@ public class LibraryWalkerTests
[SetUp]
public void Anlegen() => _wurzel = Directory.CreateDirectory(
Path.Combine(Path.GetTempPath(), "halo-bestand-" + Guid.CreateVersion7().ToString("N"))).FullName;
Path.Combine(Path.GetTempPath(), "flaro-bestand-" + Guid.CreateVersion7().ToString("N"))).FullName;
[TearDown]
public void Aufraeumen()
@@ -1,6 +1,6 @@
using Halo.Core.Users;
using Flaro.Core.Users;
namespace Halo.Core.Tests;
namespace Flaro.Core.Tests;
public class PasswordHasherTests
{
@@ -1,8 +1,8 @@
using Halo.Core.Library;
using Flaro.Core.Library;
namespace Halo.Core.Tests;
namespace Flaro.Core.Tests;
/// <summary>Was Halo in einen Medienordner schreiben darf.</summary>
/// <summary>Was Flaro in einen Medienordner schreiben darf.</summary>
///
/// <remarks>
/// <b>Die wichtigste Prüfdatei des Projekts.</b> Damit Plakate neben dem Film
@@ -86,7 +86,7 @@ public class SidecarNamesTests
///
/// <remarks>
/// Die beiden Hälften könnten auseinanderlaufen — jemand ändert das Muster
/// für Folgen und vergisst die Liste. Dann schriebe Halo nichts mehr und
/// für Folgen und vergisst die Liste. Dann schriebe Flaro nichts mehr und
/// niemand wüsste, warum.
/// </remarks>
[TestCase("Dark - S01E01.mkv")]
@@ -190,12 +190,12 @@ public class NfoTests
Assert.That(Nfo.ReadLocked(kaputt), Is.False);
}
/// <summary>Eine NFO aus dem Medienordner hat Halo nicht geschrieben.</summary>
/// <summary>Eine NFO aus dem Medienordner hat Flaro nicht geschrieben.</summary>
///
/// <remarks>
/// <b>Ohne abgeschaltete DTDs wäre das ein Weg, fremde Dateien zu lesen.</b>
/// Eine untergeschobene NFO mit einer externen Entität könnte den Server
/// dazu bringen, <c>/config/halo.db</c> einzulesen und den Inhalt als
/// dazu bringen, <c>/config/flaro.db</c> einzulesen und den Inhalt als
/// Titel auszuliefern.
/// </remarks>
[Test]
@@ -1,7 +1,7 @@
using Halo.Core.Library;
using Flaro.Core.Library;
using Microsoft.Extensions.Logging.Abstractions;
namespace Halo.Media.Tests;
namespace Flaro.Media.Tests;
public class FfprobeRunnerTests
{
@@ -13,7 +13,7 @@ public class FfprobeRunnerTests
{
if (!Testdateien.FfmpegVorhanden) Assert.Ignore("ffmpeg ist auf diesem Rechner nicht installiert.");
_ordner = Directory.CreateDirectory(
Path.Combine(Path.GetTempPath(), "halo-probe-" + Guid.CreateVersion7().ToString("N"))).FullName;
Path.Combine(Path.GetTempPath(), "flaro-probe-" + Guid.CreateVersion7().ToString("N"))).FullName;
_ffprobe = new FfprobeRunner(
new FfmpegOptions(Testdateien.Suchen("ffmpeg")!, Testdateien.Suchen("ffprobe")!),
NullLogger<FfprobeRunner>.Instance);
@@ -21,8 +21,8 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Halo.Media\Halo.Media.csproj" />
<ProjectReference Include="..\..\src\Halo.Core\Halo.Core.csproj" />
<ProjectReference Include="..\..\src\Flaro.Media\Flaro.Media.csproj" />
<ProjectReference Include="..\..\src\Flaro.Core\Flaro.Core.csproj" />
</ItemGroup>
</Project>
@@ -1,7 +1,7 @@
using Halo.Core.Delivery;
using Halo.Core.Library;
using Flaro.Core.Delivery;
using Flaro.Core.Library;
namespace Halo.Media.Tests;
namespace Flaro.Media.Tests;
/// <summary>Die ffmpeg-Zeile, betrachtet ohne sie auszuführen.</summary>
public class RemuxArgumentsTests
@@ -1,8 +1,8 @@
using Halo.Core.Delivery;
using Halo.Core.Library;
using Flaro.Core.Delivery;
using Flaro.Core.Library;
using Microsoft.Extensions.Logging.Abstractions;
namespace Halo.Media.Tests;
namespace Flaro.Media.Tests;
/// <summary>Umpacken, von der echten Datei bis zum Ergebnis.</summary>
public class RemuxTests
@@ -16,7 +16,7 @@ public class RemuxTests
{
if (!Testdateien.FfmpegVorhanden) Assert.Ignore("ffmpeg ist auf diesem Rechner nicht installiert.");
_ordner = Directory.CreateDirectory(
Path.Combine(Path.GetTempPath(), "halo-umpacken-" + Guid.CreateVersion7().ToString("N"))).FullName;
Path.Combine(Path.GetTempPath(), "flaro-umpacken-" + Guid.CreateVersion7().ToString("N"))).FullName;
var optionen = new FfmpegOptions(Testdateien.Suchen("ffmpeg")!, Testdateien.Suchen("ffprobe")!);
_ffprobe = new FfprobeRunner(optionen, NullLogger<FfprobeRunner>.Instance);
_umpacker = new FfmpegRemuxer(optionen, NullLogger<FfmpegRemuxer>.Instance);
@@ -1,8 +1,8 @@
using System.Text;
using Halo.Core.Library;
using Flaro.Core.Library;
using Microsoft.Extensions.Logging.Abstractions;
namespace Halo.Media.Tests;
namespace Flaro.Media.Tests;
/// <summary>Was wirklich auf der Platte landet.</summary>
public class SidecarWriterTests
@@ -14,7 +14,7 @@ public class SidecarWriterTests
public void Anlegen()
{
_ordner = Directory.CreateDirectory(Path.Combine(
Path.GetTempPath(), "halo-beiwerk-" + Guid.CreateVersion7().ToString("N"),
Path.GetTempPath(), "flaro-beiwerk-" + Guid.CreateVersion7().ToString("N"),
"Der Untergang (2004)")).FullName;
_film = Path.Combine(_ordner, "Der Untergang (2004).mkv");
File.WriteAllBytes(_film, new byte[2048]);
@@ -96,7 +96,7 @@ public class SidecarWriterTests
///
/// <remarks>
/// Eine vorhandene NFO ist eine Zuordnung, die jemand getroffen hat. Sie
/// zu übergehen, bloss weil Halo selbst nicht schreiben darf, wäre
/// zu übergehen, bloss weil Flaro selbst nicht schreiben darf, wäre
/// verkehrt herum.
/// </remarks>
[Test]
@@ -121,7 +121,7 @@ public class SidecarWriterTests
await Schreiber().WriteTextAsync(_film, SidecarNames.MovieNfo, "<movie>neu</movie>");
Assert.That(await File.ReadAllTextAsync(Path.Combine(_ordner, "movie.nfo")), Is.EqualTo("<movie>neu</movie>"));
Assert.That(Directory.GetFiles(_ordner, "*.halo-tmp"), Is.Empty, "Keine Reste vom Umbenennen");
Assert.That(Directory.GetFiles(_ordner, "*.flaro-tmp"), Is.Empty, "Keine Reste vom Umbenennen");
}
[Test]
@@ -1,13 +1,13 @@
using System.Diagnostics;
namespace Halo.Media.Tests;
namespace Flaro.Media.Tests;
/// <summary>Baut echte Mediendateien mit ffmpeg.</summary>
///
/// <remarks>
/// <b>Echte Dateien und keine aufgezeichneten Antworten.</b> Bei TMDB ist das
/// umgekehrt richtig — dort steht ein fremder Dienst dahinter. Hier geht es
/// um das Werkzeug, das Halo im Container ohnehin mitbringt, und die Frage
/// um das Werkzeug, das Flaro im Container ohnehin mitbringt, und die Frage
/// „liest er die zweite Tonspur mitsamt Sprache?" lässt sich nur an einer
/// Datei beantworten, die wirklich zwei hat.
/// </remarks>
@@ -67,7 +67,7 @@ public static class Testdateien
/// daran entscheidet sich, ob eine sonst tadellose Datei stumm bliebe.
/// Der Encoder gilt bei ffmpeg als erprobungsweise und braucht deshalb
/// <c>-strict -2</c> — das betrifft nur das Erzeugen dieser Testvorlage,
/// nicht Halo.
/// nicht Flaro.
/// </remarks>
public static void MitUntauglichemTon(string ziel) => Ausführen("ffmpeg",
[
@@ -1,7 +1,7 @@
using System.Net;
using System.Text;
namespace Halo.Metadata.Tests;
namespace Flaro.Metadata.Tests;
/// <summary>Ein HTTP-Gegenüber, das antwortet, was der Test vorgibt.</summary>
///
@@ -21,8 +21,8 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Halo.Metadata\Halo.Metadata.csproj" />
<ProjectReference Include="..\..\src\Halo.Core\Halo.Core.csproj" />
<ProjectReference Include="..\..\src\Flaro.Metadata\Flaro.Metadata.csproj" />
<ProjectReference Include="..\..\src\Flaro.Core\Flaro.Core.csproj" />
</ItemGroup>
</Project>
@@ -1,9 +1,9 @@
using System.Net;
using System.Text;
using Halo.Core.Metadata;
using Flaro.Core.Metadata;
using Microsoft.Extensions.Logging.Abstractions;
namespace Halo.Metadata.Tests;
namespace Flaro.Metadata.Tests;
public class ImageCacheTests
{
@@ -11,7 +11,7 @@ public class ImageCacheTests
[SetUp]
public void Anlegen() => _wurzel = Directory.CreateDirectory(
Path.Combine(Path.GetTempPath(), "halo-bilder-" + Guid.CreateVersion7().ToString("N"))).FullName;
Path.Combine(Path.GetTempPath(), "flaro-bilder-" + Guid.CreateVersion7().ToString("N"))).FullName;
[TearDown]
public void Aufraeumen()
@@ -71,10 +71,10 @@ public class ImageCacheTests
/// <remarks>
/// <b>Die wichtigste Prüfung dieser Datei.</b> Der Schlüssel kommt aus
/// einer URL. Liesse er Pfadangaben durch, wäre
/// <c>/api/v1/images/../../config/halo.db</c> ein Weg, die Datenbank
/// <c>/api/v1/images/../../config/flaro.db</c> ein Weg, die Datenbank
/// herunterzuladen — samt aller Token-Fingerabdrücke.
/// </remarks>
[TestCase("../../config/halo.db")]
[TestCase("../../config/flaro.db")]
[TestCase("..%2f..%2fconfig")]
[TestCase("/etc/passwd")]
[TestCase("abc")]
@@ -1,8 +1,8 @@
using System.Net;
using Halo.Core.Metadata;
using Flaro.Core.Metadata;
using Microsoft.Extensions.Logging.Abstractions;
namespace Halo.Metadata.Tests;
namespace Flaro.Metadata.Tests;
public class TmdbClientTests
{
@@ -1,8 +1,8 @@
using Microsoft.Extensions.Logging.Abstractions;
namespace Halo.Metadata.Tests;
namespace Flaro.Metadata.Tests;
/// <summary>Womit sich Halo bei TMDB ausweist.</summary>
/// <summary>Womit sich Flaro bei TMDB ausweist.</summary>
public class ZugangTests
{
private const string LeseToken = "eyJhbGciOiJIUzI1NiJ9.beispiel.unterschrift";