package entities import ( "testing" "time" "github.com/stretchr/testify/assert" ) func TestSMSCode_Validate(t *testing.T) { tests := []struct { name string smsCode *SMSCode wantErr bool }{ { name: "有效验证码", smsCode: &SMSCode{ Phone: "13800138000", Code: "123456", Scene: SMSSceneRegister, ExpiresAt: time.Now().Add(time.Hour), }, wantErr: false, }, { name: "手机号为空", smsCode: &SMSCode{ Phone: "", Code: "123456", Scene: SMSSceneRegister, ExpiresAt: time.Now().Add(time.Hour), }, wantErr: true, }, { name: "验证码为空", smsCode: &SMSCode{ Phone: "13800138000", Code: "", Scene: SMSSceneRegister, ExpiresAt: time.Now().Add(time.Hour), }, wantErr: true, }, { name: "场景为空", smsCode: &SMSCode{ Phone: "13800138000", Code: "123456", Scene: "", ExpiresAt: time.Now().Add(time.Hour), }, wantErr: true, }, { name: "过期时间为零", smsCode: &SMSCode{ Phone: "13800138000", Code: "123456", Scene: SMSSceneRegister, ExpiresAt: time.Time{}, }, wantErr: true, }, { name: "手机号格式无效", smsCode: &SMSCode{ Phone: "123", Code: "123456", Scene: SMSSceneRegister, ExpiresAt: time.Now().Add(time.Hour), }, wantErr: true, }, { name: "验证码格式无效-包含字母", smsCode: &SMSCode{ Phone: "13800138000", Code: "12345a", Scene: SMSSceneRegister, ExpiresAt: time.Now().Add(time.Hour), }, wantErr: true, }, { name: "验证码长度过短", smsCode: &SMSCode{ Phone: "13800138000", Code: "123", Scene: SMSSceneRegister, ExpiresAt: time.Now().Add(time.Hour), }, wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { err := tt.smsCode.Validate() if tt.wantErr { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func TestSMSCode_VerifyCode(t *testing.T) { now := time.Now() expiresAt := now.Add(time.Hour) tests := []struct { name string smsCode *SMSCode inputCode string wantErr bool }{ { name: "验证码正确", smsCode: &SMSCode{ Code: "123456", Used: false, ExpiresAt: expiresAt, }, inputCode: "123456", wantErr: false, }, { name: "验证码错误", smsCode: &SMSCode{ Code: "123456", Used: false, ExpiresAt: expiresAt, }, inputCode: "654321", wantErr: true, }, { name: "验证码已使用", smsCode: &SMSCode{ Code: "123456", Used: true, ExpiresAt: expiresAt, }, inputCode: "123456", wantErr: true, }, { name: "验证码已过期", smsCode: &SMSCode{ Code: "123456", Used: false, ExpiresAt: now.Add(-time.Hour), }, inputCode: "123456", wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { err := tt.smsCode.VerifyCode(tt.inputCode) if tt.wantErr { assert.Error(t, err) } else { assert.NoError(t, err) // 验证码正确时应该被标记为已使用 assert.True(t, tt.smsCode.Used) assert.NotNil(t, tt.smsCode.UsedAt) } }) } } func TestSMSCode_IsExpired(t *testing.T) { now := time.Now() tests := []struct { name string smsCode *SMSCode expected bool }{ { name: "未过期", smsCode: &SMSCode{ ExpiresAt: now.Add(time.Hour), }, expected: false, }, { name: "已过期", smsCode: &SMSCode{ ExpiresAt: now.Add(-time.Hour), }, expected: true, }, { name: "刚好过期", smsCode: &SMSCode{ ExpiresAt: now, }, expected: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := tt.smsCode.IsExpired() assert.Equal(t, tt.expected, result) }) } } func TestSMSCode_IsValid(t *testing.T) { now := time.Now() tests := []struct { name string smsCode *SMSCode expected bool }{ { name: "有效验证码", smsCode: &SMSCode{ Used: false, ExpiresAt: now.Add(time.Hour), }, expected: true, }, { name: "已使用", smsCode: &SMSCode{ Used: true, ExpiresAt: now.Add(time.Hour), }, expected: false, }, { name: "已过期", smsCode: &SMSCode{ Used: false, ExpiresAt: now.Add(-time.Hour), }, expected: false, }, { name: "已使用且已过期", smsCode: &SMSCode{ Used: true, ExpiresAt: now.Add(-time.Hour), }, expected: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := tt.smsCode.IsValid() assert.Equal(t, tt.expected, result) }) } } func TestSMSCode_CanResend(t *testing.T) { now := time.Now() minInterval := 60 * time.Second tests := []struct { name string smsCode *SMSCode expected bool }{ { name: "已使用-可以重发", smsCode: &SMSCode{ Used: true, CreatedAt: now.Add(-30 * time.Second), }, expected: true, }, { name: "已过期-可以重发", smsCode: &SMSCode{ Used: false, ExpiresAt: now.Add(-time.Hour), CreatedAt: now.Add(-30 * time.Second), }, expected: true, }, { name: "未过期且未使用-间隔足够-可以重发", smsCode: &SMSCode{ Used: false, ExpiresAt: now.Add(time.Hour), CreatedAt: now.Add(-2 * time.Minute), }, expected: true, }, { name: "未过期且未使用-间隔不足-不能重发", smsCode: &SMSCode{ Used: false, ExpiresAt: now.Add(time.Hour), CreatedAt: now.Add(-30 * time.Second), }, expected: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := tt.smsCode.CanResend(minInterval) assert.Equal(t, tt.expected, result) }) } } func TestSMSCode_GetRemainingTime(t *testing.T) { now := time.Now() tests := []struct { name string smsCode *SMSCode expected time.Duration }{ { name: "未过期", smsCode: &SMSCode{ ExpiresAt: now.Add(time.Hour), }, expected: time.Hour, }, { name: "已过期", smsCode: &SMSCode{ ExpiresAt: now.Add(-time.Hour), }, expected: 0, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := tt.smsCode.GetRemainingTime() // 由于时间计算可能有微小差异,我们检查是否在合理范围内 if tt.expected > 0 { assert.True(t, result > 0) assert.True(t, result <= tt.expected) } else { assert.Equal(t, tt.expected, result) } }) } } func TestSMSCode_GetMaskedCode(t *testing.T) { tests := []struct { name string code string expected string }{ { name: "6位验证码", code: "123456", expected: "1***6", }, { name: "4位验证码", code: "1234", expected: "1***4", }, { name: "短验证码", code: "12", expected: "***", }, { name: "单字符", code: "1", expected: "***", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { smsCode := &SMSCode{Code: tt.code} result := smsCode.GetMaskedCode() assert.Equal(t, tt.expected, result) }) } } func TestSMSCode_GetMaskedPhone(t *testing.T) { tests := []struct { name string phone string expected string }{ { name: "标准手机号", phone: "13800138000", expected: "138****8000", }, { name: "短手机号", phone: "138001", expected: "138001", }, { name: "空手机号", phone: "", expected: "", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { smsCode := &SMSCode{Phone: tt.phone} result := smsCode.GetMaskedPhone() assert.Equal(t, tt.expected, result) }) } } func TestSMSCode_IsSceneValid(t *testing.T) { tests := []struct { name string scene SMSScene expected bool }{ { name: "注册场景", scene: SMSSceneRegister, expected: true, }, { name: "登录场景", scene: SMSSceneLogin, expected: true, }, { name: "无效场景", scene: "invalid", expected: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { smsCode := &SMSCode{Scene: tt.scene} result := smsCode.IsSceneValid() assert.Equal(t, tt.expected, result) }) } } func TestSMSCode_GetSceneName(t *testing.T) { tests := []struct { name string scene SMSScene expected string }{ { name: "注册场景", scene: SMSSceneRegister, expected: "用户注册", }, { name: "登录场景", scene: SMSSceneLogin, expected: "用户登录", }, { name: "无效场景", scene: "invalid", expected: "invalid", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { smsCode := &SMSCode{Scene: tt.scene} result := smsCode.GetSceneName() assert.Equal(t, tt.expected, result) }) } } func TestSMSCode_IsSuspicious(t *testing.T) { tests := []struct { name string smsCode *SMSCode expected bool }{ { name: "正常记录", smsCode: &SMSCode{ IP: "192.168.1.1", UserAgent: "Mozilla/5.0", }, expected: false, }, { name: "IP为空-可疑", smsCode: &SMSCode{ IP: "", UserAgent: "Mozilla/5.0", }, expected: true, }, { name: "UserAgent为空-可疑", smsCode: &SMSCode{ IP: "192.168.1.1", UserAgent: "", }, expected: true, }, { name: "IP和UserAgent都为空-可疑", smsCode: &SMSCode{ IP: "", UserAgent: "", }, expected: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := tt.smsCode.IsSuspicious() assert.Equal(t, tt.expected, result) }) } } func TestSMSCode_GetSecurityInfo(t *testing.T) { now := time.Now() smsCode := &SMSCode{ IP: "192.168.1.1", UserAgent: "Mozilla/5.0", Scene: SMSSceneRegister, CreatedAt: now, } securityInfo := smsCode.GetSecurityInfo() assert.Equal(t, "192.168.1.1", securityInfo["ip"]) assert.Equal(t, "Mozilla/5.0", securityInfo["user_agent"]) assert.Equal(t, false, securityInfo["suspicious"]) assert.Equal(t, "用户注册", securityInfo["scene"]) assert.Equal(t, now, securityInfo["created_at"]) } func TestIsValidScene(t *testing.T) { tests := []struct { name string scene SMSScene expected bool }{ { name: "注册场景", scene: SMSSceneRegister, expected: true, }, { name: "登录场景", scene: SMSSceneLogin, expected: true, }, { name: "无效场景", scene: "invalid", expected: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := IsValidScene(tt.scene) assert.Equal(t, tt.expected, result) }) } } func TestGetSceneName(t *testing.T) { tests := []struct { name string scene SMSScene expected string }{ { name: "注册场景", scene: SMSSceneRegister, expected: "用户注册", }, { name: "登录场景", scene: SMSSceneLogin, expected: "用户登录", }, { name: "无效场景", scene: "invalid", expected: "invalid", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := GetSceneName(tt.scene) assert.Equal(t, tt.expected, result) }) } } func TestNewSMSCode(t *testing.T) { tests := []struct { name string phone string code string scene SMSScene expireTime time.Duration clientIP string userAgent string expectError bool }{ { name: "有效参数", phone: "13800138000", code: "123456", scene: SMSSceneRegister, expireTime: time.Hour, clientIP: "192.168.1.1", userAgent: "Mozilla/5.0", expectError: false, }, { name: "无效手机号", phone: "123", code: "123456", scene: SMSSceneRegister, expireTime: time.Hour, clientIP: "192.168.1.1", userAgent: "Mozilla/5.0", expectError: true, }, { name: "无效验证码", phone: "13800138000", code: "123", scene: SMSSceneRegister, expireTime: time.Hour, clientIP: "192.168.1.1", userAgent: "Mozilla/5.0", expectError: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { smsCode, err := NewSMSCode(tt.phone, tt.code, tt.scene, tt.expireTime, tt.clientIP, tt.userAgent) if tt.expectError { assert.Error(t, err) assert.Nil(t, smsCode) } else { assert.NoError(t, err) assert.NotNil(t, smsCode) assert.Equal(t, tt.phone, smsCode.Phone) assert.Equal(t, tt.code, smsCode.Code) assert.Equal(t, tt.scene, smsCode.Scene) assert.Equal(t, tt.clientIP, smsCode.IP) assert.Equal(t, tt.userAgent, smsCode.UserAgent) assert.False(t, smsCode.Used) assert.True(t, smsCode.ExpiresAt.After(time.Now())) } }) } }