258 lines
6.0 KiB
Go
258 lines
6.0 KiB
Go
package services
|
|
|
|
import (
|
|
"context"
|
|
"platform/pkg/rds"
|
|
"strconv"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/alicebob/miniredis/v2"
|
|
"github.com/redis/go-redis/v9"
|
|
)
|
|
|
|
// 设置测试的 Redis 环境
|
|
func setupRedisTest(t *testing.T) *miniredis.Miniredis {
|
|
mr, err := miniredis.Run()
|
|
if err != nil {
|
|
t.Fatalf("设置 miniredis 失败: %v", err)
|
|
}
|
|
|
|
// 替换 redis 客户端为测试客户端
|
|
rds.Client = redis.NewClient(&redis.Options{
|
|
Addr: mr.Addr(),
|
|
})
|
|
|
|
return mr
|
|
}
|
|
|
|
func Test_verifierService_SendSms(t *testing.T) {
|
|
type args struct {
|
|
ctx context.Context
|
|
phone string
|
|
purpose VerifierSmsPurpose
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
args args
|
|
setup func(mr *miniredis.Miniredis)
|
|
wantErr bool
|
|
wantErrType error
|
|
}{
|
|
{
|
|
name: "正常发送成功(无旧验证码)",
|
|
args: args{
|
|
ctx: context.Background(),
|
|
phone: "13812345678",
|
|
purpose: VerifierSmsPurposeLogin,
|
|
},
|
|
setup: func(mr *miniredis.Miniredis) {},
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "正常发送成功(有旧验证码)",
|
|
args: args{
|
|
ctx: context.Background(),
|
|
phone: "13812345679",
|
|
purpose: VerifierSmsPurposeLogin,
|
|
},
|
|
setup: func(mr *miniredis.Miniredis) {
|
|
key := smsKey("13812345679", VerifierSmsPurposeLogin)
|
|
mr.Set(key, "123456")
|
|
mr.SetTTL(key, 10*time.Minute)
|
|
},
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "发送频率过快",
|
|
args: args{
|
|
ctx: context.Background(),
|
|
phone: "13812345680",
|
|
purpose: VerifierSmsPurposeLogin,
|
|
},
|
|
setup: func(mr *miniredis.Miniredis) {
|
|
key := smsKey("13812345680", VerifierSmsPurposeLogin) + ":lock"
|
|
mr.Set(key, "")
|
|
mr.SetTTL(key, 1*time.Minute)
|
|
},
|
|
wantErr: true,
|
|
wantErrType: VerifierServiceSendLimitErr(0),
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
// 设置 Redis 测试环境
|
|
mr := setupRedisTest(t)
|
|
defer mr.Close()
|
|
|
|
// 执行测试前的设置
|
|
if tt.setup != nil {
|
|
tt.setup(mr)
|
|
}
|
|
|
|
s := &verifierService{}
|
|
err := s.SendSms(tt.args.ctx, tt.args.phone, tt.args.purpose)
|
|
|
|
// 验证错误
|
|
if (err != nil) != tt.wantErr {
|
|
t.Errorf("SendSms() error = %v, wantErr %v", err, tt.wantErr)
|
|
return
|
|
}
|
|
|
|
// 验证错误类型
|
|
if tt.wantErr && tt.wantErrType != nil {
|
|
if _, isSendLimitErr := err.(VerifierServiceSendLimitErr); isSendLimitErr {
|
|
if _, wantSendLimitErr := tt.wantErrType.(VerifierServiceSendLimitErr); !wantSendLimitErr {
|
|
t.Errorf("SendSms() error type = %T, wantErrType %T", err, tt.wantErrType)
|
|
}
|
|
}
|
|
}
|
|
|
|
// 验证 Redis 中的记录
|
|
if !tt.wantErr {
|
|
key := smsKey(tt.args.phone, tt.args.purpose)
|
|
keyLock := key + ":lock"
|
|
|
|
// 验证码应存在
|
|
val, err := mr.Get(key)
|
|
if err != nil {
|
|
t.Errorf("验证码应存在但不存在: %v", err)
|
|
}
|
|
|
|
// 限速锁应存在
|
|
_, err = mr.Get(keyLock)
|
|
if err != nil {
|
|
t.Errorf("限速锁应存在但不存在: %v", err)
|
|
}
|
|
|
|
// 验证码应为6位数字
|
|
code, err := strconv.Atoi(val)
|
|
if err != nil || code < 100000 || code > 999999 {
|
|
t.Errorf("验证码应为6位数字: %v", val)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_verifierService_VerifySms(t *testing.T) {
|
|
type args struct {
|
|
ctx context.Context
|
|
phone string
|
|
code string
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
args args
|
|
setup func(mr *miniredis.Miniredis)
|
|
wantErr bool
|
|
wantErrType error
|
|
}{
|
|
{
|
|
name: "验证码正确",
|
|
args: args{
|
|
ctx: context.Background(),
|
|
phone: "13812345678",
|
|
code: "123456",
|
|
},
|
|
setup: func(mr *miniredis.Miniredis) {
|
|
key := smsKey("13812345678", VerifierSmsPurposeLogin)
|
|
keyLock := key + ":lock"
|
|
mr.Set(key, "123456")
|
|
mr.SetTTL(key, 10*time.Minute)
|
|
mr.Set(keyLock, "")
|
|
mr.SetTTL(keyLock, 1*time.Minute)
|
|
},
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "验证码错误",
|
|
args: args{
|
|
ctx: context.Background(),
|
|
phone: "13812345679",
|
|
code: "654321",
|
|
},
|
|
setup: func(mr *miniredis.Miniredis) {
|
|
key := smsKey("13812345679", VerifierSmsPurposeLogin)
|
|
keyLock := key + ":lock"
|
|
mr.Set(key, "123456")
|
|
mr.SetTTL(key, 10*time.Minute)
|
|
mr.Set(keyLock, "")
|
|
mr.SetTTL(keyLock, 1*time.Minute)
|
|
},
|
|
wantErr: true,
|
|
wantErrType: ErrVerifierServiceInvalid,
|
|
},
|
|
{
|
|
name: "验证码过期",
|
|
args: args{
|
|
ctx: context.Background(),
|
|
phone: "13812345680",
|
|
code: "123456",
|
|
},
|
|
setup: func(mr *miniredis.Miniredis) {
|
|
// 不设置验证码,模拟过期情况
|
|
},
|
|
wantErr: true,
|
|
wantErrType: ErrVerifierServiceInvalid,
|
|
},
|
|
{
|
|
name: "手机号错误",
|
|
args: args{
|
|
ctx: context.Background(),
|
|
phone: "13812345681",
|
|
code: "123456",
|
|
},
|
|
setup: func(mr *miniredis.Miniredis) {
|
|
// 设置一个不同手机号的验证码
|
|
key := smsKey("13800000000", VerifierSmsPurposeLogin)
|
|
mr.Set(key, "123456")
|
|
mr.SetTTL(key, 10*time.Minute)
|
|
},
|
|
wantErr: true,
|
|
wantErrType: ErrVerifierServiceInvalid,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
// 设置 Redis 测试环境
|
|
mr := setupRedisTest(t)
|
|
defer mr.Close()
|
|
|
|
// 执行测试前的设置
|
|
if tt.setup != nil {
|
|
tt.setup(mr)
|
|
}
|
|
|
|
s := &verifierService{}
|
|
err := s.VerifySms(tt.args.ctx, tt.args.phone, tt.args.code)
|
|
if (err != nil) != tt.wantErr {
|
|
t.Errorf("VerifySms() error = %v, wantErr %v", err, tt.wantErr)
|
|
return
|
|
}
|
|
|
|
// 检查错误类型
|
|
if tt.wantErr && tt.wantErrType != nil && err != tt.wantErrType {
|
|
t.Errorf("VerifySms() error = %v, wantErrType %v", err, tt.wantErrType)
|
|
return
|
|
}
|
|
|
|
// 验证成功后 Redis 中应该没有该记录
|
|
if err == nil {
|
|
key := smsKey(tt.args.phone, VerifierSmsPurposeLogin)
|
|
keyLock := key + ":lock"
|
|
|
|
_, redisErr := mr.Get(key)
|
|
if redisErr == nil {
|
|
t.Errorf("验证码验证成功后应删除,但仍存在")
|
|
}
|
|
|
|
_, redisErr = mr.Get(keyLock)
|
|
if redisErr == nil {
|
|
t.Errorf("限速锁验证成功后应删除,但仍存在")
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|