github.com/kaptinlin/jsonschema@v0.4.6/keywords_test.go (about) 1 package jsonschema_test 2 3 import ( 4 "testing" 5 6 "github.com/kaptinlin/jsonschema" 7 ) 8 9 func TestStringKeywords(t *testing.T) { 10 tests := []struct { 11 name string 12 schema *jsonschema.Schema 13 valid interface{} 14 invalid interface{} 15 }{ 16 { 17 name: "MinLen valid", 18 schema: jsonschema.String(jsonschema.MinLen(3)), 19 valid: "hello", 20 invalid: "hi", 21 }, 22 { 23 name: "MinLen invalid", 24 schema: jsonschema.String(jsonschema.MinLen(5)), 25 valid: "hello", 26 invalid: "hi", 27 }, 28 { 29 name: "MaxLen valid", 30 schema: jsonschema.String(jsonschema.MaxLen(5)), 31 valid: "hello", 32 invalid: "hello world", 33 }, 34 { 35 name: "MaxLen invalid", 36 schema: jsonschema.String(jsonschema.MaxLen(3)), 37 valid: "hi", 38 invalid: "hello", 39 }, 40 { 41 name: "Pattern valid", 42 schema: jsonschema.String(jsonschema.Pattern("^[a-z]+$")), 43 valid: "hello", 44 invalid: "Hello123", 45 }, 46 { 47 name: "Pattern invalid", 48 schema: jsonschema.String(jsonschema.Pattern("^\\d+$")), 49 valid: "123", 50 invalid: "abc", 51 }, 52 { 53 name: "Combined string keywords", 54 schema: jsonschema.String( 55 jsonschema.MinLen(3), 56 jsonschema.MaxLen(10), 57 jsonschema.Pattern("^[a-z]+$"), 58 ), 59 valid: "hello", 60 invalid: "Hi", 61 }, 62 } 63 64 for _, tt := range tests { 65 t.Run(tt.name, func(t *testing.T) { 66 // Test valid data 67 result := tt.schema.Validate(tt.valid) 68 if !result.IsValid() { 69 t.Errorf("Expected valid data to pass validation, got errors: %v", result.Errors) 70 } 71 72 // Test invalid data 73 result = tt.schema.Validate(tt.invalid) 74 if result.IsValid() { 75 t.Errorf("Expected invalid data to fail validation") 76 } 77 }) 78 } 79 } 80 81 func TestNumberKeywords(t *testing.T) { 82 tests := []struct { 83 name string 84 schema *jsonschema.Schema 85 valid interface{} 86 invalid interface{} 87 }{ 88 { 89 name: "Min valid", 90 schema: jsonschema.Number(jsonschema.Min(5)), 91 valid: 10.5, 92 invalid: 3.2, 93 }, 94 { 95 name: "Min invalid", 96 schema: jsonschema.Integer(jsonschema.Min(10)), 97 valid: 15, 98 invalid: 5, 99 }, 100 { 101 name: "Max valid", 102 schema: jsonschema.Number(jsonschema.Max(100)), 103 valid: 50.5, 104 invalid: 150.2, 105 }, 106 { 107 name: "Max invalid", 108 schema: jsonschema.Integer(jsonschema.Max(50)), 109 valid: 25, 110 invalid: 75, 111 }, 112 { 113 name: "ExclusiveMin valid", 114 schema: jsonschema.Number(jsonschema.ExclusiveMin(0)), 115 valid: 0.1, 116 invalid: 0, 117 }, 118 { 119 name: "ExclusiveMin invalid", 120 schema: jsonschema.Number(jsonschema.ExclusiveMin(10)), 121 valid: 10.1, 122 invalid: 10, 123 }, 124 { 125 name: "ExclusiveMax valid", 126 schema: jsonschema.Number(jsonschema.ExclusiveMax(100)), 127 valid: 99.9, 128 invalid: 100, 129 }, 130 { 131 name: "ExclusiveMax invalid", 132 schema: jsonschema.Number(jsonschema.ExclusiveMax(50)), 133 valid: 49.9, 134 invalid: 50, 135 }, 136 { 137 name: "MultipleOf valid", 138 schema: jsonschema.Number(jsonschema.MultipleOf(2.5)), 139 valid: 10.0, 140 invalid: 11.0, 141 }, 142 { 143 name: "MultipleOf invalid", 144 schema: jsonschema.Integer(jsonschema.MultipleOf(3)), 145 valid: 9, 146 invalid: 10, 147 }, 148 { 149 name: "Combined number keywords", 150 schema: jsonschema.Number( 151 jsonschema.Min(0), 152 jsonschema.Max(100), 153 jsonschema.MultipleOf(5), 154 ), 155 valid: 25.0, 156 invalid: 23.0, 157 }, 158 } 159 160 for _, tt := range tests { 161 t.Run(tt.name, func(t *testing.T) { 162 // Test valid data 163 result := tt.schema.Validate(tt.valid) 164 if !result.IsValid() { 165 t.Errorf("Expected valid data to pass validation, got errors: %v", result.Errors) 166 } 167 168 // Test invalid data 169 result = tt.schema.Validate(tt.invalid) 170 if result.IsValid() { 171 t.Errorf("Expected invalid data to fail validation") 172 } 173 }) 174 } 175 } 176 177 func TestArrayKeywords(t *testing.T) { 178 tests := []struct { 179 name string 180 schema *jsonschema.Schema 181 valid interface{} 182 invalid interface{} 183 }{ 184 { 185 name: "Items valid", 186 schema: jsonschema.Array(jsonschema.Items(jsonschema.String())), 187 valid: []interface{}{"a", "b", "c"}, 188 invalid: []interface{}{"a", 123, "c"}, 189 }, 190 { 191 name: "Items invalid", 192 schema: jsonschema.Array(jsonschema.Items(jsonschema.Integer())), 193 valid: []interface{}{1, 2, 3}, 194 invalid: []interface{}{1, "two", 3}, 195 }, 196 { 197 name: "MinItems valid", 198 schema: jsonschema.Array(jsonschema.MinItems(2)), 199 valid: []interface{}{1, 2, 3}, 200 invalid: []interface{}{1}, 201 }, 202 { 203 name: "MinItems invalid", 204 schema: jsonschema.Array(jsonschema.MinItems(3)), 205 valid: []interface{}{1, 2, 3, 4}, 206 invalid: []interface{}{1, 2}, 207 }, 208 { 209 name: "MaxItems valid", 210 schema: jsonschema.Array(jsonschema.MaxItems(3)), 211 valid: []interface{}{1, 2}, 212 invalid: []interface{}{1, 2, 3, 4}, 213 }, 214 { 215 name: "MaxItems invalid", 216 schema: jsonschema.Array(jsonschema.MaxItems(2)), 217 valid: []interface{}{1, 2}, 218 invalid: []interface{}{1, 2, 3}, 219 }, 220 { 221 name: "UniqueItems valid", 222 schema: jsonschema.Array(jsonschema.UniqueItems(true)), 223 valid: []interface{}{1, 2, 3}, 224 invalid: []interface{}{1, 2, 2, 3}, 225 }, 226 { 227 name: "UniqueItems invalid", 228 schema: jsonschema.Array(jsonschema.UniqueItems(true)), 229 valid: []interface{}{"a", "b", "c"}, 230 invalid: []interface{}{"a", "b", "a"}, 231 }, 232 { 233 name: "Combined array keywords", 234 schema: jsonschema.Array( 235 jsonschema.Items(jsonschema.String()), 236 jsonschema.MinItems(2), 237 jsonschema.MaxItems(5), 238 jsonschema.UniqueItems(true), 239 ), 240 valid: []interface{}{"a", "b", "c"}, 241 invalid: []interface{}{"a"}, 242 }, 243 } 244 245 for _, tt := range tests { 246 t.Run(tt.name, func(t *testing.T) { 247 // Test valid data 248 result := tt.schema.Validate(tt.valid) 249 if !result.IsValid() { 250 t.Errorf("Expected valid data to pass validation, got errors: %v", result.Errors) 251 } 252 253 // Test invalid data 254 result = tt.schema.Validate(tt.invalid) 255 if result.IsValid() { 256 t.Errorf("Expected invalid data to fail validation") 257 } 258 }) 259 } 260 } 261 262 func TestObjectKeywords(t *testing.T) { 263 tests := []struct { 264 name string 265 schema *jsonschema.Schema 266 valid interface{} 267 invalid interface{} 268 }{ 269 { 270 name: "Required valid", 271 schema: jsonschema.Object( 272 jsonschema.Prop("name", jsonschema.String()), 273 jsonschema.Required("name"), 274 ), 275 valid: map[string]interface{}{"name": "John"}, 276 invalid: map[string]interface{}{"age": 25}, 277 }, 278 { 279 name: "Required invalid", 280 schema: jsonschema.Object( 281 jsonschema.Prop("name", jsonschema.String()), 282 jsonschema.Prop("age", jsonschema.Integer()), 283 jsonschema.Required("name", "age"), 284 ), 285 valid: map[string]interface{}{"name": "John", "age": 25}, 286 invalid: map[string]interface{}{"name": "John"}, 287 }, 288 { 289 name: "MinProps valid", 290 schema: jsonschema.Object( 291 jsonschema.MinProps(2), 292 ), 293 valid: map[string]interface{}{"a": 1, "b": 2, "c": 3}, 294 invalid: map[string]interface{}{"a": 1}, 295 }, 296 { 297 name: "MinProps invalid", 298 schema: jsonschema.Object( 299 jsonschema.MinProps(3), 300 ), 301 valid: map[string]interface{}{"a": 1, "b": 2, "c": 3}, 302 invalid: map[string]interface{}{"a": 1, "b": 2}, 303 }, 304 { 305 name: "MaxProps valid", 306 schema: jsonschema.Object( 307 jsonschema.MaxProps(3), 308 ), 309 valid: map[string]interface{}{"a": 1, "b": 2}, 310 invalid: map[string]interface{}{"a": 1, "b": 2, "c": 3, "d": 4}, 311 }, 312 { 313 name: "MaxProps invalid", 314 schema: jsonschema.Object( 315 jsonschema.MaxProps(2), 316 ), 317 valid: map[string]interface{}{"a": 1, "b": 2}, 318 invalid: map[string]interface{}{"a": 1, "b": 2, "c": 3}, 319 }, 320 { 321 name: "AdditionalProps false valid", 322 schema: jsonschema.Object( 323 jsonschema.Prop("name", jsonschema.String()), 324 jsonschema.AdditionalProps(false), 325 ), 326 valid: map[string]interface{}{"name": "John"}, 327 invalid: map[string]interface{}{"name": "John", "age": 25}, 328 }, 329 { 330 name: "AdditionalProps false invalid", 331 schema: jsonschema.Object( 332 jsonschema.Prop("name", jsonschema.String()), 333 jsonschema.AdditionalProps(false), 334 ), 335 valid: map[string]interface{}{"name": "John"}, 336 invalid: map[string]interface{}{"name": "John", "extra": "value"}, 337 }, 338 } 339 340 for _, tt := range tests { 341 t.Run(tt.name, func(t *testing.T) { 342 // Test valid data 343 result := tt.schema.Validate(tt.valid) 344 if !result.IsValid() { 345 t.Errorf("Expected valid data to pass validation, got errors: %v", result.Errors) 346 } 347 348 // Test invalid data 349 result = tt.schema.Validate(tt.invalid) 350 if result.IsValid() { 351 t.Errorf("Expected invalid data to fail validation") 352 } 353 }) 354 } 355 } 356 357 func TestBasicConvenienceFunctions(t *testing.T) { 358 tests := []struct { 359 name string 360 schema *jsonschema.Schema 361 valid interface{} 362 invalid interface{} 363 }{ 364 { 365 name: "PositiveInt valid", 366 schema: jsonschema.PositiveInt(), 367 valid: 5, 368 invalid: 0, 369 }, 370 { 371 name: "PositiveInt invalid", 372 schema: jsonschema.PositiveInt(), 373 valid: 1, 374 invalid: -1, 375 }, 376 { 377 name: "NonNegativeInt valid", 378 schema: jsonschema.NonNegativeInt(), 379 valid: 0, 380 invalid: -1, 381 }, 382 { 383 name: "NonNegativeInt invalid", 384 schema: jsonschema.NonNegativeInt(), 385 valid: 5, 386 invalid: -5, 387 }, 388 } 389 390 for _, tt := range tests { 391 t.Run(tt.name, func(t *testing.T) { 392 // Test valid data 393 result := tt.schema.Validate(tt.valid) 394 if !result.IsValid() { 395 t.Errorf("Expected valid data to pass validation, got errors: %v", result.Errors) 396 } 397 398 // Test invalid data 399 result = tt.schema.Validate(tt.invalid) 400 if result.IsValid() { 401 t.Errorf("Expected invalid data to fail validation") 402 } 403 }) 404 } 405 } 406 407 func TestAnnotationKeywords(t *testing.T) { 408 // Test that annotation keywords don't affect validation 409 schema := jsonschema.String( 410 jsonschema.Title("User Name"), 411 jsonschema.Description("The user's display name"), 412 jsonschema.Default("Anonymous"), 413 jsonschema.Examples("John", "Jane"), 414 jsonschema.MinLen(1), 415 ) 416 417 result := schema.Validate("Alice") 418 if !result.IsValid() { 419 t.Errorf("Expected valid string to pass validation, got errors: %v", result.Errors) 420 } 421 422 result = schema.Validate("") 423 if result.IsValid() { 424 t.Errorf("Expected empty string to fail validation due to minLength") 425 } 426 } 427 428 func TestKeywordCombinations(t *testing.T) { 429 // Test complex combinations of different keyword types 430 schema := jsonschema.Object( 431 jsonschema.Prop("username", jsonschema.String( 432 jsonschema.MinLen(3), 433 jsonschema.MaxLen(20), 434 jsonschema.Pattern("^[a-zA-Z0-9_]+$"), 435 jsonschema.Title("Username"), 436 jsonschema.Description("User's login name"), 437 )), 438 jsonschema.Prop("age", jsonschema.Integer( 439 jsonschema.Min(0), 440 jsonschema.Max(150), 441 jsonschema.Title("Age"), 442 )), 443 jsonschema.Prop("tags", jsonschema.Array( 444 jsonschema.Items(jsonschema.String(jsonschema.MinLen(1))), 445 jsonschema.UniqueItems(true), 446 jsonschema.MaxItems(10), 447 )), 448 jsonschema.Required("username"), 449 jsonschema.AdditionalProps(false), 450 jsonschema.Title("User Registration"), 451 jsonschema.Description("Schema for user registration data"), 452 ) 453 454 validData := map[string]interface{}{ 455 "username": "john_doe", 456 "age": 25, 457 "tags": []interface{}{"developer", "golang"}, 458 } 459 460 result := schema.Validate(validData) 461 if !result.IsValid() { 462 t.Errorf("Expected valid data to pass validation, got errors: %v", result.Errors) 463 } 464 465 invalidData := map[string]interface{}{ 466 "username": "jo", // Too short 467 "age": 200, // Too old 468 "extra": "not allowed", 469 } 470 471 result = schema.Validate(invalidData) 472 if result.IsValid() { 473 t.Errorf("Expected invalid data to fail validation") 474 } 475 }