468 lines
14 KiB
C#
468 lines
14 KiB
C#
using Microsoft.Extensions.Options;
|
||
using Service.AI;
|
||
using Service.AppSettingModel;
|
||
|
||
namespace WebApi.Test.Service;
|
||
|
||
/// <summary>
|
||
/// ClassificationIconPromptProvider 单元测试
|
||
/// </summary>
|
||
public class ClassificationIconPromptProviderTest : BaseTest
|
||
{
|
||
private readonly ClassificationIconPromptProvider _provider;
|
||
private readonly ILogger<ClassificationIconPromptProvider> _logger;
|
||
private readonly IOptions<IconPromptSettings> _config;
|
||
|
||
public ClassificationIconPromptProviderTest()
|
||
{
|
||
_logger = Substitute.For<ILogger<ClassificationIconPromptProvider>>();
|
||
_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("[\"<svg>...</svg>\", \"<svg>...</svg>\", \"<svg>...</svg>\", \"<svg>...</svg>\", \"<svg>...</svg>\"]");
|
||
}
|
||
|
||
[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("[\"<svg>...</svg>\"]");
|
||
}
|
||
|
||
[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<string, string>
|
||
{
|
||
["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<string, string>
|
||
{
|
||
["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<string, string>();
|
||
|
||
// 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<string, string>();
|
||
|
||
// 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
|
||
}
|