feat: backend core - auth, user, role, permission, device, webhook, monitoring, cache, repository, service, middleware, API handlers
This commit is contained in:
101
internal/auth/totp_test.go
Normal file
101
internal/auth/totp_test.go
Normal file
@@ -0,0 +1,101 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestTOTPManager_GenerateAndValidate(t *testing.T) {
|
||||
m := NewTOTPManager()
|
||||
|
||||
// 生成密钥
|
||||
setup, err := m.GenerateSecret("testuser@example.com")
|
||||
if err != nil {
|
||||
t.Fatalf("GenerateSecret 失败: %v", err)
|
||||
}
|
||||
|
||||
if setup.Secret == "" {
|
||||
t.Fatal("生成的 Secret 不应为空")
|
||||
}
|
||||
if setup.QRCodeBase64 == "" {
|
||||
t.Fatal("QRCode Base64 不应为空")
|
||||
}
|
||||
if len(setup.RecoveryCodes) != RecoveryCodeCount {
|
||||
t.Fatalf("恢复码数量期望 %d,实际 %d", RecoveryCodeCount, len(setup.RecoveryCodes))
|
||||
}
|
||||
t.Logf("生成 Secret: %s", setup.Secret)
|
||||
t.Logf("恢复码示例: %s", setup.RecoveryCodes[0])
|
||||
|
||||
// 用生成的密钥生成当前 TOTP 码,再验证
|
||||
code, err := m.GenerateCurrentCode(setup.Secret)
|
||||
if err != nil {
|
||||
t.Fatalf("GenerateCurrentCode 失败: %v", err)
|
||||
}
|
||||
if !m.ValidateCode(setup.Secret, code) {
|
||||
t.Fatalf("有效 TOTP 码应该通过验证,code=%s", code)
|
||||
}
|
||||
t.Logf("TOTP 验证通过,code=%s", code)
|
||||
}
|
||||
|
||||
func TestTOTPManager_InvalidCode(t *testing.T) {
|
||||
m := NewTOTPManager()
|
||||
setup, err := m.GenerateSecret("user")
|
||||
if err != nil {
|
||||
t.Fatalf("GenerateSecret 失败: %v", err)
|
||||
}
|
||||
|
||||
// 错误的验证码
|
||||
if m.ValidateCode(setup.Secret, "000000") {
|
||||
// 偶尔可能恰好正确,跳过而不是 fatal
|
||||
t.Skip("000000 碰巧是有效码,跳过测试")
|
||||
}
|
||||
t.Log("无效验证码正确拒绝")
|
||||
}
|
||||
|
||||
func TestTOTPManager_RecoveryCodeFormat(t *testing.T) {
|
||||
m := NewTOTPManager()
|
||||
setup, err := m.GenerateSecret("user2")
|
||||
if err != nil {
|
||||
t.Fatalf("GenerateSecret 失败: %v", err)
|
||||
}
|
||||
|
||||
for i, code := range setup.RecoveryCodes {
|
||||
parts := strings.Split(code, "-")
|
||||
if len(parts) != 2 {
|
||||
t.Errorf("恢复码 [%d] 格式错误(期望 XXXXX-XXXXX): %s", i, code)
|
||||
}
|
||||
if len(parts[0]) != 5 || len(parts[1]) != 5 {
|
||||
t.Errorf("恢复码 [%d] 各部分长度应为 5: %s", i, code)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateRecoveryCode(t *testing.T) {
|
||||
codes := []string{"ABCDE-FGHIJ", "KLMNO-PQRST", "UVWXY-ZABCD"}
|
||||
|
||||
// 正确匹配
|
||||
idx, ok := ValidateRecoveryCode("ABCDE-FGHIJ", codes)
|
||||
if !ok || idx != 0 {
|
||||
t.Fatalf("有效恢复码应该匹配,idx=%d ok=%v", idx, ok)
|
||||
}
|
||||
|
||||
// 大小写不敏感
|
||||
idx2, ok2 := ValidateRecoveryCode("klmno-pqrst", codes)
|
||||
if !ok2 || idx2 != 1 {
|
||||
t.Fatalf("大小写不敏感匹配失败,idx=%d ok=%v", idx2, ok2)
|
||||
}
|
||||
|
||||
// 去除空格
|
||||
idx3, ok3 := ValidateRecoveryCode(" UVWXY-ZABCD ", codes)
|
||||
if !ok3 || idx3 != 2 {
|
||||
t.Fatalf("去除空格匹配失败,idx=%d ok=%v", idx3, ok3)
|
||||
}
|
||||
|
||||
// 不匹配
|
||||
_, ok4 := ValidateRecoveryCode("XXXXX-YYYYY", codes)
|
||||
if ok4 {
|
||||
t.Fatal("无效恢复码不应该匹配")
|
||||
}
|
||||
|
||||
t.Log("恢复码验证全部通过")
|
||||
}
|
||||
Reference in New Issue
Block a user