using DeepDrftPublic.Client.Common; namespace DeepDrftTests; /// /// Unit tests for — the absolute-URL composition shared by the SeoModel factories /// and SeoHead (Phase 22). Origin always comes from config (never a browser API), so these pin the /// slash-join, the cover-vs-default fallback (C6/AC4), and the ISO-8601 duration edge cases. /// [TestFixture] public class SeoUrlsTests { private static readonly SeoOptions Options = new() { BaseUrl = "https://deepdrft.com", DefaultImageUrl = "/img/og-default.png", }; [TestCase("/cuts/key", "https://deepdrft.com/cuts/key")] [TestCase("cuts/key", "https://deepdrft.com/cuts/key")] [TestCase("/", "https://deepdrft.com/")] [TestCase("", "https://deepdrft.com")] public void Absolute_JoinsOriginAndPath_WithoutDoublingOrDroppingSlash(string path, string expected) { Assert.That(SeoUrls.Absolute(Options, path), Is.EqualTo(expected)); } [Test] public void Absolute_TrimsTrailingSlashOnBaseUrl() { var withSlash = Options with { BaseUrl = "https://deepdrft.com/" }; Assert.That(SeoUrls.Absolute(withSlash, "/cuts"), Is.EqualTo("https://deepdrft.com/cuts")); } [Test] public void CoverOrDefault_WithCover_BuildsEscapedImageRoute() { Assert.That(SeoUrls.CoverOrDefault(Options, "my cover.jpg"), Is.EqualTo("https://deepdrft.com/api/image/my%20cover.jpg")); } [TestCase(null)] [TestCase("")] [TestCase(" ")] public void CoverOrDefault_WithoutCover_FallsBackToDefaultImage(string? image) { Assert.That(SeoUrls.CoverOrDefault(Options, image), Is.EqualTo("https://deepdrft.com/img/og-default.png")); } [TestCase(30.0, "PT30S")] [TestCase(90.0, "PT1M30S")] [TestCase(3723.0, "PT1H2M3S")] public void IsoDuration_PositiveSeconds_RendersIso8601(double seconds, string expected) { Assert.That(SeoUrls.IsoDuration(seconds), Is.EqualTo(expected)); } [TestCase(null)] [TestCase(0.0)] [TestCase(-5.0)] [TestCase(double.NaN)] [TestCase(double.PositiveInfinity)] public void IsoDuration_NonPositiveOrNonFinite_ReturnsNull(double? seconds) { Assert.That(SeoUrls.IsoDuration(seconds), Is.Null); } }