2026-05-20 07:01:08 +00:00
|
|
|
package usecase_test
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
|
|
memberconfig "gateway/internal/model/member/config"
|
|
|
|
|
"gateway/internal/model/member/domain/enum"
|
|
|
|
|
domusecase "gateway/internal/model/member/domain/usecase"
|
|
|
|
|
"gateway/internal/model/member/repository"
|
|
|
|
|
"gateway/internal/model/member/usecase"
|
|
|
|
|
|
|
|
|
|
redislib "gateway/internal/library/redis"
|
|
|
|
|
|
|
|
|
|
"github.com/alicebob/miniredis/v2"
|
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
|
"github.com/zeromicro/go-zero/core/stores/redis"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func TestOTPUseCase_GenerateAndVerify(t *testing.T) {
|
|
|
|
|
mr := miniredis.RunT(t)
|
2026-05-20 13:03:59 +00:00
|
|
|
rds, err := redislib.NewClient(redis.RedisConf{Host: mr.Addr(), Type: testRedisTypeNode})
|
2026-05-20 07:01:08 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
|
|
uc := usecase.MustOTPUseCase(usecase.OTPUseCaseParam{
|
|
|
|
|
Store: repository.NewRedisOTPChallengeStore(rds),
|
|
|
|
|
Config: memberconfig.Config{}.Defaults(),
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
dto, code, err := uc.Generate(context.Background(), &domusecase.GenerateOTPRequest{
|
|
|
|
|
TenantID: "t1",
|
|
|
|
|
UID: "u1",
|
|
|
|
|
Purpose: enum.OTPPurposeBusinessEmail,
|
2026-05-20 13:03:59 +00:00
|
|
|
Target: testUserEmail,
|
2026-05-20 07:01:08 +00:00
|
|
|
})
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
require.NotEmpty(t, code)
|
|
|
|
|
|
|
|
|
|
target, err := uc.Verify(context.Background(), &domusecase.VerifyOTPRequest{
|
|
|
|
|
TenantID: "t1",
|
|
|
|
|
UID: "u1",
|
|
|
|
|
ChallengeID: dto.ChallengeID,
|
|
|
|
|
Code: code,
|
|
|
|
|
Purpose: enum.OTPPurposeBusinessEmail,
|
|
|
|
|
})
|
|
|
|
|
require.NoError(t, err)
|
2026-05-20 13:03:59 +00:00
|
|
|
require.Equal(t, testUserEmail, target)
|
2026-05-20 07:01:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestOTPUseCase_VerifyUIDMismatch(t *testing.T) {
|
|
|
|
|
mr := miniredis.RunT(t)
|
2026-05-20 13:03:59 +00:00
|
|
|
rds, err := redislib.NewClient(redis.RedisConf{Host: mr.Addr(), Type: testRedisTypeNode})
|
2026-05-20 07:01:08 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
|
|
uc := usecase.MustOTPUseCase(usecase.OTPUseCaseParam{
|
|
|
|
|
Store: repository.NewRedisOTPChallengeStore(rds),
|
|
|
|
|
Config: memberconfig.Config{}.Defaults(),
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
dto, code, err := uc.Generate(context.Background(), &domusecase.GenerateOTPRequest{
|
|
|
|
|
TenantID: "t1",
|
|
|
|
|
UID: "victim",
|
|
|
|
|
Purpose: enum.OTPPurposeBusinessEmail,
|
2026-05-20 13:03:59 +00:00
|
|
|
Target: testUserEmail,
|
2026-05-20 07:01:08 +00:00
|
|
|
})
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
|
|
_, err = uc.Verify(context.Background(), &domusecase.VerifyOTPRequest{
|
|
|
|
|
TenantID: "t1",
|
|
|
|
|
UID: "attacker",
|
|
|
|
|
ChallengeID: dto.ChallengeID,
|
|
|
|
|
Code: code,
|
|
|
|
|
Purpose: enum.OTPPurposeBusinessEmail,
|
|
|
|
|
})
|
|
|
|
|
require.Error(t, err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestOTPUseCase_MaxAttemptsLocks(t *testing.T) {
|
|
|
|
|
mr := miniredis.RunT(t)
|
2026-05-20 13:03:59 +00:00
|
|
|
rds, err := redislib.NewClient(redis.RedisConf{Host: mr.Addr(), Type: testRedisTypeNode})
|
2026-05-20 07:01:08 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
|
|
cfg := memberconfig.Config{}.Defaults()
|
|
|
|
|
cfg.OTP.MaxAttempts = 2
|
|
|
|
|
|
|
|
|
|
uc := usecase.MustOTPUseCase(usecase.OTPUseCaseParam{
|
|
|
|
|
Store: repository.NewRedisOTPChallengeStore(rds),
|
|
|
|
|
Config: cfg,
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
dto, _, err := uc.Generate(context.Background(), &domusecase.GenerateOTPRequest{
|
|
|
|
|
TenantID: "t1",
|
|
|
|
|
UID: "u1",
|
|
|
|
|
Purpose: enum.OTPPurposeBusinessEmail,
|
2026-05-20 13:03:59 +00:00
|
|
|
Target: testUserEmail,
|
2026-05-20 07:01:08 +00:00
|
|
|
})
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
|
|
for range cfg.OTP.MaxAttempts {
|
|
|
|
|
_, err = uc.Verify(context.Background(), &domusecase.VerifyOTPRequest{
|
|
|
|
|
TenantID: "t1",
|
|
|
|
|
UID: "u1",
|
|
|
|
|
ChallengeID: dto.ChallengeID,
|
|
|
|
|
Code: "000000",
|
|
|
|
|
Purpose: enum.OTPPurposeBusinessEmail,
|
|
|
|
|
})
|
|
|
|
|
require.Error(t, err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_, err = uc.Verify(context.Background(), &domusecase.VerifyOTPRequest{
|
|
|
|
|
TenantID: "t1",
|
|
|
|
|
UID: "u1",
|
|
|
|
|
ChallengeID: dto.ChallengeID,
|
|
|
|
|
Code: "000000",
|
|
|
|
|
Purpose: enum.OTPPurposeBusinessEmail,
|
|
|
|
|
})
|
|
|
|
|
require.Error(t, err)
|
|
|
|
|
}
|