github.com/hamba/avro/v2@v2.22.1-0.20240518180522-aff3955acf7d/codec_generic_internal_test.go (about)

     1  package avro
     2  
     3  import (
     4  	"bytes"
     5  	"math/big"
     6  	"testing"
     7  	"time"
     8  
     9  	"github.com/stretchr/testify/assert"
    10  	"github.com/stretchr/testify/require"
    11  )
    12  
    13  func TestGenericDecode(t *testing.T) {
    14  	tests := []struct {
    15  		name    string
    16  		data    []byte
    17  		schema  string
    18  		want    any
    19  		wantErr require.ErrorAssertionFunc
    20  	}{
    21  
    22  		{
    23  			name:    "Bool",
    24  			data:    []byte{0x01},
    25  			schema:  "boolean",
    26  			want:    true,
    27  			wantErr: require.NoError,
    28  		},
    29  		{
    30  			name:    "Int",
    31  			data:    []byte{0x36},
    32  			schema:  "int",
    33  			want:    27,
    34  			wantErr: require.NoError,
    35  		},
    36  		{
    37  			name:    "Int Date",
    38  			data:    []byte{0xAE, 0x9D, 0x02},
    39  			schema:  `{"type":"int","logicalType":"date"}`,
    40  			want:    time.Date(2020, 1, 2, 0, 0, 0, 0, time.UTC),
    41  			wantErr: require.NoError,
    42  		},
    43  		{
    44  			name:    "Int Time-Millis",
    45  			data:    []byte{0xAA, 0xB4, 0xDE, 0x75},
    46  			schema:  `{"type":"int","logicalType":"time-millis"}`,
    47  			want:    123456789 * time.Millisecond,
    48  			wantErr: require.NoError,
    49  		},
    50  		{
    51  			name:    "Long",
    52  			data:    []byte{0x36},
    53  			schema:  "long",
    54  			want:    int64(27),
    55  			wantErr: require.NoError,
    56  		},
    57  		{
    58  			name:    "Long Time-Micros",
    59  			data:    []byte{0x86, 0xEA, 0xC8, 0xE9, 0x97, 0x07},
    60  			schema:  `{"type":"long","logicalType":"time-micros"}`,
    61  			want:    123456789123 * time.Microsecond,
    62  			wantErr: require.NoError,
    63  		},
    64  		{
    65  			name:    "Long Timestamp-Millis",
    66  			data:    []byte{0x90, 0xB2, 0xAE, 0xC3, 0xEC, 0x5B},
    67  			schema:  `{"type":"long","logicalType":"timestamp-millis"}`,
    68  			want:    time.Date(2020, 1, 2, 3, 4, 5, 0, time.UTC),
    69  			wantErr: require.NoError,
    70  		},
    71  		{
    72  			name:    "Long Timestamp-Micros",
    73  			data:    []byte{0x80, 0xCD, 0xB7, 0xA2, 0xEE, 0xC7, 0xCD, 0x05},
    74  			schema:  `{"type":"long","logicalType":"timestamp-micros"}`,
    75  			want:    time.Date(2020, 1, 2, 3, 4, 5, 0, time.UTC),
    76  			wantErr: require.NoError,
    77  		},
    78  		{
    79  			name:    "Long Local-Timestamp-Millis",
    80  			data:    []byte{0x90, 0xB2, 0xAE, 0xC3, 0xEC, 0x5B},
    81  			schema:  `{"type":"long","logicalType":"local-timestamp-millis"}`,
    82  			want:    time.Date(2020, 1, 2, 3, 4, 5, 0, time.Local),
    83  			wantErr: require.NoError,
    84  		},
    85  		{
    86  			name:    "Long Local-Timestamp-Micros",
    87  			data:    []byte{0x80, 0xCD, 0xB7, 0xA2, 0xEE, 0xC7, 0xCD, 0x05},
    88  			schema:  `{"type":"long","logicalType":"local-timestamp-micros"}`,
    89  			want:    time.Date(2020, 1, 2, 3, 4, 5, 0, time.Local),
    90  			wantErr: require.NoError,
    91  		},
    92  		{
    93  			name:    "Float",
    94  			data:    []byte{0x33, 0x33, 0x93, 0x3F},
    95  			schema:  "float",
    96  			want:    float32(1.15),
    97  			wantErr: require.NoError,
    98  		},
    99  		{
   100  			name:    "Double",
   101  			data:    []byte{0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0xF2, 0x3F},
   102  			schema:  "double",
   103  			want:    float64(1.15),
   104  			wantErr: require.NoError,
   105  		},
   106  		{
   107  			name:    "String",
   108  			data:    []byte{0x06, 0x66, 0x6F, 0x6F},
   109  			schema:  "string",
   110  			want:    "foo",
   111  			wantErr: require.NoError,
   112  		},
   113  		{
   114  			name:    "Bytes",
   115  			data:    []byte{0x08, 0xEC, 0xAB, 0x44, 0x00},
   116  			schema:  "bytes",
   117  			want:    []byte{0xEC, 0xAB, 0x44, 0x00},
   118  			wantErr: require.NoError,
   119  		},
   120  		{
   121  			name:    "Bytes Decimal",
   122  			data:    []byte{0x6, 0x00, 0x87, 0x78},
   123  			schema:  `{"type":"bytes","logicalType":"decimal","precision":4,"scale":2}`,
   124  			want:    big.NewRat(1734, 5),
   125  			wantErr: require.NoError,
   126  		},
   127  		{
   128  			name:    "Record",
   129  			data:    []byte{0x36, 0x06, 0x66, 0x6f, 0x6f},
   130  			schema:  `{"type": "record", "name": "test", "fields" : [{"name": "a", "type": "long"}, {"name": "b", "type": "string"}]}`,
   131  			want:    map[string]any{"a": int64(27), "b": "foo"},
   132  			wantErr: require.NoError,
   133  		},
   134  		{
   135  			name:    "Ref",
   136  			data:    []byte{0x36, 0x06, 0x66, 0x6f, 0x6f, 0x36, 0x06, 0x66, 0x6f, 0x6f},
   137  			schema:  `{"type":"record","name":"parent","fields":[{"name":"a","type":{"type":"record","name":"test","fields":[{"name":"a","type":"long"},{"name":"b","type":"string"}]}},{"name":"b","type":"test"}]}`,
   138  			want:    map[string]any{"a": map[string]any{"a": int64(27), "b": "foo"}, "b": map[string]any{"a": int64(27), "b": "foo"}},
   139  			wantErr: require.NoError,
   140  		},
   141  		{
   142  			name:    "Array",
   143  			data:    []byte{0x04, 0x36, 0x38, 0x0},
   144  			schema:  `{"type":"array", "items": "int"}`,
   145  			want:    []any{27, 28},
   146  			wantErr: require.NoError,
   147  		},
   148  		{
   149  			name:    "Map",
   150  			data:    []byte{0x02, 0x06, 0x66, 0x6F, 0x6F, 0x06, 0x66, 0x6F, 0x6F, 0x00},
   151  			schema:  `{"type":"map", "values": "string"}`,
   152  			want:    map[string]any{"foo": "foo"},
   153  			wantErr: require.NoError,
   154  		},
   155  		{
   156  			name:    "Enum",
   157  			data:    []byte{0x02},
   158  			schema:  `{"type":"enum", "name": "test", "symbols": ["foo", "bar"]}`,
   159  			want:    "bar",
   160  			wantErr: require.NoError,
   161  		},
   162  		{
   163  			name:    "Enum Invalid Symbol",
   164  			data:    []byte{0x04},
   165  			schema:  `{"type":"enum", "name": "test", "symbols": ["foo", "bar"]}`,
   166  			want:    nil,
   167  			wantErr: require.Error,
   168  		},
   169  		{
   170  			name:    "Union",
   171  			data:    []byte{0x02, 0x06, 0x66, 0x6F, 0x6F},
   172  			schema:  `["null", "string"]`,
   173  			want:    map[string]any{"string": "foo"},
   174  			wantErr: require.NoError,
   175  		},
   176  		{
   177  			name: "Union Zero Index",
   178  			// 0x80 represents 128. So the bytes below will result in 0
   179  			// as a result of zig-zag encoding.
   180  			data:    []byte{0x80, 0x80, 0x80, 0x80, 0x30},
   181  			schema:  `["null"]`,
   182  			wantErr: require.NoError,
   183  		},
   184  		{
   185  			name:    "Union Nil",
   186  			data:    []byte{0x00},
   187  			schema:  `["null", "string"]`,
   188  			want:    nil,
   189  			wantErr: require.NoError,
   190  		},
   191  		{
   192  			name:    "Union Named",
   193  			data:    []byte{0x02, 0x02},
   194  			schema:  `["null", {"type":"enum", "name": "test", "symbols": ["foo", "bar"]}]`,
   195  			want:    map[string]any{"test": "bar"},
   196  			wantErr: require.NoError,
   197  		},
   198  		{
   199  			name:    "Union Invalid Schema",
   200  			data:    []byte{0x04},
   201  			schema:  `["null", "string"]`,
   202  			want:    nil,
   203  			wantErr: require.Error,
   204  		},
   205  		{
   206  			name:    "Fixed",
   207  			data:    []byte{0x66, 0x6F, 0x6F, 0x66, 0x6F, 0x6F},
   208  			schema:  `{"type":"fixed", "name": "test", "size": 6}`,
   209  			want:    [6]byte{'f', 'o', 'o', 'f', 'o', 'o'},
   210  			wantErr: require.NoError,
   211  		},
   212  		{
   213  			name:    "Fixed Decimal",
   214  			data:    []byte{0x00, 0x00, 0x00, 0x00, 0x87, 0x78},
   215  			schema:  `{"type":"fixed", "name": "test", "size": 6,"logicalType":"decimal","precision":4,"scale":2}`,
   216  			want:    big.NewRat(1734, 5),
   217  			wantErr: require.NoError,
   218  		},
   219  	}
   220  
   221  	for _, test := range tests {
   222  		test := test
   223  		t.Run(test.name, func(t *testing.T) {
   224  			defer ConfigTeardown()
   225  
   226  			schema := MustParse(test.schema)
   227  			r := NewReader(bytes.NewReader(test.data), 10)
   228  
   229  			typ, err := genericReceiver(schema)
   230  			require.NoError(t, err)
   231  			dec := decoderOfType(DefaultConfig.(*frozenConfig), schema, typ)
   232  
   233  			got := genericDecode(typ, dec, r)
   234  
   235  			test.wantErr(t, r.Error)
   236  			assert.Equal(t, test.want, got)
   237  		})
   238  	}
   239  }
   240  
   241  func TestGenericReceiver_UnsupportedType(t *testing.T) {
   242  	schema := NewPrimitiveSchema(Type("test"), nil)
   243  
   244  	_, err := genericReceiver(schema)
   245  
   246  	assert.Error(t, err)
   247  }