Files
tyapi-server/internal/domains/user/entities/sms_code_test.go
2025-07-11 21:05:58 +08:00

682 lines
13 KiB
Go

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