refactor: 重命名 backend-single 目录为 server
This commit is contained in:
@@ -0,0 +1,374 @@
|
||||
package com.emotion.service;
|
||||
|
||||
import com.emotion.dto.request.EpicScriptCreateRequest;
|
||||
import com.emotion.entity.AiConfig;
|
||||
import com.emotion.service.impl.EpicScriptServiceImpl;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* EpicScriptServiceImpl测试类
|
||||
* 包含属性测试,验证输入组装完整性等
|
||||
* 使用贴近真实业务场景的测试数据
|
||||
*
|
||||
* Feature: coze-ai-integration
|
||||
*
|
||||
* @author system
|
||||
* @date 2025-12-23
|
||||
*/
|
||||
@SpringBootTest
|
||||
@ActiveProfiles("local")
|
||||
public class EpicScriptServiceImplTest {
|
||||
|
||||
@Autowired
|
||||
private EpicScriptService epicScriptService;
|
||||
|
||||
@Autowired
|
||||
private AiConfigService aiConfigService;
|
||||
|
||||
private Random random;
|
||||
|
||||
/**
|
||||
* 爽文剧本生成的配置键
|
||||
*/
|
||||
private static final String EPIC_SCRIPT_CONFIG_KEY = "coze.course.life.generate";
|
||||
|
||||
/**
|
||||
* 真实的剧本标题示例
|
||||
*/
|
||||
private static final List<String> SAMPLE_TITLES = Arrays.asList(
|
||||
"逆袭人生:从底层到巅峰",
|
||||
"命运转折点",
|
||||
"我的职场逆袭之路",
|
||||
"重生之商业帝国",
|
||||
"平凡人的不平凡故事",
|
||||
"从零开始的创业传奇",
|
||||
"人生赢家养成记",
|
||||
"绝地反击"
|
||||
);
|
||||
|
||||
/**
|
||||
* 真实的主题/渴望示例
|
||||
*/
|
||||
private static final List<String> SAMPLE_THEMES = Arrays.asList(
|
||||
"成为行业领袖,实现财务自由",
|
||||
"找到真爱,拥有幸福家庭",
|
||||
"突破自我,成就非凡人生",
|
||||
"获得认可,证明自己的价值",
|
||||
"改变命运,逆转人生轨迹",
|
||||
"实现梦想,活出精彩人生"
|
||||
);
|
||||
|
||||
/**
|
||||
* 真实的序幕(低谷回响)示例
|
||||
*/
|
||||
private static final List<String> SAMPLE_PLOT_INTROS = Arrays.asList(
|
||||
"我是一个普通的上班族,每天朝九晚五,工资勉强够生活。公司裁员名单上赫然出现了我的名字。",
|
||||
"大学毕业后,我满怀憧憬来到大城市,却发现现实远比想象残酷。租住在狭小的地下室,每天为生计发愁。",
|
||||
"创业失败后,我背负着巨额债务,朋友疏远,家人失望。站在人生的最低谷,我不知道该何去何从。",
|
||||
"三十岁了,事业无成,感情空白。看着同龄人都已成家立业,我感到前所未有的迷茫和焦虑。"
|
||||
);
|
||||
|
||||
/**
|
||||
* 真实的转折(契机出现)示例
|
||||
*/
|
||||
private static final List<String> SAMPLE_PLOT_TURNINGS = Arrays.asList(
|
||||
"一次偶然的机会,我遇到了一位行业前辈。他的一番话点醒了我,让我看到了新的可能。",
|
||||
"在最绝望的时候,我发现了一个被忽视的市场机会。这可能是改变命运的转折点。",
|
||||
"一封意外的邮件,一个久违的电话,让我重新燃起了希望。原来机会一直都在,只是我没有发现。",
|
||||
"参加了一场行业峰会,结识了志同道合的伙伴。我们决定一起做点不一样的事情。"
|
||||
);
|
||||
|
||||
/**
|
||||
* 真实的高潮(命运抉择)示例
|
||||
*/
|
||||
private static final List<String> SAMPLE_PLOT_CLIMAXES = Arrays.asList(
|
||||
"面对两个选择:稳定但平庸的工作,还是充满风险但可能改变人生的创业机会。我必须做出决定。",
|
||||
"关键时刻,曾经的对手提出了合作邀请。是放下过去携手共进,还是坚持己见独自前行?",
|
||||
"项目进入最关键的阶段,资金链即将断裂。是放弃还是孤注一掷?这个决定将决定一切。",
|
||||
"成功近在咫尺,但代价是牺牲与家人相处的时间。事业与家庭,我该如何抉择?"
|
||||
);
|
||||
|
||||
/**
|
||||
* 真实的结局(新的开始)示例
|
||||
*/
|
||||
private static final List<String> SAMPLE_PLOT_ENDINGS = Arrays.asList(
|
||||
"经过不懈努力,我终于实现了自己的目标。站在新的起点,我知道这只是开始,更精彩的人生还在前方。",
|
||||
"回首来时路,那些曾经的困难都成为了宝贵的财富。我不仅收获了成功,更收获了成长。",
|
||||
"梦想成真的那一刻,我流下了激动的泪水。感谢那个在低谷中没有放弃的自己。",
|
||||
"新的篇章已经开启,我带着过去的经验和教训,向着更高的目标前进。人生,永远充满可能。"
|
||||
);
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
random = new Random();
|
||||
}
|
||||
|
||||
// ==================== Property 3: Input Assembly Completeness ====================
|
||||
// Feature: coze-ai-integration, Property 3: Input Assembly Completeness
|
||||
// Validates: Requirements 4.2
|
||||
|
||||
@Test
|
||||
@DisplayName("Property 3: 验证数据库中存在爽文剧本生成配置")
|
||||
public void testEpicScriptConfigExists() {
|
||||
// 从数据库获取配置
|
||||
AiConfig config = aiConfigService.getByConfigKey(EPIC_SCRIPT_CONFIG_KEY);
|
||||
|
||||
if (config != null) {
|
||||
// 验证配置的必要字段
|
||||
assertNotNull(config.getWorkflowId(), "workflowId不应为null");
|
||||
assertNotNull(config.getApiToken(), "apiToken不应为null");
|
||||
assertNotNull(config.getApiBaseUrl(), "apiBaseUrl不应为null");
|
||||
|
||||
System.out.println("爽文剧本配置验证通过: " + EPIC_SCRIPT_CONFIG_KEY);
|
||||
System.out.println(" workflowId: " + config.getWorkflowId());
|
||||
System.out.println(" apiBaseUrl: " + config.getApiBaseUrl());
|
||||
} else {
|
||||
System.out.println("警告:未找到配置 " + EPIC_SCRIPT_CONFIG_KEY + ",请先在数据库中添加配置");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Property 3: 输入组装完整性 - 验证所有非空字段都被包含在输入中")
|
||||
public void testInputAssemblyCompleteness() throws Exception {
|
||||
// 使用反射获取私有方法
|
||||
Method assembleMethod = EpicScriptServiceImpl.class.getDeclaredMethod(
|
||||
"assembleScriptInput", EpicScriptCreateRequest.class);
|
||||
assembleMethod.setAccessible(true);
|
||||
|
||||
// 使用真实业务数据进行多次测试
|
||||
IntStream.range(0, 10).forEach(i -> {
|
||||
try {
|
||||
// 从样本数据中随机选择真实业务数据
|
||||
String title = getRandomSample(SAMPLE_TITLES);
|
||||
String theme = getRandomSample(SAMPLE_THEMES);
|
||||
String style = getRandomStyle();
|
||||
String length = getRandomLength();
|
||||
String plotIntro = getRandomSample(SAMPLE_PLOT_INTROS);
|
||||
String plotTurning = getRandomSample(SAMPLE_PLOT_TURNINGS);
|
||||
String plotClimax = getRandomSample(SAMPLE_PLOT_CLIMAXES);
|
||||
String plotEnding = getRandomSample(SAMPLE_PLOT_ENDINGS);
|
||||
|
||||
// 创建请求对象
|
||||
EpicScriptCreateRequest request = new EpicScriptCreateRequest();
|
||||
request.setTitle(title);
|
||||
request.setTheme(theme);
|
||||
request.setStyle(style);
|
||||
request.setLength(length);
|
||||
request.setPlotIntro(plotIntro);
|
||||
request.setPlotTurning(plotTurning);
|
||||
request.setPlotClimax(plotClimax);
|
||||
request.setPlotEnding(plotEnding);
|
||||
|
||||
String result = (String) assembleMethod.invoke(epicScriptService, request);
|
||||
|
||||
// 验证所有字段都被包含
|
||||
assertTrue(result.contains(title), "输入应包含标题: " + title);
|
||||
assertTrue(result.contains(theme), "输入应包含主题: " + theme);
|
||||
assertTrue(result.contains(plotIntro), "输入应包含序幕");
|
||||
assertTrue(result.contains(plotTurning), "输入应包含转折");
|
||||
assertTrue(result.contains(plotClimax), "输入应包含高潮");
|
||||
assertTrue(result.contains(plotEnding), "输入应包含结局");
|
||||
|
||||
// 验证风格和篇幅描述
|
||||
assertTrue(result.contains("【剧本风格】"), "输入应包含风格标签");
|
||||
assertTrue(result.contains("【篇幅长度】"), "输入应包含篇幅标签");
|
||||
} catch (Exception e) {
|
||||
fail("测试执行失败: " + e.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Property 3: 输入组装 - 验证风格描述转换")
|
||||
public void testStyleDescriptionConversion() throws Exception {
|
||||
Method getStyleDescMethod = EpicScriptServiceImpl.class.getDeclaredMethod(
|
||||
"getStyleDescription", String.class);
|
||||
getStyleDescMethod.setAccessible(true);
|
||||
|
||||
// 验证各种风格的描述转换
|
||||
assertEquals("职场逆袭", getStyleDescMethod.invoke(epicScriptService, "career"));
|
||||
assertEquals("情感圆满", getStyleDescMethod.invoke(epicScriptService, "love"));
|
||||
assertEquals("玄幻觉醒", getStyleDescMethod.invoke(epicScriptService, "fantasy"));
|
||||
assertEquals("unknown", getStyleDescMethod.invoke(epicScriptService, "unknown"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Property 3: 输入组装 - 验证篇幅描述转换")
|
||||
public void testLengthDescriptionConversion() throws Exception {
|
||||
Method getLengthDescMethod = EpicScriptServiceImpl.class.getDeclaredMethod(
|
||||
"getLengthDescription", String.class);
|
||||
getLengthDescMethod.setAccessible(true);
|
||||
|
||||
// 验证各种篇幅的描述转换
|
||||
assertEquals("标准篇", getLengthDescMethod.invoke(epicScriptService, "medium"));
|
||||
assertEquals("长篇", getLengthDescMethod.invoke(epicScriptService, "long"));
|
||||
assertEquals("short", getLengthDescMethod.invoke(epicScriptService, "short"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Property 3: 输入组装 - 验证空字段不被包含")
|
||||
public void testInputAssemblyWithEmptyFields() throws Exception {
|
||||
// 创建只有部分字段的请求
|
||||
EpicScriptCreateRequest request = new EpicScriptCreateRequest();
|
||||
request.setTitle("测试标题");
|
||||
request.setTheme("测试主题");
|
||||
// 其他字段为空
|
||||
|
||||
Method assembleMethod = EpicScriptServiceImpl.class.getDeclaredMethod(
|
||||
"assembleScriptInput", EpicScriptCreateRequest.class);
|
||||
assembleMethod.setAccessible(true);
|
||||
|
||||
String result = (String) assembleMethod.invoke(epicScriptService, request);
|
||||
|
||||
// 验证包含非空字段
|
||||
assertTrue(result.contains("测试标题"), "输入应包含标题");
|
||||
assertTrue(result.contains("测试主题"), "输入应包含主题");
|
||||
|
||||
// 验证不包含空字段的标签
|
||||
assertFalse(result.contains("【序幕-低谷回响】"), "空字段不应被包含");
|
||||
assertFalse(result.contains("【转折-契机出现】"), "空字段不应被包含");
|
||||
assertFalse(result.contains("【高潮-命运抉择】"), "空字段不应被包含");
|
||||
assertFalse(result.contains("【结局-新的开始】"), "空字段不应被包含");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Property 3: 输入组装 - 验证所有字段为空时返回空字符串")
|
||||
public void testInputAssemblyWithAllEmptyFields() throws Exception {
|
||||
EpicScriptCreateRequest request = new EpicScriptCreateRequest();
|
||||
|
||||
Method assembleMethod = EpicScriptServiceImpl.class.getDeclaredMethod(
|
||||
"assembleScriptInput", EpicScriptCreateRequest.class);
|
||||
assembleMethod.setAccessible(true);
|
||||
|
||||
String result = (String) assembleMethod.invoke(epicScriptService, request);
|
||||
|
||||
// 验证返回空字符串
|
||||
assertTrue(result.isEmpty(), "所有字段为空时应返回空字符串");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Property 3: 输入组装 - 随机部分字段填充测试")
|
||||
public void testInputAssemblyWithRandomPartialFields() throws Exception {
|
||||
Method assembleMethod = EpicScriptServiceImpl.class.getDeclaredMethod(
|
||||
"assembleScriptInput", EpicScriptCreateRequest.class);
|
||||
assembleMethod.setAccessible(true);
|
||||
|
||||
// 使用真实业务数据进行多次测试
|
||||
IntStream.range(0, 10).forEach(i -> {
|
||||
try {
|
||||
EpicScriptCreateRequest request = new EpicScriptCreateRequest();
|
||||
|
||||
// 随机决定哪些字段有值,使用真实业务数据
|
||||
String title = random.nextBoolean() ? getRandomSample(SAMPLE_TITLES) : null;
|
||||
String theme = random.nextBoolean() ? getRandomSample(SAMPLE_THEMES) : null;
|
||||
String style = random.nextBoolean() ? getRandomStyle() : null;
|
||||
String length = random.nextBoolean() ? getRandomLength() : null;
|
||||
String plotIntro = random.nextBoolean() ? getRandomSample(SAMPLE_PLOT_INTROS) : null;
|
||||
String plotTurning = random.nextBoolean() ? getRandomSample(SAMPLE_PLOT_TURNINGS) : null;
|
||||
String plotClimax = random.nextBoolean() ? getRandomSample(SAMPLE_PLOT_CLIMAXES) : null;
|
||||
String plotEnding = random.nextBoolean() ? getRandomSample(SAMPLE_PLOT_ENDINGS) : null;
|
||||
|
||||
request.setTitle(title);
|
||||
request.setTheme(theme);
|
||||
request.setStyle(style);
|
||||
request.setLength(length);
|
||||
request.setPlotIntro(plotIntro);
|
||||
request.setPlotTurning(plotTurning);
|
||||
request.setPlotClimax(plotClimax);
|
||||
request.setPlotEnding(plotEnding);
|
||||
|
||||
String result = (String) assembleMethod.invoke(epicScriptService, request);
|
||||
|
||||
// 验证非空字段被包含,空字段不被包含
|
||||
if (title != null && !title.isEmpty()) {
|
||||
assertTrue(result.contains(title), "非空标题应被包含");
|
||||
}
|
||||
if (theme != null && !theme.isEmpty()) {
|
||||
assertTrue(result.contains(theme), "非空主题应被包含");
|
||||
}
|
||||
if (plotIntro != null && !plotIntro.isEmpty()) {
|
||||
assertTrue(result.contains(plotIntro), "非空序幕应被包含");
|
||||
}
|
||||
if (plotTurning != null && !plotTurning.isEmpty()) {
|
||||
assertTrue(result.contains(plotTurning), "非空转折应被包含");
|
||||
}
|
||||
if (plotClimax != null && !plotClimax.isEmpty()) {
|
||||
assertTrue(result.contains(plotClimax), "非空高潮应被包含");
|
||||
}
|
||||
if (plotEnding != null && !plotEnding.isEmpty()) {
|
||||
assertTrue(result.contains(plotEnding), "非空结局应被包含");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
fail("测试执行失败: " + e.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Property 3: 输入组装 - 验证输出格式正确")
|
||||
public void testInputAssemblyFormat() throws Exception {
|
||||
EpicScriptCreateRequest request = new EpicScriptCreateRequest();
|
||||
request.setTitle("我的逆袭人生");
|
||||
request.setTheme("成为行业领袖");
|
||||
request.setStyle("career");
|
||||
request.setLength("medium");
|
||||
request.setPlotIntro("从一个普通员工开始");
|
||||
request.setPlotTurning("遇到贵人指点");
|
||||
request.setPlotClimax("面临重大抉择");
|
||||
request.setPlotEnding("成功逆袭");
|
||||
|
||||
Method assembleMethod = EpicScriptServiceImpl.class.getDeclaredMethod(
|
||||
"assembleScriptInput", EpicScriptCreateRequest.class);
|
||||
assembleMethod.setAccessible(true);
|
||||
|
||||
String result = (String) assembleMethod.invoke(epicScriptService, request);
|
||||
|
||||
// 验证格式标签
|
||||
assertTrue(result.contains("【剧本标题】我的逆袭人生"), "应包含正确格式的标题");
|
||||
assertTrue(result.contains("【主题渴望】成为行业领袖"), "应包含正确格式的主题");
|
||||
assertTrue(result.contains("【剧本风格】职场逆袭"), "应包含正确格式的风格");
|
||||
assertTrue(result.contains("【篇幅长度】标准篇"), "应包含正确格式的篇幅");
|
||||
assertTrue(result.contains("【序幕-低谷回响】从一个普通员工开始"), "应包含正确格式的序幕");
|
||||
assertTrue(result.contains("【转折-契机出现】遇到贵人指点"), "应包含正确格式的转折");
|
||||
assertTrue(result.contains("【高潮-命运抉择】面临重大抉择"), "应包含正确格式的高潮");
|
||||
assertTrue(result.contains("【结局-新的开始】成功逆袭"), "应包含正确格式的结局");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取随机风格
|
||||
*/
|
||||
private String getRandomStyle() {
|
||||
String[] styles = {"career", "love", "fantasy"};
|
||||
return styles[random.nextInt(styles.length)];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取随机篇幅
|
||||
*/
|
||||
private String getRandomLength() {
|
||||
String[] lengths = {"medium", "long"};
|
||||
return lengths[random.nextInt(lengths.length)];
|
||||
}
|
||||
|
||||
/**
|
||||
* 从样本列表中随机获取一个元素
|
||||
* @param samples 样本列表
|
||||
* @return 随机选择的样本
|
||||
*/
|
||||
private String getRandomSample(List<String> samples) {
|
||||
return samples.get(random.nextInt(samples.size()));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user