move api/server to api/edge/bff
This commit is contained in:
278
api/edge/bff/internal/server/accountapiimp/password_test.go
Normal file
278
api/edge/bff/internal/server/accountapiimp/password_test.go
Normal file
@@ -0,0 +1,278 @@
|
||||
package accountapiimp
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"go.mongodb.org/mongo-driver/v2/bson"
|
||||
)
|
||||
|
||||
// TestPasswordResetFlowLogic tests the logical flow without database dependencies
|
||||
func TestPasswordResetFlowLogic(t *testing.T) {
|
||||
t.Run("CompleteFlow", func(t *testing.T) {
|
||||
// Step 1: User requests password reset
|
||||
userEmail := "test@example.com"
|
||||
assert.NotEmpty(t, userEmail, "Email should not be empty")
|
||||
assert.Contains(t, userEmail, "@", "Email should contain @ symbol")
|
||||
|
||||
// Step 2: System generates reset token
|
||||
originalToken := ""
|
||||
resetToken := "generated-reset-token-123"
|
||||
assert.NotEmpty(t, resetToken, "Reset token should be generated")
|
||||
assert.NotEqual(t, originalToken, resetToken, "Reset token should be different from empty")
|
||||
|
||||
// Step 3: User clicks reset link with token
|
||||
userID := bson.NewObjectID()
|
||||
assert.NotEqual(t, bson.NilObjectID, userID, "User ID should be valid")
|
||||
|
||||
// Step 4: System validates token and updates password
|
||||
storedToken := resetToken
|
||||
providedToken := resetToken
|
||||
tokenValid := storedToken == providedToken
|
||||
assert.True(t, tokenValid, "Token should be valid")
|
||||
|
||||
// Step 5: Password gets updated and token cleared
|
||||
oldPassword := "old-password"
|
||||
newPassword := "new-password-123!"
|
||||
clearedToken := ""
|
||||
|
||||
assert.NotEqual(t, oldPassword, newPassword, "Password should be changed")
|
||||
assert.Empty(t, clearedToken, "Token should be cleared after use")
|
||||
assert.NotEqual(t, storedToken, clearedToken, "Token should be different after clearing")
|
||||
})
|
||||
|
||||
t.Run("TokenSecurity", func(t *testing.T) {
|
||||
// Test that tokens are single-use
|
||||
originalToken := "valid-token-123"
|
||||
usedToken := "" // After use, token should be cleared
|
||||
|
||||
assert.NotEmpty(t, originalToken, "Original token should exist")
|
||||
assert.Empty(t, usedToken, "Used token should be cleared")
|
||||
assert.NotEqual(t, originalToken, usedToken, "Token should be cleared after use")
|
||||
|
||||
// Test that different tokens are not equal
|
||||
token1 := "token-123"
|
||||
token2 := "token-456"
|
||||
assert.NotEqual(t, token1, token2, "Different tokens should not be equal")
|
||||
})
|
||||
}
|
||||
|
||||
// TestPasswordValidationLogic tests password complexity requirements
|
||||
func TestPasswordValidationLogic(t *testing.T) {
|
||||
t.Run("ValidPasswords", func(t *testing.T) {
|
||||
validPasswords := []string{
|
||||
"Password123!",
|
||||
"MySecurePass1@",
|
||||
"ComplexP@ssw0rd",
|
||||
}
|
||||
|
||||
for _, password := range validPasswords {
|
||||
t.Run(password, func(t *testing.T) {
|
||||
// Test minimum length
|
||||
assert.True(t, len(password) >= 8, "Password should be at least 8 characters")
|
||||
|
||||
// Test for at least one digit
|
||||
hasDigit := false
|
||||
for _, char := range password {
|
||||
if char >= '0' && char <= '9' {
|
||||
hasDigit = true
|
||||
break
|
||||
}
|
||||
}
|
||||
assert.True(t, hasDigit, "Password should contain at least one digit")
|
||||
|
||||
// Test for at least one uppercase letter
|
||||
hasUpper := false
|
||||
for _, char := range password {
|
||||
if char >= 'A' && char <= 'Z' {
|
||||
hasUpper = true
|
||||
break
|
||||
}
|
||||
}
|
||||
assert.True(t, hasUpper, "Password should contain at least one uppercase letter")
|
||||
|
||||
// Test for at least one lowercase letter
|
||||
hasLower := false
|
||||
for _, char := range password {
|
||||
if char >= 'a' && char <= 'z' {
|
||||
hasLower = true
|
||||
break
|
||||
}
|
||||
}
|
||||
assert.True(t, hasLower, "Password should contain at least one lowercase letter")
|
||||
|
||||
// Test for at least one special character
|
||||
hasSpecial := false
|
||||
specialChars := "!@#$%^&*()_+-=[]{}|;:,.<>?"
|
||||
for _, char := range password {
|
||||
for _, special := range specialChars {
|
||||
if char == special {
|
||||
hasSpecial = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if hasSpecial {
|
||||
break
|
||||
}
|
||||
}
|
||||
assert.True(t, hasSpecial, "Password should contain at least one special character")
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("InvalidPasswords", func(t *testing.T) {
|
||||
invalidPasswords := []string{
|
||||
"", // Empty
|
||||
"short", // Too short
|
||||
"nouppercase1!", // No uppercase
|
||||
"NOLOWERCASE1!", // No lowercase
|
||||
"NoNumbers!", // No numbers
|
||||
"NoSpecial1", // No special characters
|
||||
}
|
||||
|
||||
for _, password := range invalidPasswords {
|
||||
t.Run(password, func(t *testing.T) {
|
||||
// Test that invalid passwords fail at least one requirement
|
||||
isValid := true
|
||||
|
||||
// Check length
|
||||
if len(password) < 8 {
|
||||
isValid = false
|
||||
}
|
||||
|
||||
// Check for digit
|
||||
hasDigit := false
|
||||
for _, char := range password {
|
||||
if char >= '0' && char <= '9' {
|
||||
hasDigit = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !hasDigit {
|
||||
isValid = false
|
||||
}
|
||||
|
||||
// Check for uppercase
|
||||
hasUpper := false
|
||||
for _, char := range password {
|
||||
if char >= 'A' && char <= 'Z' {
|
||||
hasUpper = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !hasUpper {
|
||||
isValid = false
|
||||
}
|
||||
|
||||
// Check for lowercase
|
||||
hasLower := false
|
||||
for _, char := range password {
|
||||
if char >= 'a' && char <= 'z' {
|
||||
hasLower = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !hasLower {
|
||||
isValid = false
|
||||
}
|
||||
|
||||
// Check for special character
|
||||
hasSpecial := false
|
||||
specialChars := "!@#$%^&*()_+-=[]{}|;:,.<>?"
|
||||
for _, char := range password {
|
||||
for _, special := range specialChars {
|
||||
if char == special {
|
||||
hasSpecial = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if hasSpecial {
|
||||
break
|
||||
}
|
||||
}
|
||||
if !hasSpecial {
|
||||
isValid = false
|
||||
}
|
||||
|
||||
assert.False(t, isValid, "Invalid password should fail validation")
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// TestEmailValidationLogic tests email format validation
|
||||
func TestEmailValidationLogic(t *testing.T) {
|
||||
t.Run("ValidEmails", func(t *testing.T) {
|
||||
validEmails := []string{
|
||||
"test@example.com",
|
||||
"user.name@domain.org",
|
||||
"user+tag@example.co.uk",
|
||||
"test123@domain.com",
|
||||
}
|
||||
|
||||
for _, email := range validEmails {
|
||||
t.Run(email, func(t *testing.T) {
|
||||
// Basic email validation logic
|
||||
hasAt := false
|
||||
hasDot := false
|
||||
atIndex := -1
|
||||
dotIndex := -1
|
||||
|
||||
for i, char := range email {
|
||||
if char == '@' {
|
||||
hasAt = true
|
||||
atIndex = i
|
||||
}
|
||||
if char == '.' {
|
||||
hasDot = true
|
||||
dotIndex = i
|
||||
}
|
||||
}
|
||||
|
||||
assert.True(t, hasAt, "Valid email should contain @")
|
||||
assert.True(t, hasDot, "Valid email should contain .")
|
||||
assert.True(t, atIndex > 0, "Valid email should have @ not at start")
|
||||
assert.True(t, dotIndex > atIndex, "Valid email should have . after @")
|
||||
assert.True(t, len(email) > atIndex+1, "Valid email should have domain after @")
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("InvalidEmails", func(t *testing.T) {
|
||||
invalidEmails := []string{
|
||||
"", // Empty
|
||||
"noat.com", // No @
|
||||
"test@nodot", // No .
|
||||
"@nodomain.com", // No local part
|
||||
"test@.com", // No domain
|
||||
"test.com@", // No domain after @
|
||||
}
|
||||
|
||||
for _, email := range invalidEmails {
|
||||
t.Run(email, func(t *testing.T) {
|
||||
// Basic email validation logic
|
||||
hasAt := false
|
||||
hasDot := false
|
||||
atIndex := -1
|
||||
dotIndex := -1
|
||||
|
||||
for i, char := range email {
|
||||
if char == '@' {
|
||||
hasAt = true
|
||||
atIndex = i
|
||||
}
|
||||
if char == '.' {
|
||||
hasDot = true
|
||||
dotIndex = i
|
||||
}
|
||||
}
|
||||
|
||||
// Invalid emails should fail at least one requirement
|
||||
domainAfterDot := len(email) > dotIndex+1
|
||||
domainAfterAt := len(email) > atIndex+1
|
||||
isValid := hasAt && hasDot && atIndex > 0 && dotIndex > atIndex && domainAfterAt && domainAfterDot && (dotIndex-atIndex) > 1
|
||||
assert.False(t, isValid, "Invalid email should fail validation")
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user