package config import ( "testing" "time" "github.com/stretchr/testify/assert" ) func TestSMTPConfig_Validate(t *testing.T) { tests := []struct { name string config SMTPConfig wantErr bool errMsg string }{ { name: "有效的 SMTP 配置", config: SMTPConfig{ Enable: true, Sort: 1, Host: "smtp.gmail.com", Port: 587, Username: "test@gmail.com", Password: "password", }, wantErr: false, }, { name: "未啟用的配置(不驗證)", config: SMTPConfig{ Enable: false, }, wantErr: false, }, { name: "缺少 Host", config: SMTPConfig{ Enable: true, Port: 587, Username: "test@gmail.com", Password: "password", }, wantErr: true, errMsg: "smtp host is required", }, { name: "無效的 Port", config: SMTPConfig{ Enable: true, Host: "smtp.gmail.com", Port: 99999, Username: "test@gmail.com", Password: "password", }, wantErr: true, errMsg: "smtp port must be between 1 and 65535", }, { name: "缺少 Username", config: SMTPConfig{ Enable: true, Host: "smtp.gmail.com", Port: 587, Password: "password", }, wantErr: true, errMsg: "smtp username is required", }, { name: "負數的 Sort", config: SMTPConfig{ Enable: true, Sort: -1, Host: "smtp.gmail.com", Port: 587, Username: "test@gmail.com", Password: "password", }, wantErr: true, errMsg: "smtp sort must be >= 0", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { err := tt.config.Validate() if tt.wantErr { assert.Error(t, err) if tt.errMsg != "" { assert.Contains(t, err.Error(), tt.errMsg) } } else { assert.NoError(t, err) } }) } } func TestAmazonSesSettings_Validate(t *testing.T) { tests := []struct { name string config AmazonSesSettings wantErr bool errMsg string }{ { name: "有效的 AWS SES 配置", config: AmazonSesSettings{ Enable: true, Sort: 1, PoolSize: 10, Region: "us-west-2", Sender: "noreply@example.com", AccessKey: "AKIAIOSFODNN7EXAMPLE", SecretKey: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", }, wantErr: false, }, { name: "未啟用的配置", config: AmazonSesSettings{ Enable: false, }, wantErr: false, }, { name: "缺少 Region", config: AmazonSesSettings{ Enable: true, Sender: "noreply@example.com", AccessKey: "AKIAIOSFODNN7EXAMPLE", SecretKey: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", }, wantErr: true, errMsg: "aws ses region is required", }, { name: "缺少 AccessKey", config: AmazonSesSettings{ Enable: true, Region: "us-west-2", Sender: "noreply@example.com", SecretKey: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", }, wantErr: true, errMsg: "aws ses access key is required", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { err := tt.config.Validate() if tt.wantErr { assert.Error(t, err) if tt.errMsg != "" { assert.Contains(t, err.Error(), tt.errMsg) } } else { assert.NoError(t, err) } }) } } func TestMitakeSMSSender_Validate(t *testing.T) { tests := []struct { name string config MitakeSMSSender wantErr bool errMsg string }{ { name: "有效的 Mitake 配置", config: MitakeSMSSender{ Enable: true, Sort: 1, PoolSize: 5, User: "testuser", Password: "testpass", }, wantErr: false, }, { name: "缺少 User", config: MitakeSMSSender{ Enable: true, Password: "testpass", }, wantErr: true, errMsg: "mitake user is required", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { err := tt.config.Validate() if tt.wantErr { assert.Error(t, err) if tt.errMsg != "" { assert.Contains(t, err.Error(), tt.errMsg) } } else { assert.NoError(t, err) } }) } } func TestDeliveryConfig_Validate(t *testing.T) { tests := []struct { name string config DeliveryConfig wantErr bool errMsg string }{ { name: "有效的配置", config: DeliveryConfig{ MaxRetries: 3, InitialDelay: 100 * time.Millisecond, BackoffFactor: 2.0, MaxDelay: 30 * time.Second, Timeout: 30 * time.Second, }, wantErr: false, }, { name: "MaxRetries 為負數", config: DeliveryConfig{ MaxRetries: -1, Timeout: 30 * time.Second, }, wantErr: true, errMsg: "max_retries must be >= 0", }, { name: "MaxRetries 過大", config: DeliveryConfig{ MaxRetries: 20, Timeout: 30 * time.Second, }, wantErr: true, errMsg: "max_retries should not exceed 10", }, { name: "BackoffFactor 小於 1.0", config: DeliveryConfig{ MaxRetries: 3, BackoffFactor: 0.5, Timeout: 30 * time.Second, }, wantErr: true, errMsg: "backoff_factor must be >= 1.0", }, { name: "Timeout 為 0", config: DeliveryConfig{ MaxRetries: 3, BackoffFactor: 2.0, Timeout: 0, }, wantErr: true, errMsg: "timeout must be > 0", }, { name: "InitialDelay 大於 MaxDelay", config: DeliveryConfig{ MaxRetries: 3, InitialDelay: 1 * time.Minute, MaxDelay: 10 * time.Second, Timeout: 30 * time.Second, }, wantErr: true, errMsg: "initial_delay", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { err := tt.config.Validate() if tt.wantErr { assert.Error(t, err) if tt.errMsg != "" { assert.Contains(t, err.Error(), tt.errMsg) } } else { assert.NoError(t, err) } }) } } func TestDeliveryConfig_SetDefaults(t *testing.T) { config := DeliveryConfig{} config.SetDefaults() assert.Equal(t, 3, config.MaxRetries) assert.Equal(t, 100*time.Millisecond, config.InitialDelay) assert.Equal(t, 2.0, config.BackoffFactor) assert.Equal(t, 30*time.Second, config.MaxDelay) assert.Equal(t, 30*time.Second, config.Timeout) // 測試不覆蓋已設置的值 config2 := DeliveryConfig{ MaxRetries: 5, Timeout: 60 * time.Second, } config2.SetDefaults() assert.Equal(t, 5, config2.MaxRetries) // 保持原值 assert.Equal(t, 60*time.Second, config2.Timeout) // 保持原值 assert.Equal(t, 100*time.Millisecond, config2.InitialDelay) // 設置默認值 }