github.com/hamba/avro/v2@v2.22.1-0.20240518180522-aff3955acf7d/encoder_union_test.go (about) 1 package avro_test 2 3 import ( 4 "bytes" 5 "math/big" 6 "testing" 7 "time" 8 9 "github.com/hamba/avro/v2" 10 "github.com/stretchr/testify/assert" 11 "github.com/stretchr/testify/require" 12 ) 13 14 func TestEncoder_UnionMap(t *testing.T) { 15 defer ConfigTeardown() 16 17 schema := `["null", "string"]` 18 buf := bytes.NewBuffer([]byte{}) 19 enc, err := avro.NewEncoder(schema, buf) 20 require.NoError(t, err) 21 22 err = enc.Encode(map[string]any{"string": "foo"}) 23 24 require.NoError(t, err) 25 assert.Equal(t, []byte{0x02, 0x06, 0x66, 0x6F, 0x6F}, buf.Bytes()) 26 } 27 28 func TestEncoder_UnionMapRecord(t *testing.T) { 29 defer ConfigTeardown() 30 31 schema := `["null", { 32 "type": "record", 33 "name": "test", 34 "fields" : [ 35 {"name": "a", "type": ["string", "null"], "default": "test"}, 36 {"name": "b", "type": "string"} 37 ] 38 }]` 39 buf := bytes.NewBuffer([]byte{}) 40 enc, err := avro.NewEncoder(schema, buf) 41 require.NoError(t, err) 42 43 err = enc.Encode(map[string]any{"test": map[string]any{"b": "foo"}}) 44 45 require.NoError(t, err) 46 assert.Equal(t, []byte{0x02, 0x00, 0x08, 0x74, 0x65, 0x73, 0x74, 0x06, 0x66, 0x6F, 0x6F}, buf.Bytes()) 47 } 48 49 func TestEncoder_UnionMapNamed(t *testing.T) { 50 defer ConfigTeardown() 51 52 schema := `["null", {"type":"enum", "name": "test", "symbols": ["foo", "bar"]}]` 53 buf := bytes.NewBuffer([]byte{}) 54 enc, err := avro.NewEncoder(schema, buf) 55 require.NoError(t, err) 56 57 err = enc.Encode(map[string]any{"test": "bar"}) 58 59 require.NoError(t, err) 60 assert.Equal(t, []byte{0x02, 0x02}, buf.Bytes()) 61 } 62 63 func TestEncoder_UnionMapNull(t *testing.T) { 64 defer ConfigTeardown() 65 66 schema := `["null", "string"]` 67 buf := bytes.NewBuffer([]byte{}) 68 enc, err := avro.NewEncoder(schema, buf) 69 require.NoError(t, err) 70 71 var m map[string]any 72 err = enc.Encode(m) 73 74 require.NoError(t, err) 75 assert.Equal(t, []byte{0x00}, buf.Bytes()) 76 } 77 78 func TestEncoder_UnionMapMultipleEntries(t *testing.T) { 79 defer ConfigTeardown() 80 81 schema := `["null", "string", "int"]` 82 buf := bytes.NewBuffer([]byte{}) 83 enc, err := avro.NewEncoder(schema, buf) 84 require.NoError(t, err) 85 86 err = enc.Encode(map[string]any{"string": "foo", "int": 27}) 87 88 assert.Error(t, err) 89 } 90 91 func TestEncoder_UnionMapWithTime(t *testing.T) { 92 defer ConfigTeardown() 93 94 schema := `["null", {"type": "long", "logicalType": "timestamp-micros"}]` 95 buf := bytes.NewBuffer([]byte{}) 96 enc, err := avro.NewEncoder(schema, buf) 97 require.NoError(t, err) 98 99 m := map[string]any{ 100 "long.timestamp-micros": time.Date(2020, 1, 2, 3, 4, 5, 0, time.UTC), 101 } 102 err = enc.Encode(m) 103 104 require.NoError(t, err) 105 assert.Equal(t, []byte{0x02, 0x80, 0xCD, 0xB7, 0xA2, 0xEE, 0xC7, 0xCD, 0x05}, buf.Bytes()) 106 } 107 108 func TestEncoder_UnionMapWithDuration(t *testing.T) { 109 defer ConfigTeardown() 110 111 schema := `["null", {"type": "int", "logicalType": "time-millis"}]` 112 buf := bytes.NewBuffer([]byte{}) 113 enc, err := avro.NewEncoder(schema, buf) 114 require.NoError(t, err) 115 116 m := map[string]any{ 117 "int.time-millis": 123456789 * time.Millisecond, 118 } 119 err = enc.Encode(m) 120 121 require.NoError(t, err) 122 assert.Equal(t, []byte{0x02, 0xAA, 0xB4, 0xDE, 0x75}, buf.Bytes()) 123 } 124 125 func TestEncoder_UnionMapWithDecimal(t *testing.T) { 126 defer ConfigTeardown() 127 128 t.Run("low scale", func(t *testing.T) { 129 schema := `["null", {"type": "bytes", "logicalType": "decimal", "precision": 4, "scale": 2}]` 130 buf := bytes.NewBuffer([]byte{}) 131 enc, err := avro.NewEncoder(schema, buf) 132 require.NoError(t, err) 133 134 m := map[string]any{ 135 "bytes.decimal": big.NewRat(1734, 5), 136 } 137 err = enc.Encode(m) 138 139 require.NoError(t, err) 140 assert.Equal(t, []byte{0x02, 0x6, 0x00, 0x87, 0x78}, buf.Bytes()) 141 }) 142 143 t.Run("high scale", func(t *testing.T) { 144 schema := `["null", {"type": "bytes", "logicalType": "decimal", "precision": 77, "scale": 38}]` 145 buf := bytes.NewBuffer([]byte{}) 146 enc, err := avro.NewEncoder(schema, buf) 147 require.NoError(t, err) 148 149 m := map[string]any{ 150 "bytes.decimal": big.NewRat(1734, 5), 151 } 152 err = enc.Encode(m) 153 154 require.NoError(t, err) 155 assert.Equal(t, []byte{0x2, 0x22, 0x65, 0xea, 0x55, 0xc, 0x11, 0x8, 0xf7, 0xc3, 0xb8, 0xec, 0x53, 0xff, 0x80, 0x0, 0x0, 0x0, 0x0}, buf.Bytes()) 156 }) 157 } 158 159 func TestEncoder_UnionMapInvalidType(t *testing.T) { 160 defer ConfigTeardown() 161 162 schema := `["null", "string"]` 163 buf := bytes.NewBuffer([]byte{}) 164 enc, err := avro.NewEncoder(schema, buf) 165 require.NoError(t, err) 166 167 err = enc.Encode(map[string]any{"long": 27}) 168 169 assert.Error(t, err) 170 } 171 172 func TestEncoder_UnionMapInvalidMap(t *testing.T) { 173 defer ConfigTeardown() 174 175 schema := `["null", "string"]` 176 buf := bytes.NewBuffer([]byte{}) 177 enc, err := avro.NewEncoder(schema, buf) 178 require.NoError(t, err) 179 180 err = enc.Encode(map[string]string{}) 181 182 assert.Error(t, err) 183 } 184 185 func TestEncoder_UnionPtr(t *testing.T) { 186 defer ConfigTeardown() 187 188 schema := `["null", "string"]` 189 buf := bytes.NewBuffer([]byte{}) 190 enc, err := avro.NewEncoder(schema, buf) 191 require.NoError(t, err) 192 193 str := "foo" 194 err = enc.Encode(&str) 195 196 require.NoError(t, err) 197 assert.Equal(t, []byte{0x02, 0x06, 0x66, 0x6F, 0x6F}, buf.Bytes()) 198 } 199 200 func TestEncoder_UnionPtrReversed(t *testing.T) { 201 defer ConfigTeardown() 202 203 schema := `["string", "null"]` 204 buf := bytes.NewBuffer([]byte{}) 205 enc, err := avro.NewEncoder(schema, buf) 206 require.NoError(t, err) 207 208 str := "foo" 209 err = enc.Encode(&str) 210 211 require.NoError(t, err) 212 assert.Equal(t, []byte{0x00, 0x06, 0x66, 0x6F, 0x6F}, buf.Bytes()) 213 } 214 215 func TestEncoder_UnionPtrNull(t *testing.T) { 216 defer ConfigTeardown() 217 218 schema := `["null", "string"]` 219 buf := bytes.NewBuffer([]byte{}) 220 enc, err := avro.NewEncoder(schema, buf) 221 require.NoError(t, err) 222 223 var str *string 224 err = enc.Encode(str) 225 226 require.NoError(t, err) 227 assert.Equal(t, []byte{0x00}, buf.Bytes()) 228 } 229 230 func TestEncoder_UnionPtrReversedNull(t *testing.T) { 231 defer ConfigTeardown() 232 233 schema := `["string", "null"]` 234 buf := bytes.NewBuffer([]byte{}) 235 enc, err := avro.NewEncoder(schema, buf) 236 require.NoError(t, err) 237 238 var str *string 239 err = enc.Encode(str) 240 241 require.NoError(t, err) 242 assert.Equal(t, []byte{0x02}, buf.Bytes()) 243 } 244 245 func TestEncoder_UnionPtrNotNullable(t *testing.T) { 246 defer ConfigTeardown() 247 248 schema := `["null", "string", "int"]` 249 buf := bytes.NewBuffer([]byte{}) 250 enc, err := avro.NewEncoder(schema, buf) 251 require.NoError(t, err) 252 253 str := "test" 254 err = enc.Encode(&str) 255 256 assert.Error(t, err) 257 } 258 259 func TestEncoder_UnionNullableSlice(t *testing.T) { 260 defer ConfigTeardown() 261 262 schema := `["null", "bytes"]` 263 buf := bytes.NewBuffer([]byte{}) 264 enc, err := avro.NewEncoder(schema, buf) 265 require.NoError(t, err) 266 267 b := []byte("foo") 268 err = enc.Encode(b) 269 270 require.NoError(t, err) 271 assert.Equal(t, []byte{0x02, 0x06, 0x66, 0x6F, 0x6F}, buf.Bytes()) 272 } 273 274 func TestEncoder_UnionNullableSliceNull(t *testing.T) { 275 defer ConfigTeardown() 276 277 schema := `["null", "bytes"]` 278 buf := bytes.NewBuffer([]byte{}) 279 enc, err := avro.NewEncoder(schema, buf) 280 require.NoError(t, err) 281 282 var b []byte 283 err = enc.Encode(b) 284 285 require.NoError(t, err) 286 assert.Equal(t, []byte{0x00}, buf.Bytes()) 287 } 288 289 func TestEncoder_UnionInterface(t *testing.T) { 290 defer ConfigTeardown() 291 292 schema := `["int", "string"]` 293 buf := bytes.NewBuffer([]byte{}) 294 enc, err := avro.NewEncoder(schema, buf) 295 require.NoError(t, err) 296 297 var val any = "foo" 298 err = enc.Encode(val) 299 300 require.NoError(t, err) 301 assert.Equal(t, []byte{0x02, 0x06, 0x66, 0x6F, 0x6F}, buf.Bytes()) 302 } 303 304 func TestEncoder_UnionInterfaceRecord(t *testing.T) { 305 defer ConfigTeardown() 306 307 avro.Register("test", &TestRecord{}) 308 309 schema := `["int", {"type": "record", "name": "test", "fields" : [{"name": "a", "type": "long"}, {"name": "b", "type": "string"}]}]` 310 buf := bytes.NewBuffer([]byte{}) 311 enc, err := avro.NewEncoder(schema, buf) 312 require.NoError(t, err) 313 314 var val any = &TestRecord{A: 27, B: "foo"} 315 err = enc.Encode(val) 316 317 require.NoError(t, err) 318 assert.Equal(t, []byte{0x02, 0x36, 0x06, 0x66, 0x6F, 0x6F}, buf.Bytes()) 319 } 320 321 func TestEncoder_UnionInterfaceRecordNonPtr(t *testing.T) { 322 defer ConfigTeardown() 323 324 avro.Register("test", TestRecord{}) 325 326 schema := `["int", {"type": "record", "name": "test", "fields" : [{"name": "a", "type": "long"}, {"name": "b", "type": "string"}]}]` 327 buf := bytes.NewBuffer([]byte{}) 328 enc, err := avro.NewEncoder(schema, buf) 329 require.NoError(t, err) 330 331 var val any = TestRecord{A: 27, B: "foo"} 332 err = enc.Encode(val) 333 334 require.NoError(t, err) 335 assert.Equal(t, []byte{0x02, 0x36, 0x06, 0x66, 0x6F, 0x6F}, buf.Bytes()) 336 } 337 338 func TestEncoder_UnionInterfaceMap(t *testing.T) { 339 defer ConfigTeardown() 340 341 avro.Register("map:int", map[string]int{}) 342 343 schema := `["int", {"type": "map", "values": "int"}]` 344 buf := bytes.NewBuffer([]byte{}) 345 enc, err := avro.NewEncoder(schema, buf) 346 require.NoError(t, err) 347 348 var val any = map[string]int{"foo": 27} 349 err = enc.Encode(val) 350 351 require.NoError(t, err) 352 assert.Equal(t, []byte{0x02, 0x01, 0x0a, 0x06, 0x66, 0x6f, 0x6f, 0x36, 0x00}, buf.Bytes()) 353 } 354 355 func TestEncoder_UnionInterfaceInMapWithBool(t *testing.T) { 356 defer ConfigTeardown() 357 358 schema := `{"type":"map", "values": ["null", "boolean"]}` 359 buf := bytes.NewBuffer([]byte{}) 360 enc, err := avro.NewEncoder(schema, buf) 361 require.NoError(t, err) 362 363 err = enc.Encode(map[string]any{"foo": true}) 364 365 require.NoError(t, err) 366 assert.Equal(t, []byte{0x01, 0x0c, 0x06, 0x66, 0x6F, 0x6F, 0x02, 0x01, 0x00}, buf.Bytes()) 367 } 368 369 func TestEncoder_UnionInterfaceArray(t *testing.T) { 370 defer ConfigTeardown() 371 372 avro.Register("array:int", []int{}) 373 374 schema := `["int", {"type": "array", "items": "int"}]` 375 buf := bytes.NewBuffer([]byte{}) 376 enc, err := avro.NewEncoder(schema, buf) 377 require.NoError(t, err) 378 379 var val any = []int{27} 380 err = enc.Encode(val) 381 382 require.NoError(t, err) 383 assert.Equal(t, []byte{0x02, 0x01, 0x02, 0x36, 0x00}, buf.Bytes()) 384 } 385 386 func TestEncoder_UnionInterfaceNull(t *testing.T) { 387 defer ConfigTeardown() 388 389 schema := `{"type": "record", "name": "test", "fields" : [{"name": "a", "type": ["null", "string", "int"]}]}` 390 buf := bytes.NewBuffer([]byte{}) 391 enc, err := avro.NewEncoder(schema, buf) 392 require.NoError(t, err) 393 394 err = enc.Encode(&TestUnion{A: nil}) 395 396 require.NoError(t, err) 397 assert.Equal(t, []byte{0x00}, buf.Bytes()) 398 } 399 400 func TestEncoder_UnionInterfaceNamed(t *testing.T) { 401 defer ConfigTeardown() 402 403 avro.Register("test", "") 404 405 schema := `["null", {"type":"enum", "name": "test", "symbols": ["A", "B"]}]` 406 buf := bytes.NewBuffer([]byte{}) 407 enc, err := avro.NewEncoder(schema, buf) 408 require.NoError(t, err) 409 410 var val any = "B" 411 err = enc.Encode(val) 412 413 require.NoError(t, err) 414 assert.Equal(t, []byte{0x02, 0x02}, buf.Bytes()) 415 } 416 417 func TestEncoder_UnionInterfaceWithTime(t *testing.T) { 418 defer ConfigTeardown() 419 420 schema := `["null", {"type": "long", "logicalType": "timestamp-micros"}]` 421 buf := bytes.NewBuffer([]byte{}) 422 enc, err := avro.NewEncoder(schema, buf) 423 require.NoError(t, err) 424 425 var val any = time.Date(2020, 1, 2, 3, 4, 5, 0, time.UTC) 426 err = enc.Encode(val) 427 428 require.NoError(t, err) 429 assert.Equal(t, []byte{0x02, 0x80, 0xCD, 0xB7, 0xA2, 0xEE, 0xC7, 0xCD, 0x05}, buf.Bytes()) 430 } 431 432 func TestEncoder_UnionInterfaceWithDuration(t *testing.T) { 433 defer ConfigTeardown() 434 435 schema := `["null", {"type": "int", "logicalType": "time-millis"}]` 436 buf := bytes.NewBuffer([]byte{}) 437 enc, err := avro.NewEncoder(schema, buf) 438 require.NoError(t, err) 439 440 var val any = 123456789 * time.Millisecond 441 err = enc.Encode(val) 442 443 require.NoError(t, err) 444 assert.Equal(t, []byte{0x02, 0xAA, 0xB4, 0xDE, 0x75}, buf.Bytes()) 445 } 446 447 func TestEncoder_UnionInterfaceWithDecimal(t *testing.T) { 448 defer ConfigTeardown() 449 450 t.Run("low scale", func(t *testing.T) { 451 schema := `["null", {"type": "bytes", "logicalType": "decimal", "precision": 4, "scale": 2}]` 452 buf := bytes.NewBuffer([]byte{}) 453 enc, err := avro.NewEncoder(schema, buf) 454 require.NoError(t, err) 455 456 var val any = big.NewRat(1734, 5) 457 err = enc.Encode(val) 458 459 require.NoError(t, err) 460 assert.Equal(t, []byte{0x02, 0x6, 0x00, 0x87, 0x78}, buf.Bytes()) 461 }) 462 463 t.Run("high scale", func(t *testing.T) { 464 schema := `["null", {"type": "bytes", "logicalType": "decimal", "precision": 77, "scale": 38}]` 465 buf := bytes.NewBuffer([]byte{}) 466 enc, err := avro.NewEncoder(schema, buf) 467 require.NoError(t, err) 468 469 var val any = big.NewRat(1734, 5) 470 err = enc.Encode(val) 471 472 require.NoError(t, err) 473 assert.Equal(t, []byte{0x2, 0x22, 0x65, 0xea, 0x55, 0xc, 0x11, 0x8, 0xf7, 0xc3, 0xb8, 0xec, 0x53, 0xff, 0x80, 0x0, 0x0, 0x0, 0x0}, buf.Bytes()) 474 }) 475 } 476 477 func TestEncoder_UnionInterfaceWithUUID(t *testing.T) { 478 defer ConfigTeardown() 479 480 schema := `["null", {"type": "string", "logicalType": "uuid"}]` 481 buf := bytes.NewBuffer([]byte{}) 482 enc, err := avro.NewEncoder(schema, buf) 483 require.NoError(t, err) 484 485 var val any = "f36e589a-3a52-492b-b95c-dad345e8d2ac" 486 err = enc.Encode(val) 487 488 require.NoError(t, err) 489 assert.Equal(t, []byte{0x2, 0x48, 0x66, 0x33, 0x36, 0x65, 0x35, 0x38, 0x39, 0x61, 0x2d, 0x33, 0x61, 0x35, 0x32, 0x2d, 0x34, 0x39, 0x32, 0x62, 0x2d, 0x62, 0x39, 0x35, 0x63, 0x2d, 0x64, 0x61, 0x64, 0x33, 0x34, 0x35, 0x65, 0x38, 0x64, 0x32, 0x61, 0x63}, buf.Bytes()) 490 } 491 492 func TestEncoder_UnionInterfaceUnregisteredType(t *testing.T) { 493 defer ConfigTeardown() 494 495 schema := `["int", {"type": "record", "name": "test", "fields" : [{"name": "a", "type": "long"}, {"name": "b", "type": "string"}]}]` 496 buf := bytes.NewBuffer([]byte{}) 497 enc, err := avro.NewEncoder(schema, buf) 498 require.NoError(t, err) 499 500 var val any = &TestRecord{} 501 err = enc.Encode(val) 502 503 assert.Error(t, err) 504 } 505 506 func TestEncoder_UnionInterfaceNotInSchema(t *testing.T) { 507 defer ConfigTeardown() 508 509 avro.Register("test", &TestRecord{}) 510 511 schema := `["int", "string"]` 512 buf := bytes.NewBuffer([]byte{}) 513 enc, err := avro.NewEncoder(schema, buf) 514 require.NoError(t, err) 515 516 var val any = &TestRecord{} 517 err = enc.Encode(val) 518 519 assert.Error(t, err) 520 }