- 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
374 lines
9.4 KiB
Go
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)
|
|
})
|
|
}
|
|
}
|