Add collector settings and per-source purge
Every limit the fetcher was using was a constant. They are settings now, and CollectOptions became the single place policy lives: AppSettings.ToCollectOptions clamps them, and the HTTP layer's FetchOptions is projected from that. One clamping site rather than two sets of ceilings drifting apart. Clamping rather than validating, for the reason the proxy options already do it: a hand-edited file must not stop the app from starting. A MaxItemBytes edited to zero would otherwise refuse everything, and a zeroed concurrency would deadlock the run outright - so both are pulled into range instead. An empty format filter is read as "everything", because switching every format off is far more likely to be a slip than an instruction to collect nothing. The media root has an ordering problem - it is a setting that decides the paths the container is built from - so the file is read once before the container exists rather than making every path lazy for one value. Purge is scoped to a source and lives on the Collect page, where the source is already chosen. Content another source also holds survives, which is what the index's reference count was for. The showcase hint says out loud what a hard link means: editing the browsable copy edits the original, and deleting it frees nothing until the last name goes. That is surprising enough to belong in the UI rather than only in the code. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
70fb3a1df3
commit
fe62bcf53f
@@ -1,3 +1,4 @@
|
||||
using AvParser.Core.Collecting;
|
||||
using AvParser.Core.Proxies;
|
||||
using AvParser.Core.Settings;
|
||||
using AvParser.Infrastructure.Settings;
|
||||
@@ -79,6 +80,90 @@ public sealed class JsonSettingsServiceTests : IDisposable
|
||||
settings.MinimumLogLevel.ShouldBe("Information");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The same guarantee, for the collector settings added afterwards.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The failure this prevents is worse here than it was for the proxy pool: a zeroed
|
||||
/// <c>MaxItemBytes</c> would refuse everything, and a zeroed concurrency would deadlock the
|
||||
/// run outright.
|
||||
/// </remarks>
|
||||
[Fact]
|
||||
public void A_file_that_predates_the_collector_keeps_the_collector_defaults()
|
||||
{
|
||||
WriteSettings("""{ "theme": "Dark", "proxyMinimumLive": 25 }""");
|
||||
|
||||
using var service = Create();
|
||||
var settings = service.Current;
|
||||
|
||||
settings.MaxConcurrentDownloads.ShouldBe(4);
|
||||
settings.MaxConcurrentPerHost.ShouldBe(2);
|
||||
settings.HostDelayMs.ShouldBe(250);
|
||||
settings.MaxItemBytes.ShouldBe(33_554_432);
|
||||
settings.MinItemBytes.ShouldBe(1024);
|
||||
settings.MaxRedirects.ShouldBe(5);
|
||||
settings.ConnectTimeoutSeconds.ShouldBe(15);
|
||||
settings.HeaderTimeoutSeconds.ShouldBe(30);
|
||||
settings.IdleTimeoutSeconds.ShouldBe(20);
|
||||
settings.AllowedMediaKinds.ShouldBe(MediaKindFilter.All);
|
||||
settings.ShowcaseMode.ShouldBe(ShowcaseMode.HardLink);
|
||||
settings.CollectUserAgent.ShouldNotBeNullOrWhiteSpace();
|
||||
|
||||
// And what the file did carry survives.
|
||||
settings.ProxyMinimumLive.ShouldBe(25);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Collect_options_from_an_older_file_are_usable()
|
||||
{
|
||||
WriteSettings("""{ "theme": "Dark" }""");
|
||||
|
||||
using var service = Create();
|
||||
var options = service.Current.ToCollectOptions();
|
||||
|
||||
options.MaxConcurrentDownloads.ShouldBeGreaterThan(0);
|
||||
options.MaxItemBytes.ShouldBeGreaterThan(0);
|
||||
options.AllowedKinds.ShouldBe(MediaKindFilter.All);
|
||||
options.IdleTimeout.ShouldBeGreaterThan(TimeSpan.Zero);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Collector_values_out_of_range_are_clamped_rather_than_thrown()
|
||||
{
|
||||
var options = new AppSettings(
|
||||
MaxConcurrentDownloads: 0,
|
||||
MaxConcurrentPerHost: -5,
|
||||
MaxItemBytes: 0,
|
||||
MaxRedirects: 9999,
|
||||
IdleTimeoutSeconds: 0,
|
||||
CollectUserAgent: " "
|
||||
).ToCollectOptions();
|
||||
|
||||
options.MaxConcurrentDownloads.ShouldBe(1);
|
||||
options.MaxConcurrentPerHost.ShouldBe(1);
|
||||
options.MaxItemBytes.ShouldBe(1024);
|
||||
options.MaxRedirects.ShouldBe(20);
|
||||
options.IdleTimeout.ShouldBe(TimeSpan.FromSeconds(1));
|
||||
options.UserAgent.ShouldBe("AvParser/0.1");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void An_empty_media_filter_is_treated_as_everything()
|
||||
{
|
||||
// Switching every format off is far more likely to be an accident than an instruction to
|
||||
// collect nothing at all.
|
||||
new AppSettings(AllowedMediaKinds: MediaKindFilter.None)
|
||||
.ToCollectOptions()
|
||||
.AllowedKinds.ShouldBe(MediaKindFilter.All);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void The_proxy_gate_setting_reaches_the_collector()
|
||||
{
|
||||
new AppSettings(AllowDirectConnection: false).ToCollectOptions().RequireProxy.ShouldBeTrue();
|
||||
new AppSettings(AllowDirectConnection: true).ToCollectOptions().RequireProxy.ShouldBeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Options_built_from_an_older_file_still_consult_the_feed()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user