Files
sendico/api/edge/bff/internal/server/accountapiimp/password_test.go
2026-02-28 00:39:20 +01:00

279 lines
7.3 KiB
Go

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")
})
}
})
}