github.com/kaptinlin/jsonschema@v0.4.6/tests/custom_format_test.go (about) 1 package tests 2 3 import ( 4 "math" 5 "regexp" 6 "strings" 7 "testing" 8 9 "github.com/kaptinlin/jsonschema" 10 "github.com/stretchr/testify/assert" 11 "github.com/stretchr/testify/require" 12 ) 13 14 // --- Test Helpers for OpenAPI Formats --- 15 16 func validateTestInt32(v interface{}) bool { 17 switch val := v.(type) { 18 case int: 19 return val >= math.MinInt32 && val <= math.MaxInt32 20 case int64: 21 return val >= math.MinInt32 && val <= math.MaxInt32 22 case float64: 23 return val == float64(int64(val)) && val >= math.MinInt32 && val <= math.MaxInt32 24 default: 25 return false 26 } 27 } 28 29 func validateTestInt64(v interface{}) bool { 30 switch val := v.(type) { 31 case int, int64: 32 // Any int/int64 fits into int64 range and is already integral 33 return true 34 case float64: 35 // Must be an integer value 36 if val != float64(int64(val)) { 37 return false 38 } 39 // Check bounds using float64 limits of int64 40 return val >= float64(math.MinInt64) && val <= float64(math.MaxInt64) 41 default: 42 return false 43 } 44 } 45 46 func registerTestOpenAPIFormats(c *jsonschema.Compiler) { 47 c.RegisterFormat("int32", validateTestInt32, "number") 48 c.RegisterFormat("int64", validateTestInt64, "number") 49 } 50 51 func TestCustomFormatRegistration(t *testing.T) { 52 compiler := jsonschema.NewCompiler() 53 compiler.SetAssertFormat(true) 54 55 compiler.RegisterFormat("identifier", func(v interface{}) bool { 56 s, ok := v.(string) 57 if !ok { 58 return true 59 } 60 matched, _ := regexp.MatchString(`^[a-z$_][a-zA-Z$_0-9]*$`, s) 61 return matched 62 }, "string") 63 64 schema, err := compiler.Compile([]byte(`{"properties": {"name": {"type": "string", "format": "identifier"}}}`)) 65 require.NoError(t, err) 66 67 assert.True(t, schema.Validate(map[string]interface{}{"name": "validName"}).IsValid()) 68 assert.False(t, schema.Validate(map[string]interface{}{"name": "123invalid"}).IsValid()) 69 } 70 71 func TestTypeSpecificFormats(t *testing.T) { 72 compiler := jsonschema.NewCompiler() 73 compiler.SetAssertFormat(true) 74 75 compiler.RegisterFormat("percentage", func(v interface{}) bool { 76 switch val := v.(type) { 77 case float64: 78 return val >= 0 && val <= 100 79 case int: 80 return val >= 0 && val <= 100 81 } 82 return false 83 }, "number") 84 85 schema, err := compiler.Compile([]byte(`{"properties": {"score": {"type": "number", "format": "percentage"}, "name": {"type": "string", "format": "percentage"}}}`)) 86 require.NoError(t, err) 87 88 assert.True(t, schema.Validate(map[string]interface{}{"score": 85.5, "name": "test"}).IsValid()) 89 assert.False(t, schema.Validate(map[string]interface{}{"score": 150.0, "name": "test"}).IsValid()) 90 } 91 92 func TestCustomFormatOverridesGlobal(t *testing.T) { 93 compiler := jsonschema.NewCompiler() 94 compiler.SetAssertFormat(true) 95 96 compiler.RegisterFormat("email", func(v interface{}) bool { 97 return strings.Contains(v.(string), "@") && len(v.(string)) > 5 98 }, "string") 99 100 schema, err := compiler.Compile([]byte(`{"properties": {"email": {"type": "string", "format": "email"}}}`)) 101 require.NoError(t, err) 102 103 assert.True(t, schema.Validate(map[string]interface{}{"email": "test@example.com"}).IsValid()) 104 assert.False(t, schema.Validate(map[string]interface{}{"email": "short"}).IsValid()) 105 } 106 107 func TestUnregisterCustomFormat(t *testing.T) { 108 compiler := jsonschema.NewCompiler() 109 compiler.SetAssertFormat(true) 110 111 compiler.RegisterFormat("test-format", func(v interface{}) bool { return false }, "string") 112 compiler.UnregisterFormat("test-format") 113 114 schema, err := compiler.Compile([]byte(`{"type": "string", "format": "test-format"}`)) 115 require.NoError(t, err) 116 117 assert.False(t, schema.Validate("test").IsValid(), "Validation should fail for an unregistered format when AssertFormat is true") 118 } 119 120 func TestOpenAPICustomFormatValidation(t *testing.T) { 121 compiler := jsonschema.NewCompiler() 122 compiler.SetAssertFormat(true) 123 registerTestOpenAPIFormats(compiler) 124 125 t.Run("int32", func(t *testing.T) { 126 schema, err := compiler.Compile([]byte(`{"type": "number", "format": "int32"}`)) 127 require.NoError(t, err) 128 129 assert.True(t, schema.Validate(123).IsValid()) 130 assert.True(t, schema.Validate(float64(2147483647)).IsValid()) 131 assert.False(t, schema.Validate(float64(2147483648)).IsValid(), "int32 overflow") 132 assert.False(t, schema.Validate(123.45).IsValid(), "int32 with fraction") 133 }) 134 135 t.Run("int64", func(t *testing.T) { 136 schema, err := compiler.Compile([]byte(`{"type": "number", "format": "int64"}`)) 137 require.NoError(t, err) 138 139 assert.True(t, schema.Validate(1234567890).IsValid()) 140 assert.False(t, schema.Validate(float64(math.MaxInt64)*2).IsValid(), "int64 overflow") 141 assert.False(t, schema.Validate(12345.67).IsValid(), "int64 with fraction") 142 }) 143 }