Build ErsatzTV Image / CI image pin matches docker/ci (pull_request) Successful in 13s
Build ErsatzTV Image / Docs update reminder (pull_request) Successful in 13s
Build ErsatzTV Image / decisions.md append-only (pull_request) Successful in 14s
Build CI Toolchain Image / Build & push CI image (push) Successful in 1m39s
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (pull_request) Successful in 17s
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (pull_request) Successful in 16s
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 7m56s
Build ErsatzTV Image / Functional E2E (curl contracts) (pull_request) Successful in 15m2s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 17m48s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
Adversarial review found a documentation defect, not a code one: both docs/decisions.md and the WatermarkSelector comment asserted the deco path was unaffected by this change. That is true of the *resolution* half and false of the *routing* half. SelectWatermarks puts deco-derived options into the same list the routing guard filters, so a deco watermark whose resolved path is a URL is rerouted to the graphics engine too — including the generated-initials localhost URL, which only the deco path still emits and which plausibly rendered through ffmpeg before. That reroute is intended (routing by what the path is beats routing by provenance, which would drift), so the fix is to say so accurately rather than to narrow the guard. Also records the accepted per-frame cost asymmetry the entry previously argued on correctness grounds alone. The guard is extracted as CanUseFFmpegNativeWatermark so it can be tested directly — review's highest-value gap was that the half of the fix which decides whether pixels appear had no automated coverage, only the one-off live E2E. Nine cases pin it, including the localhost-fallback reroute. Both deferrals now point at real issues instead of an unverifiable "tracked separately": #510 (deco vs precedence-level missing-logo policy) and #511 (remote-fetch hardening — timeout, size cap, redirects, pooling, caching, SSRF). Also pins scheme-case insensitivity in the selector.
78 lines
2.7 KiB
C#
78 lines
2.7 KiB
C#
using ErsatzTV.Core.Domain;
|
|
using ErsatzTV.Core.FFmpeg;
|
|
using NUnit.Framework;
|
|
using Shouldly;
|
|
|
|
namespace ErsatzTV.Core.Tests.FFmpeg;
|
|
|
|
/// <summary>
|
|
/// Pins which watermarks ffmpeg may carry natively and which must go to the graphics engine.
|
|
/// The remote-URL rule is the second half of the #502 fix: resolving the URL is useless if the
|
|
/// resolved path is then handed to ffmpeg as a bare <c>-i</c> argument.
|
|
/// </summary>
|
|
[TestFixture]
|
|
public class FFmpegNativeWatermarkRoutingTests
|
|
{
|
|
private const string LocalPath = "/cache/logos/ab/abc123.png";
|
|
|
|
private static WatermarkOptions Options(
|
|
string imagePath,
|
|
ChannelWatermarkMode mode = ChannelWatermarkMode.Permanent) =>
|
|
new(new ChannelWatermark { Id = 1, Name = "wm", Mode = mode }, imagePath, Option<int>.None);
|
|
|
|
[Test]
|
|
public void Local_Path_Single_Permanent_Watermark_Uses_FFmpeg()
|
|
{
|
|
FFmpegLibraryProcessService.CanUseFFmpegNativeWatermark(0, [Options(LocalPath)])
|
|
.ShouldBeTrue();
|
|
}
|
|
|
|
[TestCase("https://cdn.example.com/logos/channel.png")]
|
|
[TestCase("http://cdn.example.com/logos/channel.png")]
|
|
public void Remote_Url_Watermark_Goes_To_Graphics_Engine(string url)
|
|
{
|
|
FFmpegLibraryProcessService.CanUseFFmpegNativeWatermark(0, [Options(url)])
|
|
.ShouldBeFalse();
|
|
}
|
|
|
|
/// <summary>
|
|
/// The generated-initials fallback is a localhost URL. Only the deco path still emits it, and it is
|
|
/// routed by its resolved path like any other URL — see the #502 entry in docs/decisions.md and #510.
|
|
/// </summary>
|
|
[Test]
|
|
public void Generated_Localhost_Logo_Url_Goes_To_Graphics_Engine()
|
|
{
|
|
FFmpegLibraryProcessService
|
|
.CanUseFFmpegNativeWatermark(0, [Options("http://localhost:8409/iptv/logos/gen?text=Test")])
|
|
.ShouldBeFalse();
|
|
}
|
|
|
|
[Test]
|
|
public void Graphics_Elements_Present_Goes_To_Graphics_Engine()
|
|
{
|
|
FFmpegLibraryProcessService.CanUseFFmpegNativeWatermark(1, [Options(LocalPath)])
|
|
.ShouldBeFalse();
|
|
}
|
|
|
|
[Test]
|
|
public void Multiple_Watermarks_Go_To_Graphics_Engine()
|
|
{
|
|
FFmpegLibraryProcessService.CanUseFFmpegNativeWatermark(0, [Options(LocalPath), Options(LocalPath)])
|
|
.ShouldBeFalse();
|
|
}
|
|
|
|
[Test]
|
|
public void No_Watermarks_Does_Not_Use_FFmpeg()
|
|
{
|
|
FFmpegLibraryProcessService.CanUseFFmpegNativeWatermark(0, []).ShouldBeFalse();
|
|
}
|
|
|
|
[TestCase(ChannelWatermarkMode.Intermittent)]
|
|
[TestCase(ChannelWatermarkMode.None)]
|
|
public void Non_Permanent_Watermark_Goes_To_Graphics_Engine(ChannelWatermarkMode mode)
|
|
{
|
|
FFmpegLibraryProcessService.CanUseFFmpegNativeWatermark(0, [Options(LocalPath, mode)])
|
|
.ShouldBeFalse();
|
|
}
|
|
}
|