using Microsoft.Extensions.Options; using Service.AI; using Service.AppSettingModel; namespace WebApi.Test.Service; /// /// ClassificationIconPromptProvider 单元测试 /// public class ClassificationIconPromptProviderTest : BaseTest { private readonly ClassificationIconPromptProvider _provider; private readonly ILogger _logger; private readonly IOptions _config; public ClassificationIconPromptProviderTest() { _logger = Substitute.For>(); _config = Options.Create(new IconPromptSettings { EnableNewPrompt = true, GrayScaleRatio = 1.0, StyleStrength = 0.7, ColorScheme = "single-color" }); _provider = new ClassificationIconPromptProvider(_logger, _config); } #region GetPrompt Tests [Fact] public void GetPrompt_支出分类_应包含正确的上下文信息() { // Arrange const string categoryName = "餐饮"; const TransactionType categoryType = TransactionType.Expense; // Act var prompt = _provider.GetPrompt(categoryName, categoryType); // Assert prompt.Should().Contain(categoryName); prompt.Should().Contain("支出"); prompt.Should().Contain("分类名称"); prompt.Should().Contain("分类类型"); } [Fact] public void GetPrompt_收入分类_应包含正确的上下文信息() { // Arrange const string categoryName = "工资"; const TransactionType categoryType = TransactionType.Income; // Act var prompt = _provider.GetPrompt(categoryName, categoryType); // Assert prompt.Should().Contain(categoryName); prompt.Should().Contain("收入"); prompt.Should().Contain("分类名称"); prompt.Should().Contain("分类类型"); } [Fact] public void GetPrompt_不计入收支分类_应包含正确的上下文信息() { // Arrange const string categoryName = "转账"; const TransactionType categoryType = TransactionType.None; // Act var prompt = _provider.GetPrompt(categoryName, categoryType); // Assert prompt.Should().Contain(categoryName); prompt.Should().Contain("不计入收支"); } [Fact] public void GetPrompt_应包含设计要求() { // Arrange const string categoryName = "交通"; const TransactionType categoryType = TransactionType.Expense; // Act var prompt = _provider.GetPrompt(categoryName, categoryType); // Assert prompt.Should().Contain("24x24"); prompt.Should().Contain("viewBox=\"0 0 24 24\""); prompt.Should().Contain("扁平化、单色、极致简约"); prompt.Should().Contain("避免渐变和阴影"); prompt.Should().Contain("保持线条简洁"); prompt.Should().Contain("高对比度"); } [Fact] public void GetPrompt_应包含SVG格式要求() { // Arrange const string categoryName = "购物"; const TransactionType categoryType = TransactionType.Expense; // Act var prompt = _provider.GetPrompt(categoryName, categoryType); // Assert prompt.Should().Contain("JSON 数组格式"); prompt.Should().Contain("5 个完整的 SVG 字符串"); } [Fact] public void GetPrompt_应包含返回格式说明() { // Arrange const string categoryName = "娱乐"; const TransactionType categoryType = TransactionType.Expense; // Act var prompt = _provider.GetPrompt(categoryName, categoryType); // Assert prompt.Should().Contain("[\"...\", \"...\", \"...\", \"...\", \"...\"]"); } [Fact] public void GetPrompt_多次调用_应生成相同的提示词结构() { // Arrange const string categoryName = "餐饮"; const TransactionType categoryType = TransactionType.Expense; // Act var prompt1 = _provider.GetPrompt(categoryName, categoryType); var prompt2 = _provider.GetPrompt(categoryName, categoryType); // Assert prompt1.Should().Be(prompt2); } [Fact] public void GetPrompt_不同分类_应生成不同的提示词() { // Arrange const string categoryName1 = "餐饮"; const string categoryName2 = "交通"; const TransactionType categoryType = TransactionType.Expense; // Act var prompt1 = _provider.GetPrompt(categoryName1, categoryType); var prompt2 = _provider.GetPrompt(categoryName2, categoryType); // Assert prompt1.Should().NotBe(prompt2); prompt1.Should().Contain(categoryName1); prompt2.Should().Contain(categoryName2); } #endregion #region GetSingleIconPrompt Tests [Fact] public void GetSingleIconPrompt_支出分类_应包含正确的上下文信息() { // Arrange const string categoryName = "餐饮"; const TransactionType categoryType = TransactionType.Expense; // Act var prompt = _provider.GetSingleIconPrompt(categoryName, categoryType); // Assert prompt.Should().Contain(categoryName); prompt.Should().Contain("支出"); prompt.Should().Contain("分类名称"); prompt.Should().Contain("分类类型"); } [Fact] public void GetSingleIconPrompt_应包含单个图标要求() { // Arrange const string categoryName = "交通"; const TransactionType categoryType = TransactionType.Expense; // Act var prompt = _provider.GetSingleIconPrompt(categoryName, categoryType); // Assert prompt.Should().Contain("生成 1 个"); prompt.Should().Contain("24x24"); prompt.Should().Contain("viewBox=\"0 0 24 24\""); prompt.Should().Contain("扁平化、单色、极致简约"); } [Fact] public void GetSingleIconPrompt_应包含返回格式说明() { // Arrange const string categoryName = "购物"; const TransactionType categoryType = TransactionType.Expense; // Act var prompt = _provider.GetSingleIconPrompt(categoryName, categoryType); // Assert prompt.Should().Contain("JSON 数组格式"); prompt.Should().Contain("1 个完整的 SVG 字符串"); prompt.Should().Contain("[\"...\"]"); } [Fact] public void GetSingleIconPrompt_应与GetPrompt生成不同的提示词() { // Arrange const string categoryName = "餐饮"; const TransactionType categoryType = TransactionType.Expense; // Act var singlePrompt = _provider.GetSingleIconPrompt(categoryName, categoryType); var multiPrompt = _provider.GetPrompt(categoryName, categoryType); // Assert singlePrompt.Should().NotBe(multiPrompt); singlePrompt.Should().Contain("生成 1 个"); multiPrompt.Should().Contain("生成 5 个"); } #endregion #region PromptTemplateEngine Tests (3.1) [Fact] public void PromptTemplateEngine_ReplacePlaceholders_应正确替换单个占位符() { // Arrange var template = "分类名称:{{category_name}},类型:{{category_type}}"; var placeholders = new Dictionary { ["category_name"] = "餐饮", ["category_type"] = "支出" }; // Act var result = PromptTemplateEngine.ReplacePlaceholders(template, placeholders); // Assert result.Should().Be("分类名称:餐饮,类型:支出"); } [Fact] public void PromptTemplateEngine_ReplacePlaceholders_应正确替换多个占位符() { // Arrange var template = "颜色方案:{{color_scheme}},简约度:{{style_strength}}"; var placeholders = new Dictionary { ["color_scheme"] = "single-color", ["style_strength"] = "0.8 - 高度简约" }; // Act var result = PromptTemplateEngine.ReplacePlaceholders(template, placeholders); // Assert result.Should().Be("颜色方案:single-color,简约度:0.8 - 高度简约"); } [Fact] public void PromptTemplateEngine_ReplacePlaceholders_空模板应返回原字符串() { // Arrange var template = string.Empty; var placeholders = new Dictionary(); // Act var result = PromptTemplateEngine.ReplacePlaceholders(template, placeholders); // Assert result.Should().Be(string.Empty); } [Fact] public void PromptTemplateEngine_ReplacePlaceholders_空占位符字典应返回原字符串() { // Arrange var template = "测试模板字符串"; var placeholders = new Dictionary(); // Act var result = PromptTemplateEngine.ReplacePlaceholders(template, placeholders); // Assert result.Should().Be("测试模板字符串"); } #endregion #region 新版提示词生成逻辑测试 (3.2) [Fact] public void GetPrompt_新版提示词_应包含简约风格要求() { // Arrange const string categoryName = "餐饮"; const TransactionType categoryType = TransactionType.Expense; // Act var prompt = _provider.GetPrompt(categoryName, categoryType); // Assert prompt.Should().Contain("扁平化、单色、极致简约"); prompt.Should().Contain("避免渐变和阴影"); prompt.Should().Contain("保持线条简洁"); prompt.Should().Contain("高对比度"); } [Fact] public void GetPrompt_新版提示词_应包含颜色方案占位符替换() { // Arrange const string categoryName = "交通"; const TransactionType categoryType = TransactionType.Expense; // Act var prompt = _provider.GetPrompt(categoryName, categoryType); // Assert prompt.Should().Contain("颜色方案:single-color"); } [Fact] public void GetPrompt_新版提示词_应包含风格强度描述() { // Arrange const string categoryName = "娱乐"; const TransactionType categoryType = TransactionType.Expense; // Act var prompt = _provider.GetPrompt(categoryName, categoryType); // Assert prompt.Should().Contain("简约度:0.7"); prompt.Should().Contain("高度简约(去除所有装饰)"); } #endregion #region 抽象分类特殊处理测试 (3.3) [Fact] public void IconPromptSettings_初始化_应包含默认抽象分类配置() { // Arrange & Act var config = new IconPromptSettings(); // Assert config.AbstractCategories.Should().ContainKey("其他"); config.AbstractCategories.Should().ContainKey("通用"); config.AbstractCategories.Should().ContainKey("未知"); config.AbstractCategories["其他"].GeometryShape.Should().Be("circle"); config.AbstractCategories["其他"].ColorCode.Should().Be("#9E9E9E"); } [Fact] public void IconPromptSettings_抽象分类配置_可以自定义() { // Arrange var config = new IconPromptSettings(); // Act config.AbstractCategories["自定义分类"] = new AbstractCategoryConfig { GeometryShape = "hexagon", ColorCode = "#FF5722" }; // Assert config.AbstractCategories["自定义分类"].GeometryShape.Should().Be("hexagon"); config.AbstractCategories["自定义分类"].ColorCode.Should().Be("#FF5722"); } #endregion #region 风格强度参数注入测试 (3.4) [Fact] public void PromptTemplateEngine_ReplaceForIconGeneration_风格强度09_应显示极度简约() { // Arrange var template = "简约度:{{style_strength}}"; // Act var result = PromptTemplateEngine.ReplaceForIconGeneration( template, "餐饮", "支出", "single-color", 0.95); // Assert result.Should().Contain("0.9"); result.Should().Contain("极度简约(仅保留最核心元素)"); } [Fact] public void PromptTemplateEngine_ReplaceForIconGeneration_风格强度07_应显示高度简约() { // Arrange var template = "简约度:{{style_strength}}"; // Act var result = PromptTemplateEngine.ReplaceForIconGeneration( template, "交通", "支出", "single-color", 0.7); // Assert result.Should().Contain("0.7"); result.Should().Contain("高度简约(去除所有装饰)"); } [Fact] public void PromptTemplateEngine_ReplaceForIconGeneration_风格强度05_应显示简约() { // Arrange var template = "简约度:{{style_strength}}"; // Act var result = PromptTemplateEngine.ReplaceForIconGeneration( template, "购物", "支出", "single-color", 0.5); // Assert result.Should().Contain("0.5"); result.Should().Contain("简约(保留必要细节)"); } [Fact] public void PromptTemplateEngine_ReplaceForIconGeneration_风格强度03_应显示适中() { // Arrange var template = "简约度:{{style_strength}}"; // Act var result = PromptTemplateEngine.ReplaceForIconGeneration( template, "娱乐", "支出", "single-color", 0.3); // Assert result.Should().Contain("0.3"); result.Should().Contain("适中"); } #endregion }