文字转语音

This commit is contained in:
spllzh
2025-10-04 19:52:06 +08:00
parent 24c00c22ab
commit 06730b656e
51 changed files with 5157 additions and 8 deletions

View File

@@ -1,4 +1,4 @@
package com.rj;
package com.rj.audio;
import com.alibaba.dashscope.audio.asr.transcription.*;
import com.alibaba.dashscope.common.TaskStatus;

View File

@@ -0,0 +1,86 @@
package com.rj.audio;
import com.rj.dto.SiliconFlowTtsRequest;
import com.rj.dto.SiliconFlowTtsResponse;
import com.rj.service.SiliconFlowTtsService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.TestPropertySource;
import java.util.Base64;
import static org.junit.jupiter.api.Assertions.*;
/**
* 文本转语音测试类
*
* @author rj
* @date 2025-01-02
*/
@SpringBootTest
@TestPropertySource(properties = {
"siliconflow.enabled=true",
"siliconflow.api-key=${SILICONFLOW_API_KEY:test-key}",
"siliconflow.base-url=https://api.siliconflow.cn/v1"
})
public class TextToSpeechTest {
@Autowired
private SiliconFlowTtsService ttsService;
@Test
public void testAudioDurationCalculation() {
// 测试音频时长计算
SiliconFlowTtsService service = new SiliconFlowTtsService();
// 模拟不同大小的音频文件
long smallAudio = 50000; // 50KB
long mediumAudio = 200000; // 200KB
long largeAudio = 500000; // 500KB
// 注意由于calculateAudioDuration是private方法我们通过反射或公共方法测试
// 这里我们主要测试服务的基本功能
assertNotNull(ttsService);
}
@Test
public void testTtsRequestWithVoice() {
SiliconFlowTtsRequest request = new SiliconFlowTtsRequest();
request.setModel("fnlp/MOSS-TTSD-v0.5");
request.setInput("Hello, this is a test message.");
request.setVoice("fnlp/MOSS-TTSD-v0.5:claire");
assertNotNull(request);
assertEquals("fnlp/MOSS-TTSD-v0.5", request.getModel());
assertEquals("fnlp/MOSS-TTSD-v0.5:claire", request.getVoice());
assertNotNull(request.getInput());
}
@Test
public void testBase64Encoding() {
// 测试Base64编码/解码
String testText = "Hello World";
String encoded = Base64.getEncoder().encodeToString(testText.getBytes());
String decoded = new String(Base64.getDecoder().decode(encoded));
assertEquals(testText, decoded);
assertNotNull(encoded);
assertTrue(encoded.length() > 0);
}
@Test
public void testTtsResponseCreation() {
// 测试TTS响应创建
String mockAudioData = "mockAudioBase64Data";
SiliconFlowTtsResponse response = SiliconFlowTtsResponse.success(
mockAudioData, "mp3", 44100, 5.5
);
assertTrue(response.isSuccess());
assertEquals(mockAudioData, response.getAudio());
assertEquals("mp3", response.getFormat());
assertEquals(44100, response.getSampleRate());
assertEquals(5.5, response.getDuration());
}
}

View File

