Files
sub2api/backend/internal/service/sora_s3_storage_test.go
2026-02-28 15:01:20 +08:00

264 lines
8.3 KiB
Go

//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)
}