f3b89ca9d7
One presentational SeoHead renders the full OG/Twitter/JSON-LD head surface at prerender via typed schema.org builders. Per-medium release schema, config-sourced canonicals, 404 noindex. Zero CMS change.
69 lines
2.3 KiB
C#
69 lines
2.3 KiB
C#
using DeepDrftPublic.Client.Common;
|
|
|
|
namespace DeepDrftTests;
|
|
|
|
/// <summary>
|
|
/// Unit tests for <see cref="SeoUrls"/> — 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.
|
|
/// </summary>
|
|
[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);
|
|
}
|
|
}
|