@@ -0,0 +1,342 @@
package com.rj.minio;
import io.minio.*;
import io.minio.errors.*;
import io.minio.messages.Bucket;
import io.minio.messages.Item;
import org.junit.jupiter.api.*;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.TestPropertySource;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.List;
/**
* MinIO 存储服务测试类
* 测试MinIO的连接、文件上传、下载、删除等功能
*
* @author rj
* @date 2025-10-02
*/
@SpringBootTest
@TestPropertySource(properties = {
"minio.endpoint=http://101.35.52.237:19005",
"minio.access-key=minioadmin",
"minio.secret-key=minioadmin",
"minio.bucket-name=test-bucket"
})
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class MinIOServiceTest {
private static final String ENDPOINT = "http://101.35.52.237:19005";
private static final String ACCESS_KEY = "minioadmin";
private static final String SECRET_KEY = "minioadmin";
private static final String BUCKET_NAME = "car";
private static final String TEST_OBJECT_NAME = "system.txt";
private static final String TEST_CONTENT = "这是一个MinIO测试文件用于验证文件存储和读取功能。\nTest content for MinIO file storage.";
private MinioClient minioClient;
@BeforeEach
void setUp() {
// 初始化MinIO客户端
minioClient = MinioClient.builder()
.endpoint(ENDPOINT)
.credentials(ACCESS_KEY, SECRET_KEY)
.build();
}
@Test
@Order(1)
@DisplayName("测试MinIO连接")
void testConnection() {
try {
// 测试连接是否正常
List<Bucket> buckets = minioClient.listBuckets();
System.out.println("✅ MinIO连接成功");
System.out.println("当前存储桶列表:");
for (Bucket bucket : buckets) {
System.out.println("- " + bucket.name() + " (创建时间: " + bucket.creationDate() + ")");
}
Assertions.assertNotNull(buckets, "MinIO连接失败无法获取存储桶列表");
} catch (Exception e) {
System.err.println("❌ MinIO连接失败: " + e.getMessage());
Assertions.fail("MinIO连接测试失败: " + e.getMessage());
}
}
@Test
@Order(2)
@DisplayName("创建测试存储桶")
void testCreateBucket() {
try {
// 检查存储桶是否存在
boolean bucketExists = minioClient.bucketExists(
BucketExistsArgs.builder().bucket(BUCKET_NAME).build()
);
if (!bucketExists) {
// 创建存储桶
minioClient.makeBucket(
MakeBucketArgs.builder().bucket(BUCKET_NAME).build()
);
System.out.println("✅ 成功创建存储桶: " + BUCKET_NAME);
} else {
System.out.println("✅ 存储桶已存在: " + BUCKET_NAME);
}
// 验证存储桶是否存在
boolean exists = minioClient.bucketExists(
BucketExistsArgs.builder().bucket(BUCKET_NAME).build()
);
Assertions.assertTrue(exists, "存储桶创建失败或不存在");
} catch (Exception e) {
System.err.println("❌ 创建存储桶失败: " + e.getMessage());
Assertions.fail("创建存储桶测试失败: " + e.getMessage());
}
}
@Test
@Order(3)
@DisplayName("测试文件上传")
void testUploadFile() {
try {
// 创建测试文件内容
ByteArrayInputStream inputStream = new ByteArrayInputStream(
TEST_CONTENT.getBytes(StandardCharsets.UTF_8)
);
// 上传文件
ObjectWriteResponse response = minioClient.putObject(
PutObjectArgs.builder()
.bucket(BUCKET_NAME)
.object(TEST_OBJECT_NAME)
.stream(inputStream, TEST_CONTENT.getBytes(StandardCharsets.UTF_8).length, -1)
.contentType("text/plain")
.build()
);
System.out.println("✅ 文件上传成功!");
System.out.println("文件名: " + TEST_OBJECT_NAME);
System.out.println("存储桶: " + response.bucket());
System.out.println("ETag: " + response.etag());
Assertions.assertNotNull(response.etag(), "文件上传失败未获得ETag");
} catch (Exception e) {
System.err.println("❌ 文件上传失败: " + e.getMessage());
Assertions.fail("文件上传测试失败: " + e.getMessage());
}
}
@Test
@Order(4)
@DisplayName("测试文件下载和内容验证")
void testDownloadFile() {
try {
// 下载文件
GetObjectResponse response = minioClient.getObject(
GetObjectArgs.builder()
.bucket(BUCKET_NAME)
.object(TEST_OBJECT_NAME)
.build()
);
// 读取文件内容
StringBuilder content = new StringBuilder();
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(response, StandardCharsets.UTF_8))) {
String line;
while ((line = reader.readLine()) != null) {
content.append(line).append("\n");
}
}
String downloadedContent = content.toString().trim();
System.out.println("✅ 文件下载成功!");
System.out.println("文件内容: " + downloadedContent);
// 验证文件内容是否正确
Assertions.assertEquals(TEST_CONTENT, downloadedContent, "下载的文件内容与上传的内容不匹配");
} catch (Exception e) {
System.err.println("❌ 文件下载失败: " + e.getMessage());
Assertions.fail("文件下载测试失败: " + e.getMessage());
}
}
@Test
@Order(5)
@DisplayName("测试文件元数据获取")
void testGetFileMetadata() {
try {
// 获取文件元数据
StatObjectResponse stat = minioClient.statObject(
StatObjectArgs.builder()
.bucket(BUCKET_NAME)
.object(TEST_OBJECT_NAME)
.build()
);
System.out.println("✅ 文件元数据获取成功!");
System.out.println("文件名: " + stat.object());
System.out.println("文件大小: " + stat.size() + " 字节");
System.out.println("内容类型: " + stat.contentType());
System.out.println("最后修改时间: " + stat.lastModified());
System.out.println("ETag: " + stat.etag());
Assertions.assertNotNull(stat, "无法获取文件元数据");
Assertions.assertTrue(stat.size() > 0, "文件大小应该大于0");
} catch (Exception e) {
System.err.println("❌ 获取文件元数据失败: " + e.getMessage());
Assertions.fail("获取文件元数据测试失败: " + e.getMessage());
}
}
@Test
@Order(6)
@DisplayName("测试文件列表")
void testListFiles() {
try {
// 列出存储桶中的所有文件
Iterable<Result<Item>> results = minioClient.listObjects(
ListObjectsArgs.builder()
.bucket(BUCKET_NAME)
.build()
);
System.out.println("✅ 文件列表获取成功!");
System.out.println("存储桶 " + BUCKET_NAME + " 中的文件:");
boolean foundTestFile = false;
for (Result<Item> result : results) {
Item item = result.get();
System.out.println("- " + item.objectName() + " (大小: " + item.size() + " 字节, 修改时间: " + item.lastModified() + ")");
if (TEST_OBJECT_NAME.equals(item.objectName())) {
foundTestFile = true;
}
}
Assertions.assertTrue(foundTestFile, "在文件列表中未找到测试文件");
} catch (Exception e) {
System.err.println("❌ 获取文件列表失败: " + e.getMessage());
Assertions.fail("获取文件列表测试失败: " + e.getMessage());
}
}
@Test
@Order(7)
@DisplayName("测试文件删除")
void testDeleteFile() {
try {
// 删除文件
minioClient.removeObject(
RemoveObjectArgs.builder()
.bucket(BUCKET_NAME)
.object(TEST_OBJECT_NAME)
.build()
);
System.out.println("✅ 文件删除成功: " + TEST_OBJECT_NAME);
// 验证文件是否已删除
try {
minioClient.statObject(
StatObjectArgs.builder()
.bucket(BUCKET_NAME)
.object(TEST_OBJECT_NAME)
.build()
);
Assertions.fail("文件删除失败,文件仍然存在");
} catch (ErrorResponseException e) {
if (e.errorResponse().code().equals("NoSuchKey")) {
System.out.println("✅ 验证成功:文件已被删除");
} else {
throw e;
}
}
} catch (Exception e) {
System.err.println("❌ 文件删除失败: " + e.getMessage());
Assertions.fail("文件删除测试失败: " + e.getMessage());
}
}
@Test
@Order(8)
@DisplayName("测试大文件上传下载")
void testLargeFileUploadDownload() {
String largeFileName = "large-test-file.txt";
try {
// 创建一个较大的测试文件内容约1MB
StringBuilder largeContent = new StringBuilder();
String baseContent = "这是大文件测试内容重复多次以创建较大的文件。Large file test content repeated multiple times.\n";
for (int i = 0; i < 10000; i++) {
largeContent.append(baseContent);
}
byte[] contentBytes = largeContent.toString().getBytes(StandardCharsets.UTF_8);
ByteArrayInputStream inputStream = new ByteArrayInputStream(contentBytes);
// 上传大文件
minioClient.putObject(
PutObjectArgs.builder()
.bucket(BUCKET_NAME)
.object(largeFileName)
.stream(inputStream, contentBytes.length, -1)
.contentType("text/plain")
.build()
);
System.out.println("✅ 大文件上传成功!");
System.out.println("文件大小: " + contentBytes.length + " 字节");
// 下载并验证大文件
GetObjectResponse response = minioClient.getObject(
GetObjectArgs.builder()
.bucket(BUCKET_NAME)
.object(largeFileName)
.build()
);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = response.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
byte[] downloadedBytes = outputStream.toByteArray();
System.out.println("✅ 大文件下载成功!");
System.out.println("下载文件大小: " + downloadedBytes.length + " 字节");
// 验证文件大小
Assertions.assertEquals(contentBytes.length, downloadedBytes.length, "大文件上传下载后大小不匹配");
// 清理大文件
minioClient.removeObject(
RemoveObjectArgs.builder()
.bucket(BUCKET_NAME)
.object(largeFileName)
.build()
);
System.out.println("✅ 大文件清理完成");
} catch (Exception e) {
System.err.println("❌ 大文件测试失败: " + e.getMessage());
Assertions.fail("大文件测试失败: " + e.getMessage());
}
}
@AfterAll
static void tearDown() {
System.out.println("\n=== MinIO 测试完成 ===");
System.out.println("所有测试已执行完毕,请检查测试结果。");
System.out.println("MinIO服务器地址: " + ENDPOINT);
System.out.println("测试存储桶: " + BUCKET_NAME);
}
}

