hi-server/internal/logic/common/checkverificationcodelogic_test.go
shanshanzhong a01570b59d
Some checks failed
Build docker and publish / build (20.15.1) (push) Failing after 8m21s
fix gitea workflow path and runner label
2026-03-04 06:33:14 -08:00

260 lines
7.2 KiB
Go

package common
import (
"context"
"encoding/json"
"fmt"
"testing"
"time"
"github.com/alicebob/miniredis/v2"
"github.com/perfect-panel/server/internal/config"
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/internal/types"
"github.com/perfect-panel/server/pkg/apiversion"
"github.com/perfect-panel/server/pkg/authmethod"
"github.com/perfect-panel/server/pkg/constant"
"github.com/redis/go-redis/v9"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestCheckVerificationCodeCanonicalConsume(t *testing.T) {
miniRedis := miniredis.RunT(t)
redisClient := redis.NewClient(&redis.Options{Addr: miniRedis.Addr()})
t.Cleanup(func() {
redisClient.Close()
miniRedis.Close()
})
svcCtx := &svc.ServiceContext{
Redis: redisClient,
Config: config.Config{
VerifyCode: config.VerifyCode{
VerifyCodeExpireTime: 900,
},
},
}
email := "user@example.com"
code := "123456"
scene := constant.Register.String()
cacheKey := fmt.Sprintf("%s:%s:%s", config.AuthCodeCacheKey, scene, email)
setEmailCodePayload(t, redisClient, cacheKey, code, time.Now().Unix())
logic := NewCheckVerificationCodeLogic(context.Background(), svcCtx)
req := &types.CheckVerificationCodeRequest{
Method: authmethod.Email,
Account: email,
Code: code,
Type: uint8(constant.Register),
}
resp, err := logic.CheckVerificationCode(req)
require.NoError(t, err)
require.NotNil(t, resp)
assert.True(t, resp.Status)
assert.True(t, resp.Exist)
exists, err := redisClient.Exists(context.Background(), cacheKey).Result()
require.NoError(t, err)
assert.Equal(t, int64(0), exists)
resp, err = logic.CheckVerificationCode(req)
require.NoError(t, err)
require.NotNil(t, resp)
assert.False(t, resp.Status)
assert.False(t, resp.Exist)
}
func TestCheckVerificationCodeLegacyNoConsumeAndType3Mapping(t *testing.T) {
miniRedis := miniredis.RunT(t)
redisClient := redis.NewClient(&redis.Options{Addr: miniRedis.Addr()})
t.Cleanup(func() {
redisClient.Close()
miniRedis.Close()
})
svcCtx := &svc.ServiceContext{
Redis: redisClient,
Config: config.Config{
VerifyCode: config.VerifyCode{
VerifyCodeExpireTime: 900,
},
},
}
email := "legacy@example.com"
code := "654321"
scene := constant.Security.String()
cacheKey := fmt.Sprintf("%s:%s:%s", config.AuthCodeCacheKey, scene, email)
setEmailCodePayload(t, redisClient, cacheKey, code, time.Now().Unix())
legacyReq := &types.LegacyCheckVerificationCodeRequest{
Email: email,
Code: code,
Type: 3,
}
normalizedReq, type3Mapped, err := NormalizeLegacyCheckVerificationCodeRequest(legacyReq)
require.NoError(t, err)
assert.True(t, type3Mapped)
assert.Equal(t, uint8(constant.Security), normalizedReq.Type)
assert.Equal(t, authmethod.Email, normalizedReq.Method)
assert.Equal(t, email, normalizedReq.Account)
logic := NewCheckVerificationCodeLogic(context.Background(), svcCtx)
legacyBehavior := VerifyCodeCheckBehavior{
Source: "legacy",
Consume: false,
LegacyType3Mapped: true,
AllowSceneFallback: true,
}
resp, err := logic.CheckVerificationCodeWithBehavior(normalizedReq, legacyBehavior)
require.NoError(t, err)
require.NotNil(t, resp)
assert.True(t, resp.Status)
assert.True(t, resp.Exist)
exists, err := redisClient.Exists(context.Background(), cacheKey).Result()
require.NoError(t, err)
assert.Equal(t, int64(1), exists)
resp, err = logic.CheckVerificationCodeWithBehavior(normalizedReq, legacyBehavior)
require.NoError(t, err)
assert.True(t, resp.Status)
resp, err = logic.CheckVerificationCode(normalizedReq)
require.NoError(t, err)
assert.True(t, resp.Status)
exists, err = redisClient.Exists(context.Background(), cacheKey).Result()
require.NoError(t, err)
assert.Equal(t, int64(0), exists)
}
func TestCheckVerificationCodeLegacySceneFallback(t *testing.T) {
miniRedis := miniredis.RunT(t)
redisClient := redis.NewClient(&redis.Options{Addr: miniRedis.Addr()})
t.Cleanup(func() {
redisClient.Close()
miniRedis.Close()
})
svcCtx := &svc.ServiceContext{
Redis: redisClient,
Config: config.Config{
VerifyCode: config.VerifyCode{
VerifyCodeExpireTime: 900,
},
},
}
email := "fallback@example.com"
code := "778899"
cacheKey := fmt.Sprintf("%s:%s:%s", config.AuthCodeCacheKey, constant.Register.String(), email)
setEmailCodePayload(t, redisClient, cacheKey, code, time.Now().Unix())
logic := NewCheckVerificationCodeLogic(context.Background(), svcCtx)
req := &types.CheckVerificationCodeRequest{
Method: authmethod.Email,
Account: email,
Code: code,
Type: uint8(constant.Security),
}
resp, err := logic.CheckVerificationCodeWithBehavior(req, VerifyCodeCheckBehavior{
Source: "legacy",
Consume: false,
AllowSceneFallback: true,
})
require.NoError(t, err)
require.NotNil(t, resp)
assert.True(t, resp.Status)
resp, err = logic.CheckVerificationCodeWithBehavior(req, VerifyCodeCheckBehavior{
Source: "legacy",
Consume: false,
AllowSceneFallback: false,
})
require.NoError(t, err)
require.NotNil(t, resp)
assert.False(t, resp.Status)
}
func setEmailCodePayload(t *testing.T, redisClient *redis.Client, cacheKey string, code string, lastAt int64) {
t.Helper()
payload := CacheKeyPayload{
Code: code,
LastAt: lastAt,
}
value, err := json.Marshal(payload)
require.NoError(t, err)
err = redisClient.Set(context.Background(), cacheKey, value, time.Minute*15).Err()
require.NoError(t, err)
}
func TestCheckVerificationCodeWithApiHeaderGate(t *testing.T) {
tests := []struct {
name string
header string
expectConsume bool
}{
{name: "missing header", header: "", expectConsume: false},
{name: "invalid header", header: "invalid", expectConsume: false},
{name: "equal threshold", header: "1.0.0", expectConsume: false},
{name: "greater threshold", header: "1.0.1", expectConsume: true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
miniRedis := miniredis.RunT(t)
redisClient := redis.NewClient(&redis.Options{Addr: miniRedis.Addr()})
t.Cleanup(func() {
redisClient.Close()
miniRedis.Close()
})
svcCtx := &svc.ServiceContext{
Redis: redisClient,
Config: config.Config{
VerifyCode: config.VerifyCode{
VerifyCodeExpireTime: 900,
},
},
}
email := "gate@example.com"
code := "101010"
cacheKey := fmt.Sprintf("%s:%s:%s", config.AuthCodeCacheKey, constant.Register.String(), email)
setEmailCodePayload(t, redisClient, cacheKey, code, time.Now().Unix())
logic := NewCheckVerificationCodeLogic(context.Background(), svcCtx)
req := &types.CheckVerificationCodeRequest{
Method: authmethod.Email,
Account: email,
Code: code,
Type: uint8(constant.Register),
}
resp, err := logic.CheckVerificationCodeWithBehavior(req, VerifyCodeCheckBehavior{
Source: "canonical",
Consume: apiversion.UseLatest(tt.header, apiversion.DefaultThreshold),
})
require.NoError(t, err)
require.NotNil(t, resp)
assert.True(t, resp.Status)
exists, err := redisClient.Exists(context.Background(), cacheKey).Result()
require.NoError(t, err)
if tt.expectConsume {
assert.Equal(t, int64(0), exists)
} else {
assert.Equal(t, int64(1), exists)
}
})
}
}