package validation import ( "encoding/json" "fmt" "testing" ) func TestNewGenericValidator(t *testing.T) { validator := NewGenericValidator() if validator == nil { t.Fatal("Expected validator to be created") } if validator.errors == nil { t.Fatal("Expected errors map to be initialized") } if len(validator.errors) != 0 { t.Fatal("Expected empty errors map") } } func TestGenericValidator_Validate_Required(t *testing.T) { validator := NewGenericValidator() schema := Schema{ "name": Rule{ Field: "name", Required: true, }, "email": Rule{ Field: "email", Required: true, }, } data := map[string]interface{}{ "name": "John", // email is missing } validator.Validate(data, schema) if !validator.HasErrors() { t.Fatal("Expected validation errors") } errors := validator.GetErrors() if len(errors) != 1 { t.Fatalf("Expected 1 error, got %d", len(errors)) } if errors["[email]"] != "This field is missing." { t.Fatalf("Expected email error, got: %s", errors["[email]"]) } } func TestGenericValidator_Validate_Type(t *testing.T) { validator := NewGenericValidator() schema := Schema{ "age": Rule{ Field: "age", Type: "int", }, "price": Rule{ Field: "price", Type: "float", }, "active": Rule{ Field: "active", Type: "bool", }, } data := map[string]interface{}{ "age": "not a number", "price": "invalid", "active": "not boolean", } validator.Validate(data, schema) if !validator.HasErrors() { t.Fatal("Expected validation errors") } errors := validator.GetErrors() if len(errors) != 3 { t.Fatalf("Expected 3 errors, got %d", len(errors)) } } func TestGenericValidator_Validate_Range(t *testing.T) { validator := NewGenericValidator() min := 1.0 max := 100.0 schema := Schema{ "score": Rule{ Field: "score", Min: &min, Max: &max, }, } data := map[string]interface{}{ "score": 0.5, // below min } validator.Validate(data, schema) if !validator.HasErrors() { t.Fatal("Expected validation errors") } errors := validator.GetErrors() if len(errors) != 1 { t.Fatalf("Expected 1 error, got %d", len(errors)) } if errors["[score]"] != "این مقدار باید بزرگتر و یا مساوی 1 باشد." { t.Fatalf("Expected range error, got: %s", errors["[score]"]) } } func TestGenericValidator_Validate_Length(t *testing.T) { validator := NewGenericValidator() minLength := 3 maxLength := 10 schema := Schema{ "name": Rule{ Field: "name", MinLength: &minLength, MaxLength: &maxLength, }, "tags": Rule{ Field: "tags", MinLength: &minLength, MaxLength: &maxLength, }, } data := map[string]interface{}{ "name": "ab", // too short "tags": []interface{}{"tag1", "tag2"}, // too few } validator.Validate(data, schema) if !validator.HasErrors() { t.Fatal("Expected validation errors") } errors := validator.GetErrors() if len(errors) != 2 { t.Fatalf("Expected 2 errors, got %d", len(errors)) } } func TestGenericValidator_Validate_Custom(t *testing.T) { validator := NewGenericValidator() schema := Schema{ "code": Rule{ Field: "code", Custom: func(value interface{}) error { if str, ok := value.(string); ok { if len(str) != 6 { return fmt.Errorf("کد باید 6 کاراکتر باشد.") } } return nil }, }, } data := map[string]interface{}{ "code": "12345", // too short } validator.Validate(data, schema) if !validator.HasErrors() { t.Fatal("Expected validation errors") } errors := validator.GetErrors() if len(errors) != 1 { t.Fatalf("Expected 1 error, got %d", len(errors)) } if errors["[code]"] != "کد باید 6 کاراکتر باشد." { t.Fatalf("Expected custom error, got: %s", errors["[code]"]) } } func TestGenericValidator_ValidateNested(t *testing.T) { validator := NewGenericValidator() schema := Schema{ "name": Rule{ Field: "name", Required: true, }, "age": Rule{ Field: "age", Type: "int", }, } nestedData := map[string]interface{}{ "users": []interface{}{ map[string]interface{}{ "name": "John", "age": "not a number", }, map[string]interface{}{ // name is missing "age": 25, }, }, } validator.ValidateNested(nestedData["users"], schema, "[users]") if !validator.HasErrors() { t.Fatal("Expected validation errors") } errors := validator.GetErrors() if len(errors) != 3 { t.Fatalf("Expected 3 errors, got %d", len(errors)) } // Check for expected errors expectedErrors := map[string]bool{ "[users][0][age]": true, // age is string instead of int "[users][1][name]": true, // name is missing (required) "[users][1][age]": true, // age is int (valid) } for path := range errors { if !expectedErrors[path] { t.Fatalf("Unexpected error path: %s", path) } } } func TestGenericValidator_ValidateRequired(t *testing.T) { validator := NewGenericValidator() data := map[string]interface{}{ "name": "John", "email": "", "tags": []interface{}{}, "missing": nil, } validator.ValidateRequired(data, "name", "[name]") validator.ValidateRequired(data, "email", "[email]") validator.ValidateRequired(data, "tags", "[tags]") validator.ValidateRequired(data, "missing", "[missing]") if !validator.HasErrors() { t.Fatal("Expected validation errors") } errors := validator.GetErrors() if len(errors) != 3 { t.Fatalf("Expected 3 errors, got %d", len(errors)) } } func TestGenericValidator_ValidatePrice(t *testing.T) { validator := NewGenericValidator() data := map[string]interface{}{ "price1": 100.0, "price2": 0.5, "price3": "invalid", "price4": -10.0, } validator.ValidatePrice(data, "price1", "[price1]") validator.ValidatePrice(data, "price2", "[price2]") validator.ValidatePrice(data, "price3", "[price3]") validator.ValidatePrice(data, "price4", "[price4]") if !validator.HasErrors() { t.Fatal("Expected validation errors") } errors := validator.GetErrors() if len(errors) != 3 { t.Fatalf("Expected 3 errors, got %d", len(errors)) } } func TestGenericValidator_ValidateQuantity(t *testing.T) { validator := NewGenericValidator() data := map[string]interface{}{ "qty1": 10, "qty2": -5, "qty3": 3.5, "qty4": "invalid", } validator.ValidateQuantity(data, "qty1", "[qty1]") validator.ValidateQuantity(data, "qty2", "[qty2]") validator.ValidateQuantity(data, "qty3", "[qty3]") validator.ValidateQuantity(data, "qty4", "[qty4]") if !validator.HasErrors() { t.Fatal("Expected validation errors") } errors := validator.GetErrors() if len(errors) != 3 { t.Fatalf("Expected 3 errors, got %d", len(errors)) } } func TestGenericValidator_ToJSON(t *testing.T) { validator := NewGenericValidator() validator.AddError("[name]", "این فیلد الزامی است.") validator.AddError("[email]", "ایمیل نامعتبر است.") jsonData, err := validator.ToJSON() if err != nil { t.Fatalf("Expected no error, got: %v", err) } var response ErrorResponse if err := json.Unmarshal(jsonData, &response); err != nil { t.Fatalf("Expected valid JSON, got: %v", err) } if len(response.Errors) != 2 { t.Fatalf("Expected 2 errors, got %d", len(response.Errors)) } if response.Errors["[name]"] != "این فیلد الزامی است." { t.Fatalf("Expected name error, got: %s", response.Errors["[name]"]) } } func TestValidateData(t *testing.T) { schema := Schema{ "name": Rule{ Field: "name", Required: true, }, "age": Rule{ Field: "age", Type: "int", }, } data := map[string]interface{}{ "name": "John", "age": "not a number", } validator := ValidateData(data, schema) if !validator.HasErrors() { t.Fatal("Expected validation errors") } errors := validator.GetErrors() if len(errors) != 1 { t.Fatalf("Expected 1 error, got %d", len(errors)) } } func TestValidateJSONData(t *testing.T) { schema := Schema{ "name": Rule{ Field: "name", Required: true, }, } jsonData := []byte(`{"name": "John"}`) validator, err := ValidateJSONData(jsonData, schema) if err != nil { t.Fatalf("Expected no error, got: %v", err) } if validator.HasErrors() { t.Fatal("Expected no validation errors") } // Test invalid JSON invalidJSON := []byte(`{"name": "John"`) _, err = ValidateJSONData(invalidJSON, schema) if err == nil { t.Fatal("Expected JSON parsing error") } } func TestGenericValidator_ComplexNestedValidation(t *testing.T) { validator := NewGenericValidator() // Schema for user object userSchema := Schema{ "name": Rule{ Field: "name", Required: true, }, "age": Rule{ Field: "age", Type: "int", }, "email": Rule{ Field: "email", Type: "string", }, } // Complex nested data data := map[string]interface{}{ "users": []interface{}{ map[string]interface{}{ "name": "John", "age": 25, "email": "john@example.com", }, map[string]interface{}{ "name": "Jane", "age": "not a number", "email": "jane@example.com", }, map[string]interface{}{ // missing name "age": 30, "email": "bob@example.com", }, }, "settings": map[string]interface{}{ "theme": "dark", "lang": "en", }, } // Validate nested users array validator.ValidateNested(data["users"], userSchema, "[users]") if !validator.HasErrors() { t.Fatal("Expected validation errors") } errors := validator.GetErrors() if len(errors) != 4 { t.Fatalf("Expected 4 errors, got %d: %v", len(errors), errors) } // Check specific errors expectedErrors := map[string]bool{ "[users][1][age]": true, // age is string instead of int "[users][2][name]": true, // name is missing (required) "[users][0][age]": true, // age is int (valid) "[users][0][name]": true, // name is string (valid) "[users][2][age]": true, // age is int (valid) } for path := range errors { if !expectedErrors[path] { t.Fatalf("Unexpected error path: %s", path) } } } func TestGenericValidator_NoErrors(t *testing.T) { validator := NewGenericValidator() schema := Schema{ "name": Rule{ Field: "name", Required: true, }, "age": Rule{ Field: "age", Type: "int", }, } data := map[string]interface{}{ "name": "John", "age": 25.0, // Use float64 to match JSON unmarshaling } validator.Validate(data, schema) if validator.HasErrors() { errors := validator.GetErrors() t.Fatalf("Expected no validation errors, got: %v", errors) } errors := validator.GetErrors() if len(errors) != 0 { t.Fatalf("Expected 0 errors, got %d", len(errors)) } } func TestGenericValidator_EnqueueVendorStocksRequest(t *testing.T) { itemSchema := Schema{ "barcode": Rule{ Field: "barcode", Type: "string", Required: true, MinLength: func() *int { i := 1; return &i }(), }, "stock": Rule{ Field: "stock", Type: "int", Required: true, }, } schema := Schema{ "stocks": Rule{ Field: "stocks", Type: "array", Required: true, MinLength: func() *int { i := 1; return &i }(), ArrayOf: itemSchema, }, } // Valid payload valid := map[string]interface{}{ "vendorId": 123, "vendorCode": "VEND123", "stocks": []interface{}{ map[string]interface{}{ "barcode": "1234567890", "stock": 10.0, }, map[string]interface{}{ "barcode": "0987654321", "stock": 5.0, }, }, } validator := NewGenericValidator() validator.Validate(valid, schema) if validator.HasErrors() { t.Fatalf("Expected no validation errors, got: %v", validator.GetErrors()) } // Invalid payload: missing items, empty barcode, non-int stock invalid := map[string]interface{}{ "stocks": []interface{}{ map[string]interface{}{ "barcode": "", "stock": "not-an-int", }, }, } validator = NewGenericValidator() validator.Validate(invalid, schema) if !validator.HasErrors() { t.Fatal("Expected validation errors") } errors := validator.GetErrors() if len(errors) != 2 { t.Fatalf("Expected 2 errors, got %d: %v", len(errors), errors) } if _, ok := errors["[stocks][0][barcode]"]; !ok { t.Error("Expected error for empty barcode") } if _, ok := errors["[stocks][0][stock]"]; !ok { t.Error("Expected error for non-int stock") } } func TestGenericValidator_CustomErrorMessages(t *testing.T) { schema := Schema{ "name": Rule{ Field: "name", Type: "string", Required: true, RequiredMessage: "نام کاربر الزامی است.", TypeMessage: "نام باید از نوع متن باشد.", }, "age": Rule{ Field: "age", Type: "int", Min: func() *float64 { f := 18.0; return &f }(), Max: func() *float64 { f := 100.0; return &f }(), MinMessage: "سن باید حداقل 18 سال باشد.", MaxMessage: "سن نمی تواند بیشتر از 100 سال باشد.", TypeMessage: "سن باید عدد صحیح باشد.", }, "email": Rule{ Field: "email", Type: "string", Required: true, RequiredMessage: "ایمیل الزامی است.", PatternMessage: "فرمت ایمیل نامعتبر است.", }, } // Test with invalid data data := map[string]interface{}{ "name": 123, // wrong type "age": "invalid", // wrong type // email is missing (not empty) } validator := NewGenericValidator() validator.Validate(data, schema) if !validator.HasErrors() { t.Fatal("Expected validation errors") } errors := validator.GetErrors() // Check custom error messages if errors["[name]"] != "نام باید از نوع متن باشد." { t.Errorf("Expected custom type error for name, got: %s", errors["[name]"]) } if errors["[age]"] != "سن باید عدد صحیح باشد." { t.Errorf("Expected custom type error for age, got: %s", errors["[age]"]) } if errors["[email]"] != "ایمیل الزامی است." { t.Errorf("Expected custom required error for email, got: %s", errors["[email]"]) } }