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
}