Files
svn-log-tool/src/test/java/com/svnlog/web/service/AiWorkflowServiceTest.java
T

187 lines
7.4 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package com.svnlog.web.service;
import java.nio.file.Path;
import java.time.Instant;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import com.svnlog.web.model.TaskInfo;
import com.svnlog.web.model.TaskStatus;
import okio.Buffer;
class AiWorkflowServiceTest {
@TempDir
Path tempDir;
private AiApiService buildAiApiService(SettingsService settingsService) {
return new AiApiService(settingsService);
}
private ExcelExportService buildExcelExportService() {
return new ExcelExportService();
}
@Test
void shouldResolveDeepSeekProviderByDefault() {
final OutputFileService outputFileService = buildOutputFileService();
final SettingsService settingsService = buildSettingsService(outputFileService);
final AiApiService aiApiService = buildAiApiService(settingsService);
new AiWorkflowService(
outputFileService,
new AiInputValidator(),
aiApiService,
buildExcelExportService()
);
// DeepSeek provider:通过请求临时传入 key 来解析 context(不依赖内置默认 key
final AiApiService.AiProviderContext context = aiApiService.resolveProviderContext("sk-test-key");
Assertions.assertEquals(SettingsService.PROVIDER_DEEPSEEK, context.getProvider());
Assertions.assertEquals("deepseek-chat", context.getStageOneModel());
Assertions.assertEquals("deepseek-reasoner", context.getStageTwoModel());
}
@Test
void shouldResolveOpenAiCompatibleModelsAndUrl() {
final OutputFileService outputFileService = buildOutputFileService();
final SettingsService settingsService = buildSettingsService(outputFileService);
settingsService.updateSettings(
null,
SettingsService.PROVIDER_OPENAI_COMPATIBLE,
"http://127.0.0.1:5001/v1/",
"sk-openai-test",
"deepseek-v4-flash",
"deepseek-v4-pro",
null,
null,
null,
null
);
final AiApiService aiApiService = buildAiApiService(settingsService);
final AiWorkflowService service = new AiWorkflowService(
outputFileService, new AiInputValidator(), aiApiService, buildExcelExportService()
);
final AiApiService.AiProviderContext context = aiApiService.resolveProviderContext(null);
Assertions.assertEquals(SettingsService.PROVIDER_OPENAI_COMPATIBLE, context.getProvider());
Assertions.assertEquals("http://127.0.0.1:5001/v1/chat/completions", context.getApiUrl());
Assertions.assertEquals("deepseek-v4-flash", context.getStageOneModel());
Assertions.assertEquals("deepseek-v4-pro", context.getStageTwoModel());
}
@Test
void shouldFailFastWhenOpenAiCompatibleBaseUrlMissing() {
final OutputFileService outputFileService = buildOutputFileService();
final AiApiService aiApiService = buildAiApiService(
new StubSettingsService(outputFileService, " ", "sk-openai-test")
);
final IllegalStateException error = Assertions.assertThrows(
IllegalStateException.class,
() -> aiApiService.resolveProviderContext(null)
);
Assertions.assertTrue(error.getMessage().contains("OpenAI兼容 Base URL"));
}
@Test
void shouldParseCompatibleStreamWhenOnlyContentIsReturned() throws Exception {
final OutputFileService outputFileService = buildOutputFileService();
final SettingsService settingsService = buildSettingsService(outputFileService);
settingsService.updateSettings(
null,
SettingsService.PROVIDER_OPENAI_COMPATIBLE,
"http://127.0.0.1:5001/v1",
"sk-openai-test",
"deepseek-v4-flash",
"deepseek-v4-pro",
null,
null,
null,
null
);
final AiApiService aiApiService = buildAiApiService(settingsService);
final AiApiService.AiProviderContext providerContext = aiApiService.resolveProviderContext(null);
final TaskContext taskContext = new TaskContext(buildTaskInfo(), null, null);
final Buffer buffer = new Buffer()
.writeUtf8("data: {\"choices\":[{\"delta\":{\"content\":\"{\\\"items\\\":[\"}}]}\n")
.writeUtf8("data: {\"choices\":[{\"delta\":{\"content\":\"{\\\"summary\\\":\\\"done\\\"}\"}}]}\n")
.writeUtf8("data: {\"choices\":[{\"delta\":{\"content\":\"]}\"},\"finish_reason\":\"stop\"}]}\n")
.writeUtf8("data: [DONE]\n");
final AiApiService.AiStreamResult result = aiApiService.readStreamingResponse(
buffer,
taskContext,
providerContext,
"deepseek-v4-flash",
8000
);
Assertions.assertEquals("{\"items\":[{\"summary\":\"done\"}]}", result.getAnswer());
Assertions.assertEquals("stop", result.getFinishReason());
}
private OutputFileService buildOutputFileService() {
final OutputFileService outputFileService = new OutputFileService();
outputFileService.setOutputRoot(tempDir.resolve("outputs").toString());
return outputFileService;
}
private TaskInfo buildTaskInfo() {
final TaskInfo taskInfo = new TaskInfo();
taskInfo.setTaskId("task-1");
taskInfo.setStatus(TaskStatus.RUNNING);
taskInfo.setCreatedAt(Instant.now());
taskInfo.setUpdatedAt(Instant.now());
return taskInfo;
}
private SettingsService buildSettingsService(OutputFileService outputFileService) {
final RepositoryConfigService repositoryConfigService = buildRepositoryConfigService(outputFileService);
final SvnPresetService svnPresetService = new SvnPresetService(repositoryConfigService);
return new SettingsService(outputFileService, new SettingsPersistenceService(), svnPresetService, repositoryConfigService);
}
private RepositoryConfigService buildRepositoryConfigService(OutputFileService outputFileService) {
final RepositoryConfigService repositoryConfigService = new RepositoryConfigService(outputFileService);
repositoryConfigService.init();
return repositoryConfigService;
}
private static final class StubSettingsService extends SettingsService {
private final String openaiBaseUrl;
private final String openaiApiKey;
private StubSettingsService(OutputFileService outputFileService, String openaiBaseUrl, String openaiApiKey) {
super(
outputFileService,
new SettingsPersistenceService(),
new SvnPresetService(new RepositoryConfigService(outputFileService)),
new RepositoryConfigService(outputFileService)
);
this.openaiBaseUrl = openaiBaseUrl;
this.openaiApiKey = openaiApiKey;
}
@Override
public String getProvider() {
return SettingsService.PROVIDER_OPENAI_COMPATIBLE;
}
@Override
public String getOpenaiBaseUrl() {
return openaiBaseUrl;
}
@Override
public String getOpenaiApiKey() {
return openaiApiKey;
}
}
}