View File

@@ -23,8 +23,6 @@ public class TopSalesMockInsertDataSchedulerTest {
public void testGenerateMockData() {
try {
topSalesMockInsertDataScheduler.generateDailyTopSalesData();
} catch (Exception e) {
System.err.println("测试失败: " + e.getMessage());
e.printStackTrace();

View File

@@ -0,0 +1,332 @@
package com.rj.service;
import com.rj.config.MinIOConfig;
import org.junit.jupiter.api.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.TestPropertySource;
import java.io.InputStream;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
/**
* MinIOService 集成测试类
* 测试与实际MinIO服务器的集成
*
* @author rj
* @date 2025-10-02
*/
@SpringBootTest
@ActiveProfiles("test")
@TestPropertySource(properties = {
"minio.endpoint=http://101.35.52.237:19005",
"minio.access-key=minioadmin",
"minio.secret-key=minioadmin",
"minio.bucket-name=test-integration"
})
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
@DisplayName("MinIOService 集成测试")
class MinIOServiceIntegrationTest {
@Autowired
private MinIOService minioService;
@Autowired
private MinIOConfig minioConfig;
private static final String TEST_FILE_NAME = "integration-test-file.txt";
private static final String TEST_CONTENT = "这是集成测试文件内容\nIntegration test file content";
private static final String CUSTOM_BUCKET = "integration-custom-bucket";
@BeforeAll
static void beforeAll() {
System.out.println("开始MinIOService集成测试...");
System.out.println("请确保MinIO服务器正在运行: http://101.35.52.237:19005");
}
@AfterAll
static void afterAll() {
System.out.println("MinIOService集成测试完成");
}
@Test
@Order(1)
@DisplayName("验证配置加载")
void testConfigurationLoading() {
assertNotNull(minioConfig);
assertNotNull(minioConfig.getEndpoint());
assertNotNull(minioConfig.getAccessKey());
assertNotNull(minioConfig.getSecretKey());
assertNotNull(minioConfig.getBucketName());
System.out.println("MinIO配置信息:");
System.out.println("Endpoint: " + minioConfig.getEndpoint());
System.out.println("Bucket: " + minioConfig.getBucketName());
System.out.println("✅ 配置加载成功");
}
@Test
@Order(2)
@DisplayName("创建测试存储桶")
void testCreateBucket() {
try {
boolean result = minioService.createBucket(CUSTOM_BUCKET);
assertTrue(result);
System.out.println("✅ 创建存储桶成功: " + CUSTOM_BUCKET);
} catch (Exception e) {
System.out.println("⚠️ 创建存储桶失败(可能已存在): " + e.getMessage());
// 存储桶可能已存在,这不是错误
}
}
@Test
@Order(3)
@DisplayName("获取所有存储桶")
void testGetAllBuckets() {
List<String> buckets = minioService.getAllBuckets();
assertNotNull(buckets);
System.out.println("✅ 获取存储桶列表成功,共 " + buckets.size() + " 个存储桶:");
buckets.forEach(bucket -> System.out.println(" - " + bucket));
}
@Test
@Order(4)
@DisplayName("上传文件到默认存储桶")
void testUploadFileToDefaultBucket() {
MockMultipartFile file = new MockMultipartFile(
"file",
TEST_FILE_NAME,
"text/plain",
TEST_CONTENT.getBytes()
);
String fileUrl = minioService.uploadFile(file);
assertNotNull(fileUrl);
assertTrue(fileUrl.contains(TEST_FILE_NAME));
System.out.println("✅ 文件上传成功:");
System.out.println(" 文件名: " + TEST_FILE_NAME);
System.out.println(" 访问URL: " + fileUrl);
}
@Test
@Order(5)
@DisplayName("上传文件到自定义存储桶")
void testUploadFileToCustomBucket() {
String customFileName = "custom-" + TEST_FILE_NAME;
MockMultipartFile file = new MockMultipartFile(
"file",
customFileName,
"text/plain",
TEST_CONTENT.getBytes()
);
String fileUrl = minioService.uploadFile(file, CUSTOM_BUCKET);
System.out.println("✅ 文件上传到自定义存储桶成功:");
System.out.println(" 存储桶: " + CUSTOM_BUCKET);
System.out.println(" 文件名: " + customFileName);
System.out.println(" 访问URL: " + fileUrl);
}
@Test
@Order(6)
@DisplayName("检查文件是否存在")
void testFileExists() {
boolean exists = minioService.fileExists(TEST_FILE_NAME);
assertTrue(exists);
System.out.println("✅ 文件存在性检查成功: " + TEST_FILE_NAME);
boolean notExists = minioService.fileExists("non-existent-file.txt");
assertFalse(notExists);
System.out.println("✅ 不存在文件检查成功: non-existent-file.txt");
}
@Test
@Order(7)
@DisplayName("获取文件列表")
void testListFiles() {
List<String> files = minioService.listFiles();
assertNotNull(files);
assertTrue(files.contains(TEST_FILE_NAME));
System.out.println("✅ 获取文件列表成功,共 " + files.size() + " 个文件:");
files.forEach(file -> System.out.println(" - " + file));
}
@Test
@Order(8)
@DisplayName("获取文件详细信息")
void testListFileDetails() {
var fileDetails = minioService.listFileDetails();
assertNotNull(fileDetails);
assertFalse(fileDetails.isEmpty());
System.out.println("✅ 获取文件详细信息成功:");
fileDetails.forEach(fileInfo -> {
System.out.println(" 文件: " + fileInfo.getObjectName());
System.out.println(" 大小: " + fileInfo.getSize() + " 字节");
System.out.println(" 修改时间: " + fileInfo.getLastModified());
System.out.println(" ETag: " + fileInfo.getEtag());
});
}
@Test
@Order(9)
@DisplayName("下载文件")
void testDownloadFile() {
try (InputStream inputStream = minioService.downloadFile(TEST_FILE_NAME)) {
assertNotNull(inputStream);
// 读取文件内容
byte[] content = inputStream.readAllBytes();
String downloadedContent = new String(content);
assertEquals(TEST_CONTENT, downloadedContent);
System.out.println("✅ 文件下载成功:");
System.out.println(" 文件名: " + TEST_FILE_NAME);
System.out.println(" 内容长度: " + content.length + " 字节");
System.out.println(" 内容预览: " + downloadedContent.substring(0, Math.min(50, downloadedContent.length())) + "...");
} catch (Exception e) {
fail("文件下载失败: " + e.getMessage());
}
}
@Test
@Order(10)
@DisplayName("获取预签名URL")
void testGetPresignedUrl() {
String presignedUrl = minioService.getPresignedUrl(TEST_FILE_NAME, 3600); // 1小时
assertNotNull(presignedUrl);
assertTrue(presignedUrl.startsWith("http"));
assertTrue(presignedUrl.contains(TEST_FILE_NAME));
System.out.println("✅ 获取预签名URL成功:");
System.out.println(" 文件名: " + TEST_FILE_NAME);
System.out.println(" 预签名URL: " + presignedUrl);
}
@Test
@Order(11)
@DisplayName("复制文件")
void testCopyFile() {
String targetFileName = "copied-" + TEST_FILE_NAME;
boolean result = minioService.copyFile(TEST_FILE_NAME, targetFileName);
assertTrue(result);
// 验证复制的文件是否存在
boolean exists = minioService.fileExists(targetFileName);
assertTrue(exists);
System.out.println("✅ 文件复制成功:");
System.out.println(" 源文件: " + TEST_FILE_NAME);
System.out.println(" 目标文件: " + targetFileName);
}
@Test
@Order(12)
@DisplayName("获取文件URL")
void testGetFileUrl() {
String fileUrl = minioService.getFileUrl(TEST_FILE_NAME);
assertNotNull(fileUrl);
assertTrue(fileUrl.contains(TEST_FILE_NAME));
assertTrue(fileUrl.startsWith("http"));
System.out.println("✅ 获取文件URL成功:");
System.out.println(" 文件名: " + TEST_FILE_NAME);
System.out.println(" 文件URL: " + fileUrl);
}
@Test
@Order(13)
@DisplayName("性能测试 - 批量上传")
void testBatchUpload() {
long startTime = System.currentTimeMillis();
int fileCount = 5;
for (int i = 0; i < fileCount; i++) {
String fileName = "batch-test-" + i + ".txt";
String content = "批量测试文件内容 " + i;
MockMultipartFile file = new MockMultipartFile(
"file",
fileName,
"text/plain",
content.getBytes()
);
String fileUrl = minioService.uploadFile(file);
assertNotNull(fileUrl);
}
long endTime = System.currentTimeMillis();
long duration = endTime - startTime;
System.out.println("✅ 批量上传性能测试完成:");
System.out.println(" 文件数量: " + fileCount);
System.out.println(" 总耗时: " + duration + " ms");
System.out.println(" 平均耗时: " + (duration / fileCount) + " ms/文件");
}
@Test
@Order(14)
@DisplayName("清理测试文件")
void testCleanupTestFiles() {
List<String> files = minioService.listFiles();
int deletedCount = 0;
for (String fileName : files) {
if (fileName.startsWith("integration-test-") ||
fileName.startsWith("copied-") ||
fileName.startsWith("batch-test-")) {
boolean deleted = minioService.deleteFile(fileName);
if (deleted) {
deletedCount++;
System.out.println(" 已删除: " + fileName);
}
}
}
System.out.println("✅ 清理测试文件完成,共删除 " + deletedCount + " 个文件");
}
@Test
@Order(15)
@DisplayName("错误处理测试")
void testErrorHandling() {
// 测试下载不存在的文件
assertThrows(RuntimeException.class, () -> {
minioService.downloadFile("non-existent-file.txt");
});
System.out.println("✅ 下载不存在文件的错误处理正常");
// 测试检查不存在的文件
boolean exists = minioService.fileExists("non-existent-file.txt");
assertFalse(exists);
System.out.println("✅ 检查不存在文件的处理正常");
}
@Test
@Order(16)
@DisplayName("连接稳定性测试")
void testConnectionStability() {
// 连续执行多次操作,测试连接稳定性
for (int i = 0; i < 3; i++) {
List<String> buckets = minioService.getAllBuckets();
assertNotNull(buckets);
List<String> files = minioService.listFiles();
assertNotNull(files);
}
System.out.println("✅ 连接稳定性测试通过");
}
}

View File

@@ -0,0 +1,624 @@
package com.rj.service;
import com.rj.config.MinIOConfig;
import com.rj.utils.MinIOUtil;
import org.junit.jupiter.api.*;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.mock.web.MockMultipartFile;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.time.ZonedDateTime;
import java.util.Arrays;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
/**
* MinIOService 测试类
* 包含单元测试和集成测试
*
* @author rj
* @date 2025-10-02
*/
@ExtendWith(MockitoExtension.class)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
@DisplayName("MinIOService 测试")
class MinIOServiceTest {
@Mock
private MinIOUtil minioUtil;
@Mock
private MinIOConfig minioConfig;
@InjectMocks
private MinIOService minioService;
private static final String DEFAULT_BUCKET = "test-bucket";
private static final String DEFAULT_ENDPOINT = "http://localhost:9000";
private static final String TEST_FILE_NAME = "test-file.txt";
private static final String TEST_CONTENT = "这是测试文件内容";
@BeforeEach
void setUp() {
// 设置默认的Mock行为
when(minioConfig.getBucketName()).thenReturn(DEFAULT_BUCKET);
when(minioConfig.getEndpoint()).thenReturn(DEFAULT_ENDPOINT);
when(minioConfig.getFileUrlPrefix()).thenReturn(DEFAULT_ENDPOINT + "/" + DEFAULT_BUCKET + "/");
}
@Nested
@DisplayName("文件上传测试")
class UploadTests {
@Test
@Order(1)
@DisplayName("上传文件到默认存储桶")
void testUploadFileToDefaultBucket() {
// 准备测试数据
MockMultipartFile file = new MockMultipartFile(
"file",
"test.txt",
"text/plain",
TEST_CONTENT.getBytes()
);
// Mock行为
when(minioUtil.uploadFile(file, DEFAULT_BUCKET)).thenReturn(TEST_FILE_NAME);
// 执行测试
String result = minioService.uploadFile(file);
// 验证结果
assertNotNull(result);
assertTrue(result.contains(TEST_FILE_NAME));
assertEquals(DEFAULT_ENDPOINT + "/" + DEFAULT_BUCKET + "/" + TEST_FILE_NAME, result);
// 验证调用
verify(minioUtil).uploadFile(file, DEFAULT_BUCKET);
verify(minioConfig).getBucketName();
verify(minioConfig).getFileUrlPrefix();
}
@Test
@Order(2)
@DisplayName("上传文件到指定存储桶")
void testUploadFileToSpecificBucket() {
// 准备测试数据
String customBucket = "custom-bucket";
MockMultipartFile file = new MockMultipartFile(
"file",
"test.txt",
"text/plain",
TEST_CONTENT.getBytes()
);
// Mock行为
when(minioUtil.uploadFile(file, customBucket)).thenReturn(TEST_FILE_NAME);
// 执行测试
String result = minioService.uploadFile(file, customBucket);
// 验证结果
assertNotNull(result);
assertTrue(result.contains(TEST_FILE_NAME));
assertEquals(DEFAULT_ENDPOINT + "/" + customBucket + "/" + TEST_FILE_NAME, result);
// 验证调用
verify(minioUtil).uploadFile(file, customBucket);
verify(minioConfig).getEndpoint();
}
@Test
@Order(3)
@DisplayName("上传文件并指定文件名")
void testUploadFileWithCustomName() {
// 准备测试数据
String customFileName = "custom-name.txt";
MockMultipartFile file = new MockMultipartFile(
"file",
"original.txt",
"text/plain",
TEST_CONTENT.getBytes()
);
// Mock行为
when(minioUtil.uploadFile(file, DEFAULT_BUCKET, customFileName)).thenReturn(customFileName);
// 执行测试
String result = minioService.uploadFileWithName(file, customFileName);
// 验证结果
assertNotNull(result);
assertTrue(result.contains(customFileName));
// 验证调用
verify(minioUtil).uploadFile(file, DEFAULT_BUCKET, customFileName);
}
@Test
@Order(4)
@DisplayName("上传文件流")
void testUploadInputStream() {
// 准备测试数据
InputStream inputStream = new ByteArrayInputStream(TEST_CONTENT.getBytes());
String contentType = "text/plain";
// Mock行为
when(minioUtil.uploadFile(inputStream, DEFAULT_BUCKET, TEST_FILE_NAME, contentType)).thenReturn(true);
// 执行测试
String result = minioService.uploadFile(inputStream, TEST_FILE_NAME, contentType);
// 验证结果
assertNotNull(result);
assertTrue(result.contains(TEST_FILE_NAME));
// 验证调用
verify(minioUtil).uploadFile(inputStream, DEFAULT_BUCKET, TEST_FILE_NAME, contentType);
}
@Test
@Order(5)
@DisplayName("上传文件流失败")
void testUploadInputStreamFailure() {
// 准备测试数据
InputStream inputStream = new ByteArrayInputStream(TEST_CONTENT.getBytes());
String contentType = "text/plain";
// Mock行为 - 返回失败
when(minioUtil.uploadFile(inputStream, DEFAULT_BUCKET, TEST_FILE_NAME, contentType)).thenReturn(false);
// 执行测试并验证异常
RuntimeException exception = assertThrows(RuntimeException.class, () -> {
minioService.uploadFile(inputStream, TEST_FILE_NAME, contentType);
});
assertEquals("文件上传失败", exception.getMessage());
}
}
@Nested
@DisplayName("文件下载测试")
class DownloadTests {
@Test
@Order(6)
@DisplayName("从默认存储桶下载文件")
void testDownloadFileFromDefaultBucket() {
// 准备测试数据
InputStream expectedStream = new ByteArrayInputStream(TEST_CONTENT.getBytes());
// Mock行为
when(minioUtil.downloadFile(DEFAULT_BUCKET, TEST_FILE_NAME)).thenReturn(expectedStream);
// 执行测试
InputStream result = minioService.downloadFile(TEST_FILE_NAME);
// 验证结果
assertNotNull(result);
assertEquals(expectedStream, result);
// 验证调用
verify(minioUtil).downloadFile(DEFAULT_BUCKET, TEST_FILE_NAME);
}
@Test
@Order(7)
@DisplayName("从指定存储桶下载文件")
void testDownloadFileFromSpecificBucket() {
// 准备测试数据
String customBucket = "custom-bucket";
InputStream expectedStream = new ByteArrayInputStream(TEST_CONTENT.getBytes());
// Mock行为
when(minioUtil.downloadFile(customBucket, TEST_FILE_NAME)).thenReturn(expectedStream);
// 执行测试
InputStream result = minioService.downloadFile(customBucket, TEST_FILE_NAME);
// 验证结果
assertNotNull(result);
assertEquals(expectedStream, result);
// 验证调用
verify(minioUtil).downloadFile(customBucket, TEST_FILE_NAME);
}
}
@Nested
@DisplayName("文件删除测试")
class DeleteTests {
@Test
@Order(8)
@DisplayName("删除默认存储桶中的文件")
void testDeleteFileFromDefaultBucket() {
// Mock行为
when(minioUtil.removeFile(DEFAULT_BUCKET, TEST_FILE_NAME)).thenReturn(true);
// 执行测试
boolean result = minioService.deleteFile(TEST_FILE_NAME);
// 验证结果
assertTrue(result);
// 验证调用
verify(minioUtil).removeFile(DEFAULT_BUCKET, TEST_FILE_NAME);
}
@Test
@Order(9)
@DisplayName("删除指定存储桶中的文件")
void testDeleteFileFromSpecificBucket() {
// 准备测试数据
String customBucket = "custom-bucket";
// Mock行为
when(minioUtil.removeFile(customBucket, TEST_FILE_NAME)).thenReturn(true);
// 执行测试
boolean result = minioService.deleteFile(customBucket, TEST_FILE_NAME);
// 验证结果
assertTrue(result);
// 验证调用
verify(minioUtil).removeFile(customBucket, TEST_FILE_NAME);
}
@Test
@Order(10)
@DisplayName("删除文件失败")
void testDeleteFileFailure() {
// Mock行为 - 返回失败
when(minioUtil.removeFile(DEFAULT_BUCKET, TEST_FILE_NAME)).thenReturn(false);
// 执行测试
boolean result = minioService.deleteFile(TEST_FILE_NAME);
// 验证结果
assertFalse(result);
}
}
@Nested
@DisplayName("文件列表测试")
class ListTests {
@Test
@Order(11)
@DisplayName("获取默认存储桶文件列表")
void testListFilesFromDefaultBucket() {
// 准备测试数据
List<String> expectedFiles = Arrays.asList("file1.txt", "file2.txt", "file3.txt");
// Mock行为
when(minioUtil.listFiles(DEFAULT_BUCKET)).thenReturn(expectedFiles);
// 执行测试
List<String> result = minioService.listFiles();
// 验证结果
assertNotNull(result);
assertEquals(3, result.size());
assertEquals(expectedFiles, result);
// 验证调用
verify(minioUtil).listFiles(DEFAULT_BUCKET);
}
@Test
@Order(12)
@DisplayName("获取指定存储桶文件列表")
void testListFilesFromSpecificBucket() {
// 准备测试数据
String customBucket = "custom-bucket";
List<String> expectedFiles = Arrays.asList("custom1.txt", "custom2.txt");
// Mock行为
when(minioUtil.listFiles(customBucket)).thenReturn(expectedFiles);
// 执行测试
List<String> result = minioService.listFiles(customBucket);
// 验证结果
assertNotNull(result);
assertEquals(2, result.size());
assertEquals(expectedFiles, result);
// 验证调用
verify(minioUtil).listFiles(customBucket);
}
@Test
@Order(13)
@DisplayName("获取文件详细信息列表")
void testListFileDetails() {
// 准备测试数据
MinIOUtil.FileInfo fileInfo1 = new MinIOUtil.FileInfo();
fileInfo1.setObjectName("file1.txt");
fileInfo1.setSize(100L);
fileInfo1.setLastModified(ZonedDateTime.now());
fileInfo1.setEtag("etag1");
MinIOUtil.FileInfo fileInfo2 = new MinIOUtil.FileInfo();
fileInfo2.setObjectName("file2.txt");
fileInfo2.setSize(200L);
fileInfo2.setLastModified(ZonedDateTime.now());
fileInfo2.setEtag("etag2");
List<MinIOUtil.FileInfo> expectedFiles = Arrays.asList(fileInfo1, fileInfo2);
// Mock行为
when(minioUtil.listFileDetails(DEFAULT_BUCKET)).thenReturn(expectedFiles);
// 执行测试
List<MinIOUtil.FileInfo> result = minioService.listFileDetails();
// 验证结果
assertNotNull(result);
assertEquals(2, result.size());
assertEquals(expectedFiles, result);
// 验证调用
verify(minioUtil).listFileDetails(DEFAULT_BUCKET);
}
}
@Nested
@DisplayName("预签名URL测试")
class PresignedUrlTests {
@Test
@Order(14)
@DisplayName("获取预签名URL指定过期时间")
void testGetPresignedUrlWithExpires() {
// 准备测试数据
int expires = 3600; // 1小时
String expectedUrl = "http://localhost:9000/test-bucket/test-file.txt?X-Amz-Expires=3600";
// Mock行为
when(minioUtil.getPresignedObjectUrl(DEFAULT_BUCKET, TEST_FILE_NAME, expires)).thenReturn(expectedUrl);
// 执行测试
String result = minioService.getPresignedUrl(TEST_FILE_NAME, expires);
// 验证结果
assertNotNull(result);
assertEquals(expectedUrl, result);
// 验证调用
verify(minioUtil).getPresignedObjectUrl(DEFAULT_BUCKET, TEST_FILE_NAME, expires);
}
@Test
@Order(15)
@DisplayName("获取预签名URL默认过期时间")
void testGetPresignedUrlWithDefaultExpires() {
// 准备测试数据
String expectedUrl = "http://localhost:9000/test-bucket/test-file.txt?X-Amz-Expires=604800";
// Mock行为
when(minioUtil.getPresignedObjectUrl(DEFAULT_BUCKET, TEST_FILE_NAME)).thenReturn(expectedUrl);
// 执行测试
String result = minioService.getPresignedUrl(TEST_FILE_NAME);
// 验证结果
assertNotNull(result);
assertEquals(expectedUrl, result);
// 验证调用
verify(minioUtil).getPresignedObjectUrl(DEFAULT_BUCKET, TEST_FILE_NAME);
}
}
@Nested
@DisplayName("文件操作测试")
class FileOperationTests {
@Test
@Order(16)
@DisplayName("复制文件(同存储桶)")
void testCopyFileInSameBucket() {
// 准备测试数据
String sourceFileName = "source.txt";
String targetFileName = "target.txt";
// Mock行为
when(minioUtil.copyFile(DEFAULT_BUCKET, sourceFileName, DEFAULT_BUCKET, targetFileName)).thenReturn(true);
// 执行测试
boolean result = minioService.copyFile(sourceFileName, targetFileName);
// 验证结果
assertTrue(result);
// 验证调用
verify(minioUtil).copyFile(DEFAULT_BUCKET, sourceFileName, DEFAULT_BUCKET, targetFileName);
}
@Test
@Order(17)
@DisplayName("复制文件到其他存储桶")
void testCopyFileToOtherBucket() {
// 准备测试数据
String sourceFileName = "source.txt";
String targetBucket = "target-bucket";
String targetFileName = "target.txt";
// Mock行为
when(minioUtil.copyFile(DEFAULT_BUCKET, sourceFileName, targetBucket, targetFileName)).thenReturn(true);
// 执行测试
boolean result = minioService.copyFile(sourceFileName, targetBucket, targetFileName);
// 验证结果
assertTrue(result);
// 验证调用
verify(minioUtil).copyFile(DEFAULT_BUCKET, sourceFileName, targetBucket, targetFileName);
}
@Test
@Order(18)
@DisplayName("检查文件是否存在")
void testFileExists() {
// Mock行为 - 文件存在
when(minioUtil.getFileInfo(DEFAULT_BUCKET, TEST_FILE_NAME)).thenReturn(mock(io.minio.StatObjectResponse.class));
// 执行测试
boolean result = minioService.fileExists(TEST_FILE_NAME);
// 验证结果
assertTrue(result);
// 验证调用
verify(minioUtil).getFileInfo(DEFAULT_BUCKET, TEST_FILE_NAME);
}
@Test
@Order(19)
@DisplayName("检查文件不存在")
void testFileNotExists() {
// Mock行为 - 文件不存在
when(minioUtil.getFileInfo(DEFAULT_BUCKET, TEST_FILE_NAME)).thenReturn(null);
// 执行测试
boolean result = minioService.fileExists(TEST_FILE_NAME);
// 验证结果
assertFalse(result);
// 验证调用
verify(minioUtil).getFileInfo(DEFAULT_BUCKET, TEST_FILE_NAME);
}
}
@Nested
@DisplayName("存储桶管理测试")
class BucketManagementTests {
@Test
@Order(20)
@DisplayName("创建存储桶")
void testCreateBucket() {
// 准备测试数据
String bucketName = "new-bucket";
// Mock行为
when(minioUtil.createBucket(bucketName)).thenReturn(true);
// 执行测试
boolean result = minioService.createBucket(bucketName);
// 验证结果
assertTrue(result);
// 验证调用
verify(minioUtil).createBucket(bucketName);
}
@Test
@Order(21)
@DisplayName("删除存储桶")
void testDeleteBucket() {
// 准备测试数据
String bucketName = "old-bucket";
// Mock行为
when(minioUtil.removeBucket(bucketName)).thenReturn(true);
// 执行测试
boolean result = minioService.deleteBucket(bucketName);
// 验证结果
assertTrue(result);
// 验证调用
verify(minioUtil).removeBucket(bucketName);
}
@Test
@Order(22)
@DisplayName("获取所有存储桶")
void testGetAllBuckets() {
// 准备测试数据
List<String> expectedBuckets = Arrays.asList("bucket1", "bucket2", "bucket3");
// Mock行为
when(minioUtil.getAllBuckets()).thenReturn(expectedBuckets);
// 执行测试
List<String> result = minioService.getAllBuckets();
// 验证结果
assertNotNull(result);
assertEquals(3, result.size());
assertEquals(expectedBuckets, result);
// 验证调用
verify(minioUtil).getAllBuckets();
}
}
@Nested
@DisplayName("URL生成测试")
class UrlGenerationTests {
@Test
@Order(23)
@DisplayName("获取文件URL默认存储桶")
void testGetFileUrlFromDefaultBucket() {
// 执行测试
String result = minioService.getFileUrl(TEST_FILE_NAME);
// 验证结果
assertNotNull(result);
assertEquals(DEFAULT_ENDPOINT + "/" + DEFAULT_BUCKET + "/" + TEST_FILE_NAME, result);
// 验证调用
verify(minioConfig).getFileUrlPrefix();
}
@Test
@Order(24)
@DisplayName("获取文件URL指定存储桶")
void testGetFileUrlFromSpecificBucket() {
// 准备测试数据
String customBucket = "custom-bucket";
// 执行测试
String result = minioService.getFileUrl(customBucket, TEST_FILE_NAME);
// 验证结果
assertNotNull(result);
assertEquals(DEFAULT_ENDPOINT + "/" + customBucket + "/" + TEST_FILE_NAME, result);
// 验证调用
verify(minioConfig).getEndpoint();
}
}
@Test
@Order(25)
@DisplayName("验证所有Mock对象的交互")
void testAllMockInteractions() {
// 这个测试确保我们没有遗漏任何重要的Mock验证
// 在实际项目中,这通常不是必需的,但有助于确保测试的完整性
// 执行一个简单的操作来触发Mock交互
minioService.listFiles();
// 验证基本的Mock交互
verify(minioConfig, atLeastOnce()).getBucketName();
verify(minioUtil, atLeastOnce()).listFiles(anyString());
}
}

View File

@@ -0,0 +1,78 @@
package com.rj.service;
import com.rj.config.SiliconFlowConfig;
import com.rj.dto.SiliconFlowTtsRequest;
import com.rj.dto.SiliconFlowTtsResponse;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.TestPropertySource;
import static org.junit.jupiter.api.Assertions.*;
/**
* SiliconFlow TTS 服务测试类
*
* @author rj
* @date 2025-01-02
*/
@SpringBootTest
@TestPropertySource(properties = {
"siliconflow.enabled=true",
"siliconflow.api-key=test-api-key",
"siliconflow.base-url=https://api.siliconflow.cn/v1"
})
public class SiliconFlowTtsServiceTest {
@Autowired
private SiliconFlowTtsService ttsService;
@Autowired
private SiliconFlowConfig config;
@Test
public void testServiceConfiguration() {
assertNotNull(config);
assertTrue(config.isEnabled());
assertEquals("https://api.siliconflow.cn/v1", config.getBaseUrl());
assertEquals("fnlp/MOSS-TTSD-v0.5", config.getDefaultTtsModel());
}
@Test
public void testServiceAvailability() {
// 注意这个测试需要真实的API Key才能通过
// 在测试环境中,我们主要测试配置是否正确
assertNotNull(ttsService);
}
@Test
public void testTtsRequestCreation() {
SiliconFlowTtsRequest request = new SiliconFlowTtsRequest();
request.setModel("fnlp/MOSS-TTSD-v0.5");
request.setInput("[S1]Hello, how are you today?[S2]I'm doing great, thanks for asking![S1]That's wonderful to hear");
// voice参数有默认值但也可以手动设置
assertNotNull(request);
assertEquals("fnlp/MOSS-TTSD-v0.5", request.getModel());
assertNotNull(request.getInput());
assertEquals("fnlp/MOSS-TTSD-v0.5:claire", request.getVoice()); // 验证默认voice参数
}
@Test
public void testTtsResponseCreation() {
SiliconFlowTtsResponse successResponse = SiliconFlowTtsResponse.success(
"base64AudioData", "wav", 22050, 5.5
);
assertTrue(successResponse.isSuccess());
assertEquals("base64AudioData", successResponse.getAudio());
assertEquals("wav", successResponse.getFormat());
assertEquals(22050, successResponse.getSampleRate());
assertEquals(5.5, successResponse.getDuration());
SiliconFlowTtsResponse errorResponse = SiliconFlowTtsResponse.error("API调用失败");
assertFalse(errorResponse.isSuccess());
assertEquals("API调用失败", errorResponse.getMessage());
}
}