initial commit

This commit is contained in:
m.zare
2026-04-10 18:25:21 +03:30
commit 77ca6c34a3
263 changed files with 34470 additions and 0 deletions

View File

@@ -0,0 +1,642 @@
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]"])
}
}