using System.Linq;
using Dpz.Core.Entity.Base.Image;
using Dpz.Core.Public.ViewModel.Request;
using Dpz.Core.Service.Mediator.Features.Media.Contracts;

namespace Dpz.Core.ServiceTest;

[TestFixture]
public class ImageMetadataBackfillHelperTest
{
    [Test]
    public void CreateCandidate_WhenAllUrlsHaveMetadata_ReturnsNull()
    {
        var candidate = ImageMetadataBackfillHelper.CreateCandidate(
            "id",
            ["https://cdn.example.com/a.jpg"],
            [
                new ImageMetadata
                {
                    Url = "https://cdn.example.com/a.jpg",
                    Width = 100,
                    Height = 100,
                },
            ]
        );

        Assert.That(candidate, Is.Null);
    }

    [Test]
    public void CreateCandidate_WhenForced_ReturnsCandidateEvenIfAllUrlsHaveMetadata()
    {
        var candidate = ImageMetadataBackfillHelper.CreateCandidate(
            "id",
            ["https://cdn.example.com/a.jpg"],
            [
                new ImageMetadata
                {
                    Url = "https://cdn.example.com/a.jpg",
                    Width = 100,
                    Height = 100,
                },
            ],
            forceCreate: true
        );

        Assert.That(candidate, Is.Not.Null);
        Assert.That(candidate.Urls, Is.EqualTo(["https://cdn.example.com/a.jpg"]));
    }

    [Test]
    public void CreateCandidate_WhenAnyUrlMissesMetadata_ReturnsAllTargetUrls()
    {
        var candidate = ImageMetadataBackfillHelper.CreateCandidate(
            "id",
            ["https://cdn.example.com/a.jpg", "https://cdn.example.com/b.jpg"],
            [
                new ImageMetadata
                {
                    Url = "https://cdn.example.com/a.jpg",
                    Width = 100,
                    Height = 100,
                },
            ]
        );

        Assert.That(candidate, Is.Not.Null);
        Assert.That(
            candidate.Urls,
            Is.EqualTo(["https://cdn.example.com/a.jpg", "https://cdn.example.com/b.jpg"])
        );
    }

    [Test]
    public void FilterObjectStorageUrls_WhenHostMissing_ReturnsEmptyList()
    {
        var urls = ImageMetadataBackfillHelper.FilterObjectStorageUrls(
            ["https://cdn.example.com/a.jpg"],
            ""
        );

        Assert.That(urls, Is.Empty);
    }

    [Test]
    public void FilterObjectStorageUrls_OnlyReturnsUrlsUnderConfiguredHost()
    {
        var urls = ImageMetadataBackfillHelper.FilterObjectStorageUrls(
            [
                "https://cdn.example.com/a.jpg",
                "https://cdn.example.com.evil/a.jpg",
                "https://other.example.com/a.jpg",
            ],
            "https://cdn.example.com/"
        );

        Assert.That(urls, Is.EqualTo(new[] { "https://cdn.example.com/a.jpg" }));
    }

    [Test]
    public void MergeByUrl_PreservesTargetOrderAndCollectedMetadataWins()
    {
        var result = ImageMetadataBackfillHelper.MergeByUrl(
            ["https://cdn.example.com/b.jpg", "https://cdn.example.com/a.jpg"],
            [
                new ImageMetadata
                {
                    Url = "https://cdn.example.com/a.jpg",
                    Width = 100,
                    Height = 100,
                },
                new ImageMetadata
                {
                    Url = "https://cdn.example.com/b.jpg",
                    Width = 200,
                    Height = 200,
                },
            ],
            [
                new ImageMetadata
                {
                    Url = "https://cdn.example.com/a.jpg",
                    Width = 300,
                    Height = 300,
                },
            ]
        );

        Assert.That(
            result.Select(x => x.Url),
            Is.EqualTo(new[] { "https://cdn.example.com/b.jpg", "https://cdn.example.com/a.jpg" })
        );
        Assert.That(result[1].Width, Is.EqualTo(300));
    }

    [Test]
    public void EditMumbleRequest_ImagesDefaultIsNullAndEmptyListMeansClear()
    {
        var keepExisting = new EditMumbleRequest { Id = "id", Markdown = "content" };
        var clear = new EditMumbleRequest
        {
            Id = "id",
            Markdown = "content",
            Images = [],
        };

        Assert.That(keepExisting.Images, Is.Null);
        Assert.That(clear.Images, Is.Empty);
    }
}
⚠⚠    以下内容为AI分析的结果,请根据实际情况进行判断。

代码说明

这是一个 C# 单元测试类,用于测试 ImageMetadataBackfillHelper 工具类的功能。该工具类主要用于处理图片元数据的回填(backfill)操作。

测试类概述

类名: ImageMetadataBackfillHelperTest
测试框架: NUnit(通过 [TestFixture] 和 [Test] 特性标识)

各测试方法说明

1. CreateCandidate_WhenAllUrlsHaveMetadata_ReturnsNull

  • 测试场景: 当所有 URL 都已有元数据时
  • 预期结果: 返回 null(无需回填)
  • 验证逻辑: 传入的 URL 已经存在对应的元数据,因此不需要创建候选项

2. CreateCandidate_WhenForced_ReturnsCandidateEvenIfAllUrlsHaveMetadata

  • 测试场景: 强制创建模式下,即使所有 URL 已有元数据
  • 预期结果: 仍然返回候选项(forceCreate: true 参数的作用)
  • 验证逻辑: 候选项不为 null,且包含指定的 URL

3. CreateCandidate_WhenAnyUrlMissesMetadata_ReturnsAllTargetUrls

  • 测试场景: 当任意 URL 缺少元数据时
  • 预期结果: 返回包含所有目标 URL 的候选项
  • 验证逻辑: 两个 URL 中只有一个有元数据,应返回包含两个 URL 的候选项

4. FilterObjectStorageUrls_WhenHostMissing_ReturnsEmptyList

  • 测试场景: 对象存储主机地址为空时
  • 预期结果: 返回空列表
  • 验证逻辑: 没有配置主机地址,无法过滤,返回空

5. FilterObjectStorageUrls_OnlyReturnsUrlsUnderConfiguredHost

  • 测试场景: 只返回匹配配置主机的 URL
  • 预期结果: 过滤掉不匹配的 URL(防止恶意 URL 或其他域名)
  • 验证逻辑: 三个 URL 中只有严格匹配 cdn.example.com 的一个被保留

6. MergeByUrl_PreservesTargetOrderAndCollectedMetadataWins

  • 测试场景: 合并元数据时的优先级和顺序处理
  • 预期结果:
    • 保持目标 URL 的原始顺序
    • 新收集的元数据优先于旧数据(覆盖策略)
  • 验证逻辑: URL 按 b、a 顺序排列,且 a 的宽度被更新为 300(新值)

7. EditMumbleRequest_ImagesDefaultIsNullAndEmptyListMeansClear

  • 测试场景: 测试 EditMumbleRequest 的 Images 属性语义
  • 预期结果:
    • null 表示保持现有图片
    • 空列表 [] 表示清空图片
  • 验证逻辑: 这是一个重要的 API 设计约定测试

核心业务逻辑

该测试类验证的功能围绕:

  1. 智能回填: 只为缺失元数据的图片创建回填任务
  2. URL 过滤: 安全地只处理指定域名下的图片
  3. 元数据合并: 正确合并旧数据和新收集的数据
  4. API 语义: 确保请求对象的字段语义明确(null vs 空集合)

这是一个典型的数据处理工具类测试,关注边界条件和业务规则的正确性。

评论加载中...