package validator import ( "testing" govalidator "github.com/go-playground/validator/v10" ) func TestValidatePasswordComplexity(t *testing.T) { tests := []struct { name string password string valid bool }{ {"valid password", "Password1", true}, {"valid complex password", "MyP@ssw0rd!", true}, {"missing uppercase", "password1", false}, {"missing lowercase", "PASSWORD1", false}, {"missing digit", "Password", false}, {"only digits", "12345678", false}, {"only lowercase", "abcdefgh", false}, {"only uppercase", "ABCDEFGH", false}, {"empty string", "", false}, {"single valid char each", "aA1", true}, {"unicode uppercase with digit and lower", "Über1abc", true}, } v := govalidator.New() v.RegisterValidation("password_complexity", validatePasswordComplexity) type testStruct struct { Password string `validate:"password_complexity"` } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { s := testStruct{Password: tc.password} err := v.Struct(s) if tc.valid && err != nil { t.Errorf("expected password %q to be valid, got error: %v", tc.password, err) } if !tc.valid && err == nil { t.Errorf("expected password %q to be invalid, got nil error", tc.password) } }) } } func TestValidatePasswordComplexityWithMinLength(t *testing.T) { v := govalidator.New() v.RegisterValidation("password_complexity", validatePasswordComplexity) type request struct { Password string `validate:"required,min=8,password_complexity"` } tests := []struct { name string password string valid bool }{ {"valid 8+ chars with complexity", "Abcdefg1", true}, {"too short but complex", "Ab1", false}, {"long but no uppercase", "abcdefgh1", false}, {"long but no lowercase", "ABCDEFGH1", false}, {"long but no digit", "Abcdefghi", false}, {"empty", "", false}, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { r := request{Password: tc.password} err := v.Struct(r) if tc.valid && err != nil { t.Errorf("expected %q to be valid, got error: %v", tc.password, err) } if !tc.valid && err == nil { t.Errorf("expected %q to be invalid, got nil", tc.password) } }) } } func TestFormatMessagePasswordComplexity(t *testing.T) { cv := NewCustomValidator() type request struct { Password string `json:"password" validate:"required,min=8,password_complexity"` } r := request{Password: "lowercase1"} err := cv.Validate(r) if err == nil { t.Fatal("expected validation error for password without uppercase") } resp := FormatValidationErrors(err) if resp == nil { t.Fatal("expected non-nil error response") } field, ok := resp.Fields["password"] if !ok { t.Fatal("expected 'password' field in error response") } expectedMsg := "Password must be at least 8 characters with at least one uppercase letter, one lowercase letter, and one digit" if field.Message != expectedMsg { t.Errorf("expected message %q, got %q", expectedMsg, field.Message) } if field.Tag != "password_complexity" { t.Errorf("expected tag 'password_complexity', got %q", field.Tag) } }