veza/veza-backend-api/internal/validators/password_validator_test.go
senke 0e7b6fede1 [T0-002] fix(rust): Corriger erreurs compilation Rust
- Conflit SQLx résolu (alignement sur version 0.7)
- build.rs configurés pour protoc dans chat/stream servers
- API Prometheus migrée vers HistogramOpts
- Traits Display/Debug corrigés (String au lieu de &dyn Display)
- API TOTP corrigée (totp-rs 5.4 avec Secret::Encoded)
- Layers tracing-subscriber corrigés (types conditionnels)
- VezaError/VezaResult exportés dans lib.rs
- TransactionProvider simplifié (retour void au lieu de Box<dyn>)
- VezaConfig contraint Serialize pour to_json()

Files: veza-common/Cargo.toml, veza-common/src/*.rs, veza-chat-server/Cargo.toml, veza-chat-server/build.rs, veza-stream-server/Cargo.toml, veza-stream-server/build.rs, VEZA_ROADMAP.json
Hours: 8 estimated, 3 actual
2026-01-04 01:44:20 +01:00

374 lines
9.4 KiB
Go

package validators
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestNewPasswordValidator(t *testing.T) {
validator := NewPasswordValidator()
assert.NotNil(t, validator)
assert.Equal(t, 12, validator.MinLength)
}
func TestPasswordValidator_Validate_ValidPassword(t *testing.T) {
validator := NewPasswordValidator()
tests := []struct {
name string
password string
}{
{
name: "valid password with all requirements",
password: "SecurePass123!",
},
{
name: "valid password with special chars",
password: "MyP@ssw0rd!Test",
},
{
name: "valid password with multiple special chars",
password: "Test#123$Pass",
},
{
name: "valid password exactly 12 chars",
password: "Test123!Pass",
},
{
name: "valid password longer than 12 chars",
password: "VerySecurePassword123!@#",
},
{
name: "valid password with parentheses",
password: "Test(123)Pass",
},
{
name: "valid password with comma",
password: "Test,123Pass",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
strength, err := validator.Validate(tt.password)
assert.NoError(t, err)
assert.True(t, strength.Valid, "Password should be valid: %s", tt.password)
assert.Equal(t, 4, strength.Score, "Password should have score 4")
assert.Empty(t, strength.Details, "Should have no validation errors")
})
}
}
func TestPasswordValidator_Validate_InvalidLength(t *testing.T) {
validator := NewPasswordValidator()
tests := []struct {
name string
password string
}{
{
name: "password too short - 11 chars",
password: "Test123!Pas",
},
{
name: "password too short - 5 chars",
password: "Test1",
},
{
name: "empty password",
password: "",
},
{
name: "password too short - 1 char",
password: "T",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
strength, err := validator.Validate(tt.password)
assert.NoError(t, err)
assert.False(t, strength.Valid, "Password should be invalid: %s", tt.password)
assert.Contains(t, strength.Details, "Password must be at least 12 characters long")
})
}
}
func TestPasswordValidator_Validate_MissingUpperCase(t *testing.T) {
validator := NewPasswordValidator()
tests := []struct {
name string
password string
}{
{
name: "password without uppercase",
password: "testpassword123!",
},
{
name: "password without uppercase - all lowercase",
password: "lowercase123!@#",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
strength, err := validator.Validate(tt.password)
assert.NoError(t, err)
assert.False(t, strength.Valid, "Password should be invalid: %s", tt.password)
assert.Contains(t, strength.Details, "Must contain uppercase letter")
})
}
}
func TestPasswordValidator_Validate_MissingLowerCase(t *testing.T) {
validator := NewPasswordValidator()
tests := []struct {
name string
password string
}{
{
name: "password without lowercase",
password: "TESTPASSWORD123!",
},
{
name: "password without lowercase - all uppercase",
password: "UPPERCASE123!@#",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
strength, err := validator.Validate(tt.password)
assert.NoError(t, err)
assert.False(t, strength.Valid, "Password should be invalid: %s", tt.password)
assert.Contains(t, strength.Details, "Must contain lowercase letter")
})
}
}
func TestPasswordValidator_Validate_MissingNumber(t *testing.T) {
validator := NewPasswordValidator()
tests := []struct {
name string
password string
}{
{
name: "password without number",
password: "TestPassword!",
},
{
name: "password without number - only letters and special",
password: "TestPass!@#$",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
strength, err := validator.Validate(tt.password)
assert.NoError(t, err)
assert.False(t, strength.Valid, "Password should be invalid: %s", tt.password)
assert.Contains(t, strength.Details, "Must contain number")
})
}
}
func TestPasswordValidator_Validate_MissingSpecialChar(t *testing.T) {
validator := NewPasswordValidator()
tests := []struct {
name string
password string
}{
{
name: "password without special char",
password: "TestPassword123",
},
{
name: "password without special char - only alphanumeric",
password: "TestPass9753", // Pas de pattern séquentiel (9753 n'est pas dans les séquences)
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
strength, err := validator.Validate(tt.password)
assert.NoError(t, err)
assert.False(t, strength.Valid, "Password should be invalid: %s", tt.password)
assert.Contains(t, strength.Details, "Must contain special character")
})
}
}
func TestPasswordValidator_Validate_MultipleMissing(t *testing.T) {
validator := NewPasswordValidator()
tests := []struct {
name string
password string
expectedErrors []string
}{
{
name: "missing uppercase and lowercase",
password: "9753108642!!", // 12 caractères, pas de pattern séquentiel (mélange de chiffres non séquentiels)
expectedErrors: []string{"Must contain uppercase letter", "Must contain lowercase letter"},
},
{
name: "missing number and special char",
password: "TestPassword",
expectedErrors: []string{"Must contain number", "Must contain special character"},
},
{
name: "missing uppercase, number and special",
password: "testpassword",
expectedErrors: []string{"Must contain uppercase letter", "Must contain number", "Must contain special character"},
},
{
name: "missing everything except length",
password: "alllowercase",
expectedErrors: []string{"Must contain uppercase letter", "Must contain number", "Must contain special character"},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
strength, err := validator.Validate(tt.password)
assert.NoError(t, err)
assert.False(t, strength.Valid, "Password should be invalid: %s", tt.password)
for _, expectedError := range tt.expectedErrors {
assert.Contains(t, strength.Details, expectedError)
}
})
}
}
func TestPasswordValidator_Validate_Score(t *testing.T) {
validator := NewPasswordValidator()
tests := []struct {
name string
password string
expectedScore int
expectedValid bool
}{
{
name: "valid password - score 4",
password: "Test123!Pass",
expectedScore: 4,
expectedValid: true,
},
{
name: "missing uppercase - score 3",
password: "test123!pass",
expectedScore: 3,
expectedValid: false,
},
{
name: "missing lowercase - score 3",
password: "TEST123!PASS",
expectedScore: 3,
expectedValid: false,
},
{
name: "missing number - score 3",
password: "TestPass!Word",
expectedScore: 3,
expectedValid: false,
},
{
name: "missing special - score 3",
password: "TestPass9753", // Pas de pattern séquentiel (9753 n'est pas dans les séquences)
expectedScore: 3,
expectedValid: false,
},
{
name: "missing 3 requirements - score 1 (only lowercase)",
password: "testpassword",
expectedScore: 1,
expectedValid: false,
},
{
name: "too short - returns early with score 0",
password: "testonly",
expectedScore: 0,
expectedValid: false,
},
{
name: "only lowercase - score 1",
password: "lowercaseonly",
expectedScore: 1,
expectedValid: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
strength, err := validator.Validate(tt.password)
assert.NoError(t, err)
assert.Equal(t, tt.expectedValid, strength.Valid)
assert.Equal(t, tt.expectedScore, strength.Score)
})
}
}
func TestPasswordValidator_Validate_SpecialCharacters(t *testing.T) {
validator := NewPasswordValidator()
specialChars := []string{"!", "@", "#", "$", "%", "^", "&", "*", "(", ")", ",", ".", "?", ":", "\"", "{", "}", "|", "<", ">"}
for _, special := range specialChars {
t.Run("special char: "+special, func(t *testing.T) {
password := "TestPass123" + special
strength, err := validator.Validate(password)
assert.NoError(t, err)
assert.True(t, strength.Valid, "Password with special char %s should be valid", special)
})
}
}
func TestPasswordValidator_Validate_EdgeCases(t *testing.T) {
validator := NewPasswordValidator()
tests := []struct {
name string
password string
want bool
}{
{
name: "password with unicode characters",
password: "Test123!Passé",
want: true,
},
{
name: "password with spaces",
password: "Test 123!Pass",
want: true, // Spaces are allowed, just not in the special char regex
},
{
name: "password with tabs",
password: "Test123!Pass\t",
want: true,
},
{
name: "password with newlines",
password: "Test123!Pass\n",
want: true,
},
{
name: "password with special chars not in regex",
password: "Test123-Pass_", // - and _ are not in the special char regex
want: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
strength, err := validator.Validate(tt.password)
assert.NoError(t, err)
assert.Equal(t, tt.want, strength.Valid)
})
}
}