refactor: optimize security baseline, task logging, frontend maven packaging, and redirect root page to v2
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
package com.svnlog.web.service;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* AiApiService 中 truncateErrorBody / redactSensitiveFields 的单元测试。
|
||||
* truncateErrorBody 为 package-private,可直接测试。
|
||||
*/
|
||||
class AiApiServiceTest {
|
||||
|
||||
// settingsService 未用到 truncateErrorBody,传 null 安全
|
||||
private final AiApiService service = new AiApiService(null);
|
||||
|
||||
@Test
|
||||
void shouldReturnEmptyPlaceholderWhenBodyIsNull() {
|
||||
Assertions.assertEquals("(empty)", service.truncateErrorBody(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnEmptyPlaceholderWhenBodyIsBlank() {
|
||||
Assertions.assertEquals("(empty)", service.truncateErrorBody(" "));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnShortBodyWithoutSensitiveDataAsIs() {
|
||||
final String body = "{\"error\": \"model not found\", \"code\": 404}";
|
||||
Assertions.assertEquals(body, service.truncateErrorBody(body));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldTruncateLongBodyAndAppendMarker() {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < 600; i++) {
|
||||
sb.append('x');
|
||||
}
|
||||
final String result = service.truncateErrorBody(sb.toString());
|
||||
Assertions.assertTrue(result.contains("[已截断"), "should contain truncation marker");
|
||||
Assertions.assertTrue(result.length() < 600, "result should be shorter than original");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRedactApiKeyFieldInJsonBody() {
|
||||
final String body = "{\"error\": \"invalid\", \"api_key\": \"sk-secret-123456\"}";
|
||||
final String result = service.truncateErrorBody(body);
|
||||
Assertions.assertFalse(result.contains("sk-secret-123456"), "api_key value must be redacted");
|
||||
Assertions.assertTrue(result.contains("***"), "redaction marker must be present");
|
||||
Assertions.assertTrue(result.contains("api_key"), "field name must be preserved");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRedactTokenFieldInJsonBody() {
|
||||
final String body = "{\"token\": \"eyJhbGciOiJIUzI1NiJ9.payload\", \"status\": 401}";
|
||||
final String result = service.truncateErrorBody(body);
|
||||
Assertions.assertFalse(result.contains("eyJhbGciOiJIUzI1NiJ9.payload"), "token value must be redacted");
|
||||
Assertions.assertTrue(result.contains("***"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRedactPasswordFieldInJsonBody() {
|
||||
final String body = "{\"password\": \"my-secret-pw\", \"code\": 401}";
|
||||
final String result = service.truncateErrorBody(body);
|
||||
Assertions.assertFalse(result.contains("my-secret-pw"), "password value must be redacted");
|
||||
Assertions.assertTrue(result.contains("***"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRedactAuthorizationBearerToken() {
|
||||
final String body = "Authorization: Bearer sk-real-token-abc\nError: unauthorized";
|
||||
final String result = service.truncateErrorBody(body);
|
||||
Assertions.assertFalse(result.contains("sk-real-token-abc"), "bearer token must be redacted");
|
||||
Assertions.assertTrue(result.contains("***"), "redaction marker must be present");
|
||||
Assertions.assertTrue(result.contains("Authorization"), "header name must be preserved");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldPreserveNonSensitiveContentAfterRedaction() {
|
||||
final String body = "{\"error\": \"rate limit exceeded\", \"code\": 429}";
|
||||
final String result = service.truncateErrorBody(body);
|
||||
Assertions.assertTrue(result.contains("rate limit exceeded"), "non-sensitive content must be preserved");
|
||||
Assertions.assertTrue(result.contains("429"), "non-sensitive content must be preserved");
|
||||
}
|
||||
}
|
||||
@@ -30,14 +30,15 @@ class AiWorkflowServiceTest {
|
||||
final OutputFileService outputFileService = buildOutputFileService();
|
||||
final SettingsService settingsService = buildSettingsService(outputFileService);
|
||||
final AiApiService aiApiService = buildAiApiService(settingsService);
|
||||
final AiWorkflowService service = new AiWorkflowService(
|
||||
new AiWorkflowService(
|
||||
outputFileService,
|
||||
new AiInputValidator(),
|
||||
aiApiService,
|
||||
buildExcelExportService()
|
||||
);
|
||||
|
||||
final AiApiService.AiProviderContext context = aiApiService.resolveProviderContext(null);
|
||||
// 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());
|
||||
|
||||
Reference in New Issue
Block a user