package validators import ( "strings" "testing" "veza-backend-api/internal/models" "github.com/stretchr/testify/assert" "gorm.io/driver/sqlite" "gorm.io/gorm" ) // setupTestDB crée une base de données de test func setupTestDB(t *testing.T) *gorm.DB { db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{}) if err != nil { t.Fatalf("Failed to open test database: %v", err) } // Auto-migrate err = db.AutoMigrate(&models.User{}) if err != nil { t.Fatalf("Failed to migrate: %v", err) } return db } func TestEmailValidator_ValidateFormat(t *testing.T) { db := setupTestDB(t) validator := NewEmailValidator(db) tests := []struct { name string email string want bool }{ { name: "valid email", email: "test@example.com", want: true, }, { name: "valid email with subdomain", email: "user@mail.example.com", want: true, }, { name: "valid email with plus", email: "user+tag@example.com", want: true, }, { name: "valid email with dots", email: "first.last@example.com", want: true, }, { name: "valid email with numbers", email: "user123@example.com", want: true, }, { name: "valid email with underscore", email: "user_name@example.com", want: true, }, { name: "invalid email - no @", email: "invalidemail.com", want: false, }, { name: "invalid email - no domain", email: "invalid@", want: false, }, { name: "invalid email - no local part", email: "@example.com", want: false, }, { name: "invalid email - no TLD", email: "invalid@example", want: false, }, { name: "invalid email - too long", email: strings.Repeat("a", 250) + "@example.com", want: false, }, { name: "invalid email - local part starts with dot", email: ".user@example.com", want: false, }, { name: "invalid email - local part ends with dot", email: "user.@example.com", want: false, }, { name: "invalid email - domain starts with dot", email: "user@.example.com", want: false, }, { name: "invalid email - domain ends with dot", email: "user@example.com.", want: false, }, { name: "invalid email - domain starts with hyphen", email: "user@-example.com", want: false, }, { name: "invalid email - domain ends with hyphen", email: "user@example.com-", want: false, }, { name: "invalid email - empty string", email: "", want: false, }, { name: "invalid email - only spaces", email: " ", want: false, }, { name: "valid email - trimmed spaces", email: " test@example.com ", want: true, }, { name: "valid email - case insensitive", email: "TEST@EXAMPLE.COM", want: true, }, { name: "valid email - with percent", email: "user%tag@example.com", want: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := validator.ValidateFormat(tt.email) assert.Equal(t, tt.want, got, "ValidateFormat(%q) = %v, want %v", tt.email, got, tt.want) }) } } func TestEmailValidator_IsUnique(t *testing.T) { db := setupTestDB(t) validator := NewEmailValidator(db) // Créer un utilisateur de test user := &models.User{ Email: "existing@example.com", Username: "existing", Role: "user", } err := db.Create(user).Error assert.NoError(t, err) tests := []struct { name string email string want bool wantErr bool }{ { name: "unique email", email: "new@example.com", want: true, wantErr: false, }, { name: "existing email", email: "existing@example.com", want: false, wantErr: false, }, { name: "existing email - case insensitive", email: "EXISTING@EXAMPLE.COM", want: false, wantErr: false, }, { name: "existing email - with spaces", email: " existing@example.com ", want: false, wantErr: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got, err := validator.IsUnique(tt.email) if tt.wantErr { assert.Error(t, err) } else { assert.NoError(t, err) assert.Equal(t, tt.want, got, "IsUnique(%q) = %v, want %v", tt.email, got, tt.want) } }) } } func TestEmailValidator_Validate(t *testing.T) { db := setupTestDB(t) validator := NewEmailValidator(db) // Créer un utilisateur de test user := &models.User{ Email: "existing@example.com", Username: "existing", Role: "user", } err := db.Create(user).Error assert.NoError(t, err) tests := []struct { name string email string wantErr bool errMsg string }{ { name: "valid and unique email", email: "new@example.com", wantErr: false, }, { name: "invalid format", email: "invalid-email", wantErr: true, errMsg: "invalid email format", }, { name: "existing email", email: "existing@example.com", wantErr: true, errMsg: "email already exists", }, { name: "empty email", email: "", wantErr: true, errMsg: "invalid email format", }, { name: "email with invalid format - no @", email: "invalidemail.com", wantErr: true, errMsg: "invalid email format", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { err := validator.Validate(tt.email) if tt.wantErr { assert.Error(t, err) if tt.errMsg != "" { assert.Contains(t, err.Error(), tt.errMsg) } } else { assert.NoError(t, err) } }) } } func TestEmailValidator_ValidateFormat_EdgeCases(t *testing.T) { db := setupTestDB(t) validator := NewEmailValidator(db) tests := []struct { name string email string want bool }{ { name: "email at max length (254 chars)", email: strings.Repeat("a", 64) + "@" + strings.Repeat("b", 186) + ".co", want: true, }, { name: "email over max length (255 chars)", email: strings.Repeat("a", 64) + "@" + strings.Repeat("b", 187) + ".co", want: false, }, { name: "local part at max length (64 chars)", email: strings.Repeat("a", 64) + "@example.com", want: true, }, { name: "local part over max length (65 chars)", email: strings.Repeat("a", 65) + "@example.com", want: false, }, { name: "domain at max length (253 chars)", email: "user@" + strings.Repeat("a", 253), want: false, // Domain doit avoir au moins un point }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := validator.ValidateFormat(tt.email) assert.Equal(t, tt.want, got, "ValidateFormat(%q) = %v, want %v", tt.email, got, tt.want) }) } }