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  }