feat: Enhance dependency injection and service integration

- Updated main.go to initialize email service and include it in the dependency injection container.
- Refactored handlers to utilize context in service method calls, improving consistency and error handling.
- Introduced new service options for upload, security, and captcha services, enhancing modularity and testability.
- Removed unused repository implementations to streamline the codebase.

This commit continues the effort to improve the architecture by ensuring all services are properly injected and utilized across the application.
This commit is contained in:
lan
2025-12-02 22:52:33 +08:00
parent 792e96b238
commit 034e02e93a
54 changed files with 2305 additions and 2708 deletions

View File

@@ -1,358 +0,0 @@
package service
import (
"crypto/rand"
"crypto/rsa"
"strings"
"testing"
"time"
"go.uber.org/zap/zaptest"
)
// TestSignatureService_Constants 测试签名服务相关常量
func TestSignatureService_Constants(t *testing.T) {
if RSAKeySize != 4096 {
t.Errorf("RSAKeySize = %d, want 4096", RSAKeySize)
}
if PrivateKeyRedisKey == "" {
t.Error("PrivateKeyRedisKey should not be empty")
}
if PublicKeyRedisKey == "" {
t.Error("PublicKeyRedisKey should not be empty")
}
if KeyExpirationTime != 24*7*time.Hour {
t.Errorf("KeyExpirationTime = %v, want 7 days", KeyExpirationTime)
}
if CertificateRefreshInterval != 24*time.Hour {
t.Errorf("CertificateRefreshInterval = %v, want 24 hours", CertificateRefreshInterval)
}
if CertificateExpirationPeriod != 24*7*time.Hour {
t.Errorf("CertificateExpirationPeriod = %v, want 7 days", CertificateExpirationPeriod)
}
}
// TestSignatureService_DataValidation 测试签名数据验证逻辑
func TestSignatureService_DataValidation(t *testing.T) {
tests := []struct {
name string
data string
wantValid bool
}{
{
name: "非空数据有效",
data: "test data",
wantValid: true,
},
{
name: "空数据无效",
data: "",
wantValid: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
isValid := tt.data != ""
if isValid != tt.wantValid {
t.Errorf("Data validation failed: got %v, want %v", isValid, tt.wantValid)
}
})
}
}
// TestPlayerCertificate_Structure 测试PlayerCertificate结构
func TestPlayerCertificate_Structure(t *testing.T) {
cert := PlayerCertificate{
ExpiresAt: "2025-01-01T00:00:00Z",
RefreshedAfter: "2025-01-01T00:00:00Z",
PublicKeySignature: "signature",
PublicKeySignatureV2: "signaturev2",
}
// 验证结构体字段
if cert.ExpiresAt == "" {
t.Error("ExpiresAt should not be empty")
}
if cert.RefreshedAfter == "" {
t.Error("RefreshedAfter should not be empty")
}
// PublicKeySignature是可选的
if cert.PublicKeySignature == "" {
t.Log("PublicKeySignature is optional")
}
}
// TestWrapString 测试字符串换行函数
func TestWrapString(t *testing.T) {
tests := []struct {
name string
str string
width int
expected string
}{
{
name: "正常换行",
str: "1234567890",
width: 5,
expected: "12345\n67890",
},
{
name: "字符串长度等于width",
str: "12345",
width: 5,
expected: "12345",
},
{
name: "字符串长度小于width",
str: "123",
width: 5,
expected: "123",
},
{
name: "width为0返回原字符串",
str: "1234567890",
width: 0,
expected: "1234567890",
},
{
name: "width为负数返回原字符串",
str: "1234567890",
width: -1,
expected: "1234567890",
},
{
name: "空字符串",
str: "",
width: 5,
expected: "",
},
{
name: "width为1",
str: "12345",
width: 1,
expected: "1\n2\n3\n4\n5",
},
{
name: "长字符串多次换行",
str: "123456789012345",
width: 5,
expected: "12345\n67890\n12345",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := WrapString(tt.str, tt.width)
if result != tt.expected {
t.Errorf("WrapString(%q, %d) = %q, want %q", tt.str, tt.width, result, tt.expected)
}
})
}
}
// TestWrapString_LineCount 测试换行后的行数
func TestWrapString_LineCount(t *testing.T) {
tests := []struct {
name string
str string
width int
wantLines int
}{
{
name: "10个字符width=5应该2行",
str: "1234567890",
width: 5,
wantLines: 2,
},
{
name: "15个字符width=5应该3行",
str: "123456789012345",
width: 5,
wantLines: 3,
},
{
name: "5个字符width=5应该1行",
str: "12345",
width: 5,
wantLines: 1,
},
{
name: "width为0应该1行",
str: "1234567890",
width: 0,
wantLines: 1,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := WrapString(tt.str, tt.width)
lines := strings.Count(result, "\n") + 1
if lines != tt.wantLines {
t.Errorf("Line count = %d, want %d (result: %q)", lines, tt.wantLines, result)
}
})
}
}
// TestWrapString_NoTrailingNewline 测试末尾不换行
func TestWrapString_NoTrailingNewline(t *testing.T) {
str := "1234567890"
result := WrapString(str, 5)
// 验证末尾没有换行符
if strings.HasSuffix(result, "\n") {
t.Error("Result should not end with newline")
}
// 验证包含换行符(除了最后一行)
if !strings.Contains(result, "\n") {
t.Error("Result should contain newline for multi-line output")
}
}
// TestEncodePrivateKeyToPEM_ActualCall 实际调用EncodePrivateKeyToPEM函数
func TestEncodePrivateKeyToPEM_ActualCall(t *testing.T) {
// 生成测试用的RSA私钥
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
t.Fatalf("生成RSA私钥失败: %v", err)
}
tests := []struct {
name string
keyType []string
wantError bool
}{
{
name: "默认类型",
keyType: []string{},
wantError: false,
},
{
name: "RSA类型",
keyType: []string{"RSA"},
wantError: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
pemBytes, err := EncodePrivateKeyToPEM(privateKey, tt.keyType...)
if (err != nil) != tt.wantError {
t.Errorf("EncodePrivateKeyToPEM() error = %v, wantError %v", err, tt.wantError)
return
}
if !tt.wantError {
if len(pemBytes) == 0 {
t.Error("EncodePrivateKeyToPEM() 返回的PEM字节不应为空")
}
pemStr := string(pemBytes)
// 验证PEM格式
if !strings.Contains(pemStr, "BEGIN") || !strings.Contains(pemStr, "END") {
t.Error("EncodePrivateKeyToPEM() 返回的PEM格式不正确")
}
// 验证类型
if len(tt.keyType) > 0 && tt.keyType[0] == "RSA" {
if !strings.Contains(pemStr, "RSA PRIVATE KEY") {
t.Error("EncodePrivateKeyToPEM() 应包含 'RSA PRIVATE KEY'")
}
} else {
if !strings.Contains(pemStr, "PRIVATE KEY") {
t.Error("EncodePrivateKeyToPEM() 应包含 'PRIVATE KEY'")
}
}
}
})
}
}
// TestEncodePublicKeyToPEM_ActualCall 实际调用EncodePublicKeyToPEM函数
func TestEncodePublicKeyToPEM_ActualCall(t *testing.T) {
logger := zaptest.NewLogger(t)
// 生成测试用的RSA密钥对
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
t.Fatalf("生成RSA密钥对失败: %v", err)
}
publicKey := &privateKey.PublicKey
tests := []struct {
name string
keyType []string
wantError bool
}{
{
name: "默认类型",
keyType: []string{},
wantError: false,
},
{
name: "RSA类型",
keyType: []string{"RSA"},
wantError: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
pemBytes, err := EncodePublicKeyToPEM(logger, publicKey, tt.keyType...)
if (err != nil) != tt.wantError {
t.Errorf("EncodePublicKeyToPEM() error = %v, wantError %v", err, tt.wantError)
return
}
if !tt.wantError {
if len(pemBytes) == 0 {
t.Error("EncodePublicKeyToPEM() 返回的PEM字节不应为空")
}
pemStr := string(pemBytes)
// 验证PEM格式
if !strings.Contains(pemStr, "BEGIN") || !strings.Contains(pemStr, "END") {
t.Error("EncodePublicKeyToPEM() 返回的PEM格式不正确")
}
// 验证类型
if len(tt.keyType) > 0 && tt.keyType[0] == "RSA" {
if !strings.Contains(pemStr, "RSA PUBLIC KEY") {
t.Error("EncodePublicKeyToPEM() 应包含 'RSA PUBLIC KEY'")
}
} else {
if !strings.Contains(pemStr, "PUBLIC KEY") {
t.Error("EncodePublicKeyToPEM() 应包含 'PUBLIC KEY'")
}
}
}
})
}
}
// TestEncodePublicKeyToPEM_NilKey 测试nil公钥
func TestEncodePublicKeyToPEM_NilKey(t *testing.T) {
logger := zaptest.NewLogger(t)
_, err := EncodePublicKeyToPEM(logger, nil)
if err == nil {
t.Error("EncodePublicKeyToPEM() 对于nil公钥应返回错误")
}
}
// TestNewSignatureService 测试创建SignatureService
func TestNewSignatureService(t *testing.T) {
logger := zaptest.NewLogger(t)
// 注意这里需要实际的redis client但我们只测试结构体创建
// 在实际测试中可以使用mock redis client
service := NewSignatureService(logger, nil)
if service == nil {
t.Error("NewSignatureService() 不应返回nil")
}
if service.logger != logger {
t.Error("NewSignatureService() logger 设置不正确")
}
}