github.com/hamba/avro/v2@v2.22.1-0.20240518180522-aff3955acf7d/schema_internal_test.go (about) 1 package avro 2 3 import ( 4 "strconv" 5 "testing" 6 7 "github.com/stretchr/testify/assert" 8 "github.com/stretchr/testify/require" 9 ) 10 11 func TestName_NameAndNamespace(t *testing.T) { 12 n, err := newName("bar", "foo", nil) 13 require.NoError(t, err) 14 15 assert.Equal(t, "bar", n.Name()) 16 assert.Equal(t, "foo", n.Namespace()) 17 assert.Equal(t, "foo.bar", n.FullName()) 18 } 19 20 func TestName_QualifiedName(t *testing.T) { 21 n, err := newName("foo.bar", "test", nil) 22 require.NoError(t, err) 23 24 assert.Equal(t, "bar", n.Name()) 25 assert.Equal(t, "foo", n.Namespace()) 26 assert.Equal(t, "foo.bar", n.FullName()) 27 } 28 29 func TestName_NameAndNamespaceAndAlias(t *testing.T) { 30 n, err := newName("bar", "foo", []string{"baz", "test.bat"}) 31 require.NoError(t, err) 32 33 assert.Equal(t, "bar", n.Name()) 34 assert.Equal(t, "foo", n.Namespace()) 35 assert.Equal(t, "foo.bar", n.FullName()) 36 assert.Equal(t, []string{"foo.baz", "test.bat"}, n.Aliases()) 37 } 38 39 func TestName_EmpryName(t *testing.T) { 40 _, err := newName("", "foo", nil) 41 42 assert.Error(t, err) 43 } 44 45 func TestName_InvalidNameFirstChar(t *testing.T) { 46 _, err := newName("+bar", "foo", nil) 47 48 assert.Error(t, err) 49 } 50 51 func TestName_InvalidNameOtherChar(t *testing.T) { 52 _, err := newName("bar+", "foo", nil) 53 54 assert.Error(t, err) 55 } 56 57 func TestName_InvalidNamespaceFirstChar(t *testing.T) { 58 _, err := newName("bar", "+foo", nil) 59 60 assert.Error(t, err) 61 } 62 63 func TestName_InvalidNamespaceOtherChar(t *testing.T) { 64 _, err := newName("bar", "foo+", nil) 65 66 assert.Error(t, err) 67 } 68 69 func TestName_InvalidAliasFirstChar(t *testing.T) { 70 _, err := newName("bar", "foo", []string{"+bar"}) 71 72 assert.Error(t, err) 73 } 74 75 func TestName_InvalidAliasOtherChar(t *testing.T) { 76 _, err := newName("bar", "foo", []string{"bar+"}) 77 78 assert.Error(t, err) 79 } 80 81 func TestName_InvalidAliasFQNFirstChar(t *testing.T) { 82 _, err := newName("bar", "foo", []string{"test.+bar"}) 83 84 assert.Error(t, err) 85 } 86 87 func TestName_InvalidAliasFQNOtherChar(t *testing.T) { 88 _, err := newName("bar", "foo", []string{"test.bar+"}) 89 90 assert.Error(t, err) 91 } 92 93 func TestProperties_PropGetsFromEmptySet(t *testing.T) { 94 p := properties{} 95 96 assert.Nil(t, p.Prop("test")) 97 } 98 99 func TestIsValidDefault(t *testing.T) { 100 tests := []struct { 101 name string 102 schemaFn func() Schema 103 def any 104 want any 105 wantOk bool 106 }{ 107 108 { 109 name: "Null", 110 schemaFn: func() Schema { 111 return &NullSchema{} 112 }, 113 def: nil, 114 want: nullDefault, 115 wantOk: true, 116 }, 117 { 118 name: "Null Invalid Type", 119 schemaFn: func() Schema { 120 return &NullSchema{} 121 }, 122 def: "test", 123 wantOk: false, 124 }, 125 { 126 name: "String", 127 schemaFn: func() Schema { 128 return NewPrimitiveSchema(String, nil) 129 }, 130 def: "test", 131 want: "test", 132 wantOk: true, 133 }, 134 { 135 name: "String Invalid Type", 136 schemaFn: func() Schema { 137 return NewPrimitiveSchema(String, nil) 138 }, 139 def: 1, 140 wantOk: false, 141 }, 142 { 143 name: "Bytes", 144 schemaFn: func() Schema { 145 return NewPrimitiveSchema(Bytes, nil) 146 }, 147 def: "test", 148 want: []byte("test"), 149 wantOk: true, 150 }, 151 { 152 name: "Bytes Invalid Type", 153 schemaFn: func() Schema { 154 return NewPrimitiveSchema(Bytes, nil) 155 }, 156 def: 1, 157 wantOk: false, 158 }, 159 { 160 name: "Enum", 161 schemaFn: func() Schema { 162 s, _ := NewEnumSchema("foo", "", []string{"BAR"}) 163 return s 164 }, 165 def: "BAR", 166 want: "BAR", 167 wantOk: true, 168 }, 169 { 170 name: "Enum Invalid Default", 171 schemaFn: func() Schema { 172 s, _ := NewEnumSchema("foo", "", []string{"BAR"}) 173 return s 174 }, 175 def: "BUP", 176 wantOk: false, 177 }, 178 { 179 name: "Enum Empty string", 180 schemaFn: func() Schema { 181 s, _ := NewEnumSchema("foo", "", []string{"BAR"}) 182 return s 183 }, 184 def: "", 185 wantOk: false, 186 }, 187 { 188 name: "Enum Invalid Type", 189 schemaFn: func() Schema { 190 s, _ := NewEnumSchema("foo", "", []string{"BAR"}) 191 return s 192 }, 193 def: 1, 194 wantOk: false, 195 }, 196 { 197 name: "Fixed", 198 schemaFn: func() Schema { 199 s, _ := NewFixedSchema("foo", "", 4, nil) 200 return s 201 }, 202 def: "test", 203 want: [4]byte{'t', 'e', 's', 't'}, 204 wantOk: true, 205 }, 206 { 207 name: "Fixed Invalid Type", 208 schemaFn: func() Schema { 209 s, _ := NewFixedSchema("foo", "", 1, nil) 210 return s 211 }, 212 def: 1, 213 wantOk: false, 214 }, 215 { 216 name: "Boolean", 217 schemaFn: func() Schema { 218 return NewPrimitiveSchema(Boolean, nil) 219 }, 220 def: true, 221 want: true, 222 wantOk: true, 223 }, 224 { 225 name: "Boolean Invalid Type", 226 schemaFn: func() Schema { 227 return NewPrimitiveSchema(Boolean, nil) 228 }, 229 def: 1, 230 wantOk: false, 231 }, 232 { 233 name: "Int", 234 schemaFn: func() Schema { 235 return NewPrimitiveSchema(Int, nil) 236 }, 237 def: 1, 238 want: 1, 239 wantOk: true, 240 }, 241 { 242 name: "Int Int8", 243 schemaFn: func() Schema { 244 return NewPrimitiveSchema(Int, nil) 245 }, 246 def: int8(1), 247 want: 1, 248 wantOk: true, 249 }, 250 { 251 name: "Int Int16", 252 schemaFn: func() Schema { 253 return NewPrimitiveSchema(Int, nil) 254 }, 255 def: int16(1), 256 want: 1, 257 wantOk: true, 258 }, 259 { 260 name: "Int Int32", 261 schemaFn: func() Schema { 262 return NewPrimitiveSchema(Int, nil) 263 }, 264 def: int32(1), 265 want: 1, 266 wantOk: true, 267 }, 268 { 269 name: "Int Float64", 270 schemaFn: func() Schema { 271 return NewPrimitiveSchema(Int, nil) 272 }, 273 def: float64(1), 274 want: 1, 275 wantOk: true, 276 }, 277 { 278 name: "Int Invalid Type", 279 schemaFn: func() Schema { 280 return NewPrimitiveSchema(Int, nil) 281 }, 282 def: "test", 283 wantOk: false, 284 }, 285 { 286 name: "Long", 287 schemaFn: func() Schema { 288 return NewPrimitiveSchema(Long, nil) 289 }, 290 def: int64(1), 291 want: int64(1), 292 wantOk: true, 293 }, 294 { 295 name: "Long Float64", 296 schemaFn: func() Schema { 297 return NewPrimitiveSchema(Long, nil) 298 }, 299 def: float64(1), 300 want: int64(1), 301 wantOk: true, 302 }, 303 { 304 name: "Long Invalid Type", 305 schemaFn: func() Schema { 306 return NewPrimitiveSchema(Long, nil) 307 }, 308 def: "test", 309 wantOk: false, 310 }, 311 { 312 name: "Float", 313 schemaFn: func() Schema { 314 return NewPrimitiveSchema(Float, nil) 315 }, 316 def: float32(1), 317 want: float32(1), 318 wantOk: true, 319 }, 320 { 321 name: "Float Float64", 322 schemaFn: func() Schema { 323 return NewPrimitiveSchema(Float, nil) 324 }, 325 def: float64(1), 326 want: float32(1), 327 wantOk: true, 328 }, 329 { 330 name: "Float Invalid Type", 331 schemaFn: func() Schema { 332 return NewPrimitiveSchema(Float, nil) 333 }, 334 def: "test", 335 wantOk: false, 336 }, 337 { 338 name: "Double", 339 schemaFn: func() Schema { 340 return NewPrimitiveSchema(Double, nil) 341 }, 342 def: float64(1), 343 want: float64(1), 344 wantOk: true, 345 }, 346 { 347 name: "Double Invalid Type", 348 schemaFn: func() Schema { 349 return NewPrimitiveSchema(Double, nil) 350 }, 351 def: "test", 352 wantOk: false, 353 }, 354 } 355 356 for _, test := range tests { 357 test := test 358 t.Run(test.name, func(t *testing.T) { 359 t.Parallel() 360 361 got, ok := isValidDefault(test.schemaFn(), test.def) 362 363 assert.Equal(t, test.wantOk, ok) 364 if ok { 365 assert.Equal(t, test.want, got) 366 } 367 }) 368 } 369 } 370 371 func TestRecursionError_Error(t *testing.T) { 372 err := recursionError{} 373 374 assert.Equal(t, "", err.Error()) 375 } 376 377 func TestSchema_FingerprintUsingCaches(t *testing.T) { 378 schema := NewPrimitiveSchema(String, nil) 379 380 want, _ := schema.FingerprintUsing(CRC64Avro) 381 382 got, _ := schema.FingerprintUsing(CRC64Avro) 383 384 value, ok := schema.fingerprinter.cache.Load(CRC64Avro) 385 require.True(t, ok) 386 assert.Equal(t, want, value) 387 assert.Equal(t, want, got) 388 } 389 390 func TestSchema_IsPromotable(t *testing.T) { 391 tests := []struct { 392 writerTyp Type 393 readerType Type 394 want bool 395 }{ 396 { 397 writerTyp: Int, 398 readerType: Long, 399 want: true, 400 }, 401 { 402 writerTyp: Int, 403 readerType: Float, 404 want: true, 405 }, 406 { 407 writerTyp: Int, 408 readerType: Double, 409 want: true, 410 }, 411 { 412 writerTyp: Long, 413 readerType: Float, 414 want: true, 415 }, 416 { 417 writerTyp: Long, 418 readerType: Double, 419 want: true, 420 }, 421 { 422 writerTyp: Float, 423 readerType: Double, 424 want: true, 425 }, 426 { 427 writerTyp: String, 428 readerType: Bytes, 429 want: true, 430 }, 431 { 432 writerTyp: Bytes, 433 readerType: String, 434 want: true, 435 }, 436 { 437 writerTyp: Double, 438 readerType: Int, 439 want: false, 440 }, 441 { 442 writerTyp: Boolean, 443 readerType: Int, 444 want: false, 445 }, 446 { 447 writerTyp: Null, 448 readerType: Null, 449 want: false, 450 }, 451 } 452 453 for i, test := range tests { 454 test := test 455 t.Run(strconv.Itoa(i), func(t *testing.T) { 456 t.Parallel() 457 458 ok := isPromotable(test.writerTyp, test.readerType) 459 460 assert.Equal(t, test.want, ok) 461 }) 462 } 463 } 464 465 func TestSchema_IsNative(t *testing.T) { 466 tests := []struct { 467 typ Type 468 wantOk bool 469 }{ 470 { 471 typ: Null, 472 wantOk: true, 473 }, 474 { 475 typ: Boolean, 476 wantOk: true, 477 }, 478 { 479 typ: Int, 480 wantOk: true, 481 }, 482 { 483 typ: Long, 484 wantOk: true, 485 }, 486 487 { 488 typ: Float, 489 wantOk: true, 490 }, 491 { 492 typ: Double, 493 wantOk: true, 494 }, 495 496 { 497 typ: Bytes, 498 wantOk: true, 499 }, 500 { 501 typ: String, 502 wantOk: true, 503 }, 504 { 505 typ: Record, 506 wantOk: false, 507 }, 508 { 509 typ: Array, 510 wantOk: false, 511 }, 512 { 513 typ: Map, 514 wantOk: false, 515 }, 516 { 517 typ: Fixed, 518 wantOk: false, 519 }, 520 { 521 typ: Enum, 522 wantOk: false, 523 }, 524 { 525 typ: Union, 526 wantOk: false, 527 }, 528 } 529 530 for i, test := range tests { 531 test := test 532 t.Run(strconv.Itoa(i), func(t *testing.T) { 533 t.Parallel() 534 535 ok := isNative(test.typ) 536 assert.Equal(t, test.wantOk, ok) 537 }) 538 } 539 } 540 541 func TestSchema_FieldEncodeDefault(t *testing.T) { 542 schema := MustParse(`{ 543 "type": "record", 544 "name": "test", 545 "fields" : [ 546 {"name": "a", "type": "string", "default": "bar"}, 547 {"name": "b", "type": "boolean"} 548 ] 549 }`).(*RecordSchema) 550 551 fooEncoder := func(a any) ([]byte, error) { 552 return []byte("foo"), nil 553 } 554 barEncoder := func(a any) ([]byte, error) { 555 return []byte("bar"), nil 556 } 557 558 assert.Equal(t, nil, schema.fields[0].encodedDef.Load()) 559 560 _, err := schema.fields[0].encodeDefault(nil) 561 assert.Error(t, err) 562 563 _, err = schema.fields[1].encodeDefault(fooEncoder) 564 assert.Error(t, err) 565 566 def, err := schema.fields[0].encodeDefault(fooEncoder) 567 assert.NoError(t, err) 568 assert.Equal(t, []byte("foo"), def) 569 570 def, err = schema.fields[0].encodeDefault(barEncoder) 571 assert.NoError(t, err) 572 assert.Equal(t, []byte("foo"), def) 573 } 574 575 func TestEnumSchema_GetSymbol(t *testing.T) { 576 tests := []struct { 577 schemaFn func() *EnumSchema 578 idx int 579 want any 580 wantOk bool 581 }{ 582 { 583 schemaFn: func() *EnumSchema { 584 enum, _ := NewEnumSchema("foo", "", []string{"BAR"}) 585 return enum 586 }, 587 idx: 0, 588 wantOk: true, 589 want: "BAR", 590 }, 591 { 592 schemaFn: func() *EnumSchema { 593 enum, _ := NewEnumSchema("foo", "", []string{"BAR"}) 594 return enum 595 }, 596 idx: 1, 597 wantOk: false, 598 }, 599 { 600 schemaFn: func() *EnumSchema { 601 enum, _ := NewEnumSchema("foo", "", []string{"FOO"}, WithDefault("FOO")) 602 return enum 603 }, 604 idx: 1, 605 wantOk: false, 606 }, 607 { 608 schemaFn: func() *EnumSchema { 609 enum, _ := NewEnumSchema("foo", "", []string{"FOO"}) 610 enum.encodedSymbols = []string{"FOO", "BAR"} 611 return enum 612 }, 613 idx: 1, 614 wantOk: false, 615 }, 616 { 617 schemaFn: func() *EnumSchema { 618 enum, _ := NewEnumSchema("foo", "", []string{"FOO"}, WithDefault("FOO")) 619 enum.encodedSymbols = []string{"FOO", "BAR"} 620 return enum 621 }, 622 idx: 1, 623 wantOk: true, 624 want: "FOO", 625 }, 626 { 627 schemaFn: func() *EnumSchema { 628 enum, _ := NewEnumSchema("foo", "", []string{"FOO", "BAR"}) 629 enum.encodedSymbols = []string{"FOO"} 630 return enum 631 }, 632 idx: 0, 633 wantOk: true, 634 want: "FOO", 635 }, 636 } 637 638 for i, test := range tests { 639 test := test 640 t.Run(strconv.Itoa(i), func(t *testing.T) { 641 t.Parallel() 642 643 got, ok := test.schemaFn().Symbol(test.idx) 644 assert.Equal(t, test.wantOk, ok) 645 if ok { 646 assert.Equal(t, test.want, got) 647 } 648 }) 649 } 650 }