feat(sync): full code sync from release
This commit is contained in:
263
backend/internal/service/sora_s3_storage_test.go
Normal file
263
backend/internal/service/sora_s3_storage_test.go
Normal file
@@ -0,0 +1,263 @@
|
||||
//go:build unit
|
||||
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/Wei-Shaw/sub2api/internal/config"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// ==================== RefreshClient ====================
|
||||
|
||||
func TestRefreshClient(t *testing.T) {
|
||||
s := newS3StorageWithCDN("https://cdn.example.com")
|
||||
require.NotNil(t, s.client)
|
||||
require.NotNil(t, s.cfg)
|
||||
|
||||
s.RefreshClient()
|
||||
require.Nil(t, s.client)
|
||||
require.Nil(t, s.cfg)
|
||||
}
|
||||
|
||||
func TestRefreshClient_AlreadyNil(t *testing.T) {
|
||||
s := NewSoraS3Storage(nil)
|
||||
s.RefreshClient() // 不应 panic
|
||||
require.Nil(t, s.client)
|
||||
require.Nil(t, s.cfg)
|
||||
}
|
||||
|
||||
// ==================== GetMediaTypeFromKey ====================
|
||||
|
||||
func TestGetMediaTypeFromKey_VideoExtensions(t *testing.T) {
|
||||
for _, ext := range []string{".mp4", ".mov", ".webm", ".m4v", ".avi", ".mkv", ".3gp", ".flv"} {
|
||||
require.Equal(t, "video", GetMediaTypeFromKey("path/to/file"+ext), "ext=%s", ext)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetMediaTypeFromKey_VideoUpperCase(t *testing.T) {
|
||||
require.Equal(t, "video", GetMediaTypeFromKey("file.MP4"))
|
||||
require.Equal(t, "video", GetMediaTypeFromKey("file.MOV"))
|
||||
}
|
||||
|
||||
func TestGetMediaTypeFromKey_ImageExtensions(t *testing.T) {
|
||||
require.Equal(t, "image", GetMediaTypeFromKey("file.png"))
|
||||
require.Equal(t, "image", GetMediaTypeFromKey("file.jpg"))
|
||||
require.Equal(t, "image", GetMediaTypeFromKey("file.jpeg"))
|
||||
require.Equal(t, "image", GetMediaTypeFromKey("file.gif"))
|
||||
require.Equal(t, "image", GetMediaTypeFromKey("file.webp"))
|
||||
}
|
||||
|
||||
func TestGetMediaTypeFromKey_NoExtension(t *testing.T) {
|
||||
require.Equal(t, "image", GetMediaTypeFromKey("file"))
|
||||
require.Equal(t, "image", GetMediaTypeFromKey("path/to/file"))
|
||||
}
|
||||
|
||||
func TestGetMediaTypeFromKey_UnknownExtension(t *testing.T) {
|
||||
require.Equal(t, "image", GetMediaTypeFromKey("file.bin"))
|
||||
require.Equal(t, "image", GetMediaTypeFromKey("file.xyz"))
|
||||
}
|
||||
|
||||
// ==================== Enabled ====================
|
||||
|
||||
func TestEnabled_NilSettingService(t *testing.T) {
|
||||
s := NewSoraS3Storage(nil)
|
||||
require.False(t, s.Enabled(context.Background()))
|
||||
}
|
||||
|
||||
func TestEnabled_ConfigDisabled(t *testing.T) {
|
||||
settingRepo := newStubSettingRepoForQuota(map[string]string{
|
||||
SettingKeySoraS3Enabled: "false",
|
||||
SettingKeySoraS3Bucket: "test-bucket",
|
||||
})
|
||||
settingService := NewSettingService(settingRepo, &config.Config{})
|
||||
s := NewSoraS3Storage(settingService)
|
||||
require.False(t, s.Enabled(context.Background()))
|
||||
}
|
||||
|
||||
func TestEnabled_ConfigEnabledWithBucket(t *testing.T) {
|
||||
settingRepo := newStubSettingRepoForQuota(map[string]string{
|
||||
SettingKeySoraS3Enabled: "true",
|
||||
SettingKeySoraS3Bucket: "my-bucket",
|
||||
})
|
||||
settingService := NewSettingService(settingRepo, &config.Config{})
|
||||
s := NewSoraS3Storage(settingService)
|
||||
require.True(t, s.Enabled(context.Background()))
|
||||
}
|
||||
|
||||
func TestEnabled_ConfigEnabledEmptyBucket(t *testing.T) {
|
||||
settingRepo := newStubSettingRepoForQuota(map[string]string{
|
||||
SettingKeySoraS3Enabled: "true",
|
||||
})
|
||||
settingService := NewSettingService(settingRepo, &config.Config{})
|
||||
s := NewSoraS3Storage(settingService)
|
||||
require.False(t, s.Enabled(context.Background()))
|
||||
}
|
||||
|
||||
// ==================== initClient ====================
|
||||
|
||||
func TestInitClient_Disabled(t *testing.T) {
|
||||
settingRepo := newStubSettingRepoForQuota(map[string]string{
|
||||
SettingKeySoraS3Enabled: "false",
|
||||
})
|
||||
settingService := NewSettingService(settingRepo, &config.Config{})
|
||||
s := NewSoraS3Storage(settingService)
|
||||
|
||||
_, _, err := s.getClient(context.Background())
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), "disabled")
|
||||
}
|
||||
|
||||
func TestInitClient_IncompleteConfig(t *testing.T) {
|
||||
settingRepo := newStubSettingRepoForQuota(map[string]string{
|
||||
SettingKeySoraS3Enabled: "true",
|
||||
SettingKeySoraS3Bucket: "test-bucket",
|
||||
// 缺少 access_key_id 和 secret_access_key
|
||||
})
|
||||
settingService := NewSettingService(settingRepo, &config.Config{})
|
||||
s := NewSoraS3Storage(settingService)
|
||||
|
||||
_, _, err := s.getClient(context.Background())
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), "incomplete")
|
||||
}
|
||||
|
||||
func TestInitClient_DefaultRegion(t *testing.T) {
|
||||
settingRepo := newStubSettingRepoForQuota(map[string]string{
|
||||
SettingKeySoraS3Enabled: "true",
|
||||
SettingKeySoraS3Bucket: "test-bucket",
|
||||
SettingKeySoraS3AccessKeyID: "AKID",
|
||||
SettingKeySoraS3SecretAccessKey: "SECRET",
|
||||
// Region 为空 → 默认 us-east-1
|
||||
})
|
||||
settingService := NewSettingService(settingRepo, &config.Config{})
|
||||
s := NewSoraS3Storage(settingService)
|
||||
|
||||
client, cfg, err := s.getClient(context.Background())
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, client)
|
||||
require.Equal(t, "test-bucket", cfg.Bucket)
|
||||
}
|
||||
|
||||
func TestInitClient_DoubleCheck(t *testing.T) {
|
||||
// 验证双重检查锁定:第二次 getClient 命中缓存
|
||||
settingRepo := newStubSettingRepoForQuota(map[string]string{
|
||||
SettingKeySoraS3Enabled: "true",
|
||||
SettingKeySoraS3Bucket: "test-bucket",
|
||||
SettingKeySoraS3AccessKeyID: "AKID",
|
||||
SettingKeySoraS3SecretAccessKey: "SECRET",
|
||||
})
|
||||
settingService := NewSettingService(settingRepo, &config.Config{})
|
||||
s := NewSoraS3Storage(settingService)
|
||||
|
||||
client1, _, err1 := s.getClient(context.Background())
|
||||
require.NoError(t, err1)
|
||||
client2, _, err2 := s.getClient(context.Background())
|
||||
require.NoError(t, err2)
|
||||
require.Equal(t, client1, client2) // 同一客户端实例
|
||||
}
|
||||
|
||||
func TestInitClient_NilSettingService(t *testing.T) {
|
||||
s := NewSoraS3Storage(nil)
|
||||
_, _, err := s.getClient(context.Background())
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), "setting service not available")
|
||||
}
|
||||
|
||||
// ==================== GenerateObjectKey ====================
|
||||
|
||||
func TestGenerateObjectKey_ExtWithoutDot(t *testing.T) {
|
||||
s := NewSoraS3Storage(nil)
|
||||
key := s.GenerateObjectKey("", 1, "mp4")
|
||||
require.Contains(t, key, ".mp4")
|
||||
require.True(t, len(key) > 0)
|
||||
}
|
||||
|
||||
func TestGenerateObjectKey_ExtWithDot(t *testing.T) {
|
||||
s := NewSoraS3Storage(nil)
|
||||
key := s.GenerateObjectKey("", 1, ".mp4")
|
||||
require.Contains(t, key, ".mp4")
|
||||
// 不应出现 ..mp4
|
||||
require.NotContains(t, key, "..mp4")
|
||||
}
|
||||
|
||||
func TestGenerateObjectKey_WithPrefix(t *testing.T) {
|
||||
s := NewSoraS3Storage(nil)
|
||||
key := s.GenerateObjectKey("uploads/", 42, ".png")
|
||||
require.True(t, len(key) > 0)
|
||||
require.Contains(t, key, "uploads/sora/42/")
|
||||
}
|
||||
|
||||
func TestGenerateObjectKey_PrefixWithoutTrailingSlash(t *testing.T) {
|
||||
s := NewSoraS3Storage(nil)
|
||||
key := s.GenerateObjectKey("uploads", 42, ".png")
|
||||
require.Contains(t, key, "uploads/sora/42/")
|
||||
}
|
||||
|
||||
// ==================== GeneratePresignedURL ====================
|
||||
|
||||
func TestGeneratePresignedURL_GetClientError(t *testing.T) {
|
||||
s := NewSoraS3Storage(nil) // settingService=nil → getClient 失败
|
||||
_, err := s.GeneratePresignedURL(context.Background(), "key", 3600)
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
// ==================== GetAccessURL ====================
|
||||
|
||||
func TestGetAccessURL_CDN(t *testing.T) {
|
||||
s := newS3StorageWithCDN("https://cdn.example.com")
|
||||
url, err := s.GetAccessURL(context.Background(), "sora/1/2024/01/01/video.mp4")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "https://cdn.example.com/sora/1/2024/01/01/video.mp4", url)
|
||||
}
|
||||
|
||||
func TestGetAccessURL_CDNTrailingSlash(t *testing.T) {
|
||||
s := newS3StorageWithCDN("https://cdn.example.com/")
|
||||
url, err := s.GetAccessURL(context.Background(), "key.mp4")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "https://cdn.example.com/key.mp4", url)
|
||||
}
|
||||
|
||||
func TestGetAccessURL_GetClientError(t *testing.T) {
|
||||
s := NewSoraS3Storage(nil)
|
||||
_, err := s.GetAccessURL(context.Background(), "key")
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
// ==================== TestConnection ====================
|
||||
|
||||
func TestTestConnection_GetClientError(t *testing.T) {
|
||||
s := NewSoraS3Storage(nil)
|
||||
err := s.TestConnection(context.Background())
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
// ==================== UploadFromURL ====================
|
||||
|
||||
func TestUploadFromURL_GetClientError(t *testing.T) {
|
||||
s := NewSoraS3Storage(nil)
|
||||
_, _, err := s.UploadFromURL(context.Background(), 1, "https://example.com/file.mp4")
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
// ==================== DeleteObjects ====================
|
||||
|
||||
func TestDeleteObjects_EmptyKeys(t *testing.T) {
|
||||
s := NewSoraS3Storage(nil)
|
||||
err := s.DeleteObjects(context.Background(), []string{})
|
||||
require.NoError(t, err) // 空列表直接返回
|
||||
}
|
||||
|
||||
func TestDeleteObjects_NilKeys(t *testing.T) {
|
||||
s := NewSoraS3Storage(nil)
|
||||
err := s.DeleteObjects(context.Background(), nil)
|
||||
require.NoError(t, err) // nil 列表直接返回
|
||||
}
|
||||
|
||||
func TestDeleteObjects_GetClientError(t *testing.T) {
|
||||
s := NewSoraS3Storage(nil)
|
||||
err := s.DeleteObjects(context.Background(), []string{"key1", "key2"})
|
||||
require.Error(t, err)
|
||||
}
|
||||
Reference in New Issue
Block a user