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

     1  package avro_test
     2  
     3  import (
     4  	"encoding/json"
     5  	"testing"
     6  
     7  	"github.com/hamba/avro/v2"
     8  	"github.com/stretchr/testify/assert"
     9  	"github.com/stretchr/testify/require"
    10  )
    11  
    12  func TestParse_InvalidType(t *testing.T) {
    13  	schemas := []string{
    14  		`123`,
    15  		`{"type": 123}`,
    16  	}
    17  
    18  	for _, schm := range schemas {
    19  		_, err := avro.Parse(schm)
    20  
    21  		assert.Error(t, err)
    22  	}
    23  }
    24  
    25  func TestMustParse(t *testing.T) {
    26  	s := avro.MustParse("null")
    27  
    28  	assert.Equal(t, avro.Null, s.Type())
    29  }
    30  
    31  func TestMustParse_PanicsOnError(t *testing.T) {
    32  	assert.Panics(t, func() {
    33  		avro.MustParse("123")
    34  	})
    35  }
    36  
    37  func TestParseFiles(t *testing.T) {
    38  	s, err := avro.ParseFiles("testdata/schema.avsc")
    39  
    40  	require.NoError(t, err)
    41  	assert.Equal(t, avro.String, s.Type())
    42  }
    43  
    44  func TestParseFiles_FileDoesntExist(t *testing.T) {
    45  	_, err := avro.ParseFiles("test.something")
    46  
    47  	assert.Error(t, err)
    48  }
    49  
    50  func TestParseFiles_InvalidSchema(t *testing.T) {
    51  	_, err := avro.ParseFiles("testdata/bad-schema.avsc")
    52  
    53  	assert.Error(t, err)
    54  }
    55  
    56  func TestNullSchema(t *testing.T) {
    57  	schemas := []string{
    58  		`null`,
    59  		`{"type":"null"}`,
    60  	}
    61  
    62  	for _, schm := range schemas {
    63  		schema, err := avro.Parse(schm)
    64  
    65  		require.NoError(t, err)
    66  		assert.Equal(t, avro.Null, schema.Type())
    67  		want := [32]byte{0xf0, 0x72, 0xcb, 0xec, 0x3b, 0xf8, 0x84, 0x18, 0x71, 0xd4, 0x28, 0x42, 0x30, 0xc5, 0xe9, 0x83, 0xdc, 0x21, 0x1a, 0x56, 0x83, 0x7a, 0xed, 0x86, 0x24, 0x87, 0x14, 0x8f, 0x94, 0x7d, 0x1a, 0x1f}
    68  		assert.Equal(t, want, schema.Fingerprint())
    69  	}
    70  }
    71  
    72  func TestPrimitiveSchema(t *testing.T) {
    73  	tests := []struct {
    74  		schema          string
    75  		want            avro.Type
    76  		wantFingerprint [32]byte
    77  	}{
    78  		{
    79  			schema:          "string",
    80  			want:            avro.String,
    81  			wantFingerprint: [32]byte{0xe9, 0xe5, 0xc1, 0xc9, 0xe4, 0xf6, 0x27, 0x73, 0x39, 0xd1, 0xbc, 0xde, 0x7, 0x33, 0xa5, 0x9b, 0xd4, 0x2f, 0x87, 0x31, 0xf4, 0x49, 0xda, 0x6d, 0xc1, 0x30, 0x10, 0xa9, 0x16, 0x93, 0xd, 0x48},
    82  		},
    83  		{
    84  			schema:          `{"type":"string"}`,
    85  			want:            avro.String,
    86  			wantFingerprint: [32]byte{0xe9, 0xe5, 0xc1, 0xc9, 0xe4, 0xf6, 0x27, 0x73, 0x39, 0xd1, 0xbc, 0xde, 0x7, 0x33, 0xa5, 0x9b, 0xd4, 0x2f, 0x87, 0x31, 0xf4, 0x49, 0xda, 0x6d, 0xc1, 0x30, 0x10, 0xa9, 0x16, 0x93, 0xd, 0x48},
    87  		},
    88  		{
    89  			schema:          "bytes",
    90  			want:            avro.Bytes,
    91  			wantFingerprint: [32]byte{0x9a, 0xe5, 0x7, 0xa9, 0xdd, 0x39, 0xee, 0x5b, 0x7c, 0x7e, 0x28, 0x5d, 0xa2, 0xc0, 0x84, 0x65, 0x21, 0xc8, 0xae, 0x8d, 0x80, 0xfe, 0xea, 0xe5, 0x50, 0x4e, 0xc, 0x98, 0x1d, 0x53, 0xf5, 0xfa},
    92  		},
    93  		{
    94  			schema:          `{"type":"bytes"}`,
    95  			want:            avro.Bytes,
    96  			wantFingerprint: [32]byte{0x9a, 0xe5, 0x7, 0xa9, 0xdd, 0x39, 0xee, 0x5b, 0x7c, 0x7e, 0x28, 0x5d, 0xa2, 0xc0, 0x84, 0x65, 0x21, 0xc8, 0xae, 0x8d, 0x80, 0xfe, 0xea, 0xe5, 0x50, 0x4e, 0xc, 0x98, 0x1d, 0x53, 0xf5, 0xfa},
    97  		},
    98  		{
    99  			schema:          "int",
   100  			want:            avro.Int,
   101  			wantFingerprint: [32]byte{0x3f, 0x2b, 0x87, 0xa9, 0xfe, 0x7c, 0xc9, 0xb1, 0x38, 0x35, 0x59, 0x8c, 0x39, 0x81, 0xcd, 0x45, 0xe3, 0xe3, 0x55, 0x30, 0x9e, 0x50, 0x90, 0xaa, 0x9, 0x33, 0xd7, 0xbe, 0xcb, 0x6f, 0xba, 0x45},
   102  		},
   103  		{
   104  			schema:          `{"type":"int"}`,
   105  			want:            avro.Int,
   106  			wantFingerprint: [32]byte{0x3f, 0x2b, 0x87, 0xa9, 0xfe, 0x7c, 0xc9, 0xb1, 0x38, 0x35, 0x59, 0x8c, 0x39, 0x81, 0xcd, 0x45, 0xe3, 0xe3, 0x55, 0x30, 0x9e, 0x50, 0x90, 0xaa, 0x9, 0x33, 0xd7, 0xbe, 0xcb, 0x6f, 0xba, 0x45},
   107  		},
   108  		{
   109  			schema:          "long",
   110  			want:            avro.Long,
   111  			wantFingerprint: [32]byte{0xc3, 0x2c, 0x49, 0x7d, 0xf6, 0x73, 0xc, 0x97, 0xfa, 0x7, 0x36, 0x2a, 0xa5, 0x2, 0x3f, 0x37, 0xd4, 0x9a, 0x2, 0x7e, 0xc4, 0x52, 0x36, 0x7, 0x78, 0x11, 0x4c, 0xf4, 0x27, 0x96, 0x5a, 0xdd},
   112  		},
   113  		{
   114  			schema:          `{"type":"long"}`,
   115  			want:            avro.Long,
   116  			wantFingerprint: [32]byte{0xc3, 0x2c, 0x49, 0x7d, 0xf6, 0x73, 0xc, 0x97, 0xfa, 0x7, 0x36, 0x2a, 0xa5, 0x2, 0x3f, 0x37, 0xd4, 0x9a, 0x2, 0x7e, 0xc4, 0x52, 0x36, 0x7, 0x78, 0x11, 0x4c, 0xf4, 0x27, 0x96, 0x5a, 0xdd},
   117  		},
   118  		{
   119  			schema:          "float",
   120  			want:            avro.Float,
   121  			wantFingerprint: [32]byte{0x1e, 0x71, 0xf9, 0xec, 0x5, 0x1d, 0x66, 0x3f, 0x56, 0xb0, 0xd8, 0xe1, 0xfc, 0x84, 0xd7, 0x1a, 0xa5, 0x6c, 0xcf, 0xe9, 0xfa, 0x93, 0xaa, 0x20, 0xd1, 0x5, 0x47, 0xa7, 0xab, 0xeb, 0x5c, 0xc0},
   122  		},
   123  		{
   124  			schema:          `{"type":"float"}`,
   125  			want:            avro.Float,
   126  			wantFingerprint: [32]byte{0x1e, 0x71, 0xf9, 0xec, 0x5, 0x1d, 0x66, 0x3f, 0x56, 0xb0, 0xd8, 0xe1, 0xfc, 0x84, 0xd7, 0x1a, 0xa5, 0x6c, 0xcf, 0xe9, 0xfa, 0x93, 0xaa, 0x20, 0xd1, 0x5, 0x47, 0xa7, 0xab, 0xeb, 0x5c, 0xc0},
   127  		},
   128  		{
   129  			schema:          "double",
   130  			want:            avro.Double,
   131  			wantFingerprint: [32]byte{0x73, 0xa, 0x9a, 0x8c, 0x61, 0x16, 0x81, 0xd7, 0xee, 0xf4, 0x42, 0xe0, 0x3c, 0x16, 0xc7, 0xd, 0x13, 0xbc, 0xa3, 0xeb, 0x8b, 0x97, 0x7b, 0xb4, 0x3, 0xea, 0xff, 0x52, 0x17, 0x6a, 0xf2, 0x54},
   132  		},
   133  		{
   134  			schema:          `{"type":"double"}`,
   135  			want:            avro.Double,
   136  			wantFingerprint: [32]byte{0x73, 0xa, 0x9a, 0x8c, 0x61, 0x16, 0x81, 0xd7, 0xee, 0xf4, 0x42, 0xe0, 0x3c, 0x16, 0xc7, 0xd, 0x13, 0xbc, 0xa3, 0xeb, 0x8b, 0x97, 0x7b, 0xb4, 0x3, 0xea, 0xff, 0x52, 0x17, 0x6a, 0xf2, 0x54},
   137  		},
   138  		{
   139  			schema:          "boolean",
   140  			want:            avro.Boolean,
   141  			wantFingerprint: [32]byte{0xa5, 0xb0, 0x31, 0xab, 0x62, 0xbc, 0x41, 0x6d, 0x72, 0xc, 0x4, 0x10, 0xd8, 0x2, 0xea, 0x46, 0xb9, 0x10, 0xc4, 0xfb, 0xe8, 0x5c, 0x50, 0xa9, 0x46, 0xcc, 0xc6, 0x58, 0xb7, 0x4e, 0x67, 0x7e},
   142  		},
   143  		{
   144  			schema:          `{"type":"boolean"}`,
   145  			want:            avro.Boolean,
   146  			wantFingerprint: [32]byte{0xa5, 0xb0, 0x31, 0xab, 0x62, 0xbc, 0x41, 0x6d, 0x72, 0xc, 0x4, 0x10, 0xd8, 0x2, 0xea, 0x46, 0xb9, 0x10, 0xc4, 0xfb, 0xe8, 0x5c, 0x50, 0xa9, 0x46, 0xcc, 0xc6, 0x58, 0xb7, 0x4e, 0x67, 0x7e},
   147  		},
   148  	}
   149  
   150  	for _, test := range tests {
   151  		test := test
   152  		t.Run(test.schema, func(t *testing.T) {
   153  			t.Parallel()
   154  
   155  			schema, err := avro.ParseWithCache(test.schema, "", &avro.SchemaCache{})
   156  
   157  			require.NoError(t, err)
   158  			assert.Equal(t, test.want, schema.Type())
   159  			assert.Equal(t, test.wantFingerprint, schema.Fingerprint())
   160  		})
   161  	}
   162  }
   163  
   164  func TestPrimitiveSchema_HandlesProps(t *testing.T) {
   165  	schm := `
   166  {
   167     "type": "string",
   168     "foo": "bar",
   169     "baz": 1
   170  }
   171  `
   172  
   173  	s, err := avro.Parse(schm)
   174  
   175  	assert.NoError(t, err)
   176  	assert.Equal(t, avro.String, s.Type())
   177  	assert.Equal(t, "bar", s.(*avro.PrimitiveSchema).Prop("foo"))
   178  	assert.Equal(t, float64(1), s.(*avro.PrimitiveSchema).Prop("baz"))
   179  }
   180  
   181  func TestRecordSchema(t *testing.T) {
   182  	tests := []struct {
   183  		name    string
   184  		schema  string
   185  		wantErr require.ErrorAssertionFunc
   186  	}{
   187  		{
   188  			name:    "Valid",
   189  			schema:  `{"type":"record", "name":"test", "namespace": "org.hamba.avro", "doc": "docs", "fields":[{"name": "field", "type": "int"}]}`,
   190  			wantErr: require.NoError,
   191  		},
   192  		{
   193  			name:    "Invalid Name First Char",
   194  			schema:  `{"type":"record", "name":"0test", "namespace": "org.hamba.avro", "fields":[{"name": "field", "type": "int"}]}`,
   195  			wantErr: require.Error,
   196  		},
   197  		{
   198  			name:    "Invalid Name Other Char",
   199  			schema:  `{"type":"record", "name":"test+", "namespace": "org.hamba.avro", "fields":[{"name": "field", "type": "int"}]}`,
   200  			wantErr: require.Error,
   201  		},
   202  		{
   203  			name:    "Empty Name",
   204  			schema:  `{"type":"record", "name":"", "namespace": "org.hamba.avro", "fields":[{"name": "field", "type": "int"}]}`,
   205  			wantErr: require.Error,
   206  		},
   207  		{
   208  			name:    "No Name",
   209  			schema:  `{"type":"record", "namespace": "org.hamba.avro", "fields":[{"name": "intField", "type": "int"}]}`,
   210  			wantErr: require.Error,
   211  		},
   212  		{
   213  			name:    "Invalid Namespace",
   214  			schema:  `{"type":"record", "name":"test", "namespace": "org.hamba.avro+", "fields":[{"name": "field", "type": "int"}]}`,
   215  			wantErr: require.Error,
   216  		},
   217  		{
   218  			name:    "Empty Namespace",
   219  			schema:  `{"type":"record", "name":"test", "namespace": "", "fields":[{"name": "intField", "type": "int"}]}`,
   220  			wantErr: require.Error,
   221  		},
   222  		{
   223  			name:    "No Fields",
   224  			schema:  `{"type":"record", "name":"test", "namespace": "org.hamba.avro"}`,
   225  			wantErr: require.Error,
   226  		},
   227  		{
   228  			name:    "Invalid Field Type",
   229  			schema:  `{"type":"record", "name":"test", "namespace": "org.hamba.avro", "fields":["test"]}`,
   230  			wantErr: require.Error,
   231  		},
   232  		{
   233  			name:    "No Field Name",
   234  			schema:  `{"type":"record", "name":"test", "namespace": "org.hamba.avro", "fields":[{"type": "int"}]}`,
   235  			wantErr: require.Error,
   236  		},
   237  		{
   238  			name:    "Invalid Field Name",
   239  			schema:  `{"type":"record", "name":"test", "namespace": "org.hamba.avro", "fields":[{"name": "field+", "type": "int"}]}`,
   240  			wantErr: require.Error,
   241  		},
   242  		{
   243  			name:    "Invalid Alias",
   244  			schema:  `{"type":"record", "name":"test", "namespace": "org.hamba.avro", "fields":[{"name": "field", "aliases": ["test+"], "type": "int"}]}`,
   245  			wantErr: require.Error,
   246  		},
   247  		{
   248  			name:    "No Field Type",
   249  			schema:  `{"type":"record", "name":"test", "namespace": "org.hamba.avro", "fields":[{"name": "field"}]}`,
   250  			wantErr: require.Error,
   251  		},
   252  		{
   253  			name:    "Invalid Field Type",
   254  			schema:  `{"type":"record", "name":"test", "namespace": "org.hamba.avro", "fields":[{"name": "field", "type": "blah"}]}`,
   255  			wantErr: require.Error,
   256  		},
   257  	}
   258  
   259  	for _, test := range tests {
   260  		test := test
   261  		t.Run(test.name, func(t *testing.T) {
   262  			t.Parallel()
   263  
   264  			schema, err := avro.ParseWithCache(test.schema, "", &avro.SchemaCache{})
   265  
   266  			test.wantErr(t, err)
   267  			if schema != nil {
   268  				assert.Equal(t, avro.Record, schema.Type())
   269  			}
   270  		})
   271  	}
   272  }
   273  
   274  func TestErrorRecordSchema(t *testing.T) {
   275  	tests := []struct {
   276  		name       string
   277  		schema     string
   278  		wantSchema bool
   279  		wantErr    require.ErrorAssertionFunc
   280  	}{
   281  		{
   282  			name:       "Valid",
   283  			schema:     `{"type":"error", "name":"test", "namespace": "org.hamba.avro", "doc": "docs", "fields":[{"name": "field", "type": "int"}]}`,
   284  			wantSchema: true,
   285  			wantErr:    require.NoError,
   286  		},
   287  		{
   288  			name:    "Invalid Name First Char",
   289  			schema:  `{"type":"error", "name":"0test", "namespace": "org.hamba.avro", "fields":[{"name": "field", "type": "int"}]}`,
   290  			wantErr: require.Error,
   291  		},
   292  		{
   293  			name:    "Invalid Name Other Char",
   294  			schema:  `{"type":"error", "name":"test+", "namespace": "org.hamba.avro", "fields":[{"name": "field", "type": "int"}]}`,
   295  			wantErr: require.Error,
   296  		},
   297  		{
   298  			name:    "Empty Name",
   299  			schema:  `{"type":"error", "name":"", "namespace": "org.hamba.avro", "fields":[{"name": "field", "type": "int"}]}`,
   300  			wantErr: require.Error,
   301  		},
   302  		{
   303  			name:    "No Name",
   304  			schema:  `{"type":"error", "namespace": "org.hamba.avro", "fields":[{"name": "intField", "type": "int"}]}`,
   305  			wantErr: require.Error,
   306  		},
   307  		{
   308  			name:    "Invalid Namespace",
   309  			schema:  `{"type":"error", "name":"test", "namespace": "org.hamba.avro+", "fields":[{"name": "field", "type": "int"}]}`,
   310  			wantErr: require.Error,
   311  		},
   312  		{
   313  			name:    "Empty Namespace",
   314  			schema:  `{"type":"error", "name":"test", "namespace": "", "fields":[{"name": "intField", "type": "int"}]}`,
   315  			wantErr: require.Error,
   316  		},
   317  	}
   318  
   319  	for _, test := range tests {
   320  		test := test
   321  		t.Run(test.name, func(t *testing.T) {
   322  			t.Parallel()
   323  
   324  			schema, err := avro.ParseWithCache(test.schema, "", &avro.SchemaCache{})
   325  
   326  			test.wantErr(t, err)
   327  			if test.wantSchema {
   328  				assert.Equal(t, avro.Record, schema.Type())
   329  				recSchema := schema.(*avro.RecordSchema)
   330  				assert.True(t, recSchema.IsError())
   331  			}
   332  		})
   333  	}
   334  }
   335  
   336  func TestRecordSchema_ValidatesDefault(t *testing.T) {
   337  	tests := []struct {
   338  		name    string
   339  		schema  string
   340  		wantErr assert.ErrorAssertionFunc
   341  	}{
   342  		{
   343  			name:    "String",
   344  			schema:  `{"type":"record", "name":"test", "namespace": "org.hamba.avro", "fields":[{"name": "a", "type": "string", "default": "test"}]}`,
   345  			wantErr: assert.NoError,
   346  		},
   347  		{
   348  			name:    "Int",
   349  			schema:  `{"type":"record", "name":"test", "namespace": "org.hamba.avro", "fields":[{"name": "a", "type": "int", "default": 1}]}`,
   350  			wantErr: assert.NoError,
   351  		},
   352  		{
   353  			name:    "Long",
   354  			schema:  `{"type":"record", "name":"test", "namespace": "org.hamba.avro", "fields":[{"name": "a", "type": "long", "default": 1}]}`,
   355  			wantErr: assert.NoError,
   356  		},
   357  		{
   358  			name:    "Float",
   359  			schema:  `{"type":"record", "name":"test", "namespace": "org.hamba.avro", "fields":[{"name": "a", "type": "float", "default": 1}]}`,
   360  			wantErr: assert.NoError,
   361  		},
   362  		{
   363  			name:    "Double",
   364  			schema:  `{"type":"record", "name":"test", "namespace": "org.hamba.avro", "fields":[{"name": "a", "type": "double", "default": 1}]}`,
   365  			wantErr: assert.NoError,
   366  		},
   367  		{
   368  			name:    "Array",
   369  			schema:  `{"type":"record", "name":"test", "namespace": "org.hamba.avro", "fields":[{"name": "a", "type": {"type":"array", "items": "int"}, "default": [1,2]}]}`,
   370  			wantErr: assert.NoError,
   371  		},
   372  		{
   373  			name:    "Array Not Array",
   374  			schema:  `{"type":"record", "name":"test", "namespace": "org.hamba.avro", "fields":[{"name": "a", "type": {"type":"array", "items": "int"}, "default": "test"}]}`,
   375  			wantErr: assert.Error,
   376  		},
   377  		{
   378  			name:    "Array Invalid Type",
   379  			schema:  `{"type":"record", "name":"test", "namespace": "org.hamba.avro", "fields":[{"name": "a", "type": {"type":"array", "items": "int"}, "default": ["test"]}]}`,
   380  			wantErr: assert.Error,
   381  		},
   382  		{
   383  			name:    "Map",
   384  			schema:  `{"type":"record", "name":"test", "namespace": "org.hamba.avro", "fields":[{"name": "a", "type": {"type":"map", "values": "int"}, "default": {"b": 1}}]}`,
   385  			wantErr: assert.NoError,
   386  		},
   387  		{
   388  			name:    "Map Not Map",
   389  			schema:  `{"type":"record", "name":"test", "namespace": "org.hamba.avro", "fields":[{"name": "a", "type": {"type":"map", "values": "int"}, "default": "test"}]}`,
   390  			wantErr: assert.Error,
   391  		},
   392  		{
   393  			name:    "Map Invalid Type",
   394  			schema:  `{"type":"record", "name":"test", "namespace": "org.hamba.avro", "fields":[{"name": "a", "type": {"type":"map", "values": "int"}, "default": {"b": "test"}}]}`,
   395  			wantErr: assert.Error,
   396  		},
   397  		{
   398  			name:    "Union",
   399  			schema:  `{"type":"record", "name":"test", "namespace": "org.hamba.avro", "fields":[{"name": "a", "type": ["string", "null"]}]}`,
   400  			wantErr: assert.NoError,
   401  		},
   402  		{
   403  			name:    "Union Default",
   404  			schema:  `{"type":"record", "name":"test", "namespace": "org.hamba.avro", "fields":[{"name": "a", "type": ["null", "string"], "default": null}]}`,
   405  			wantErr: assert.NoError,
   406  		},
   407  		{
   408  			name:    "Union Invalid Type",
   409  			schema:  `{"type":"record", "name":"test", "namespace": "org.hamba.avro", "fields":[{"name": "a", "type": ["null", "string"], "default": "string"}]}`,
   410  			wantErr: assert.Error,
   411  		},
   412  		{
   413  			name:    "Record",
   414  			schema:  `{"type":"record", "name":"test", "namespace": "org.hamba.avro", "fields":[{"name": "a", "type": {"type":"record", "name": "test2", "fields":[{"name": "b", "type": "int"},{"name": "c", "type": "int", "default": 1}]}, "default": {"b": 1}}]}`,
   415  			wantErr: assert.NoError,
   416  		},
   417  		{
   418  			name:    "Record Not Map",
   419  			schema:  `{"type":"record", "name":"test", "namespace": "org.hamba.avro", "fields":[{"name": "a", "type": {"type":"record", "name": "test2", "fields":[{"name": "b", "type": "int"},{"name": "c", "type": "int", "default": 1}]}, "default": "test"}]}`,
   420  			wantErr: assert.Error,
   421  		},
   422  		{
   423  			name:    "Record Invalid Type",
   424  			schema:  `{"type":"record", "name":"test", "namespace": "org.hamba.avro", "fields":[{"name": "a", "type": {"type":"record", "name": "test2", "fields":[{"name": "b", "type": "int"},{"name": "c", "type": "int", "default": 1}]}, "default": {"b": "test"}}]}`,
   425  			wantErr: assert.Error,
   426  		},
   427  		{
   428  			name:    "Record Invalid Field Type",
   429  			schema:  `{"type":"record", "name":"test", "namespace": "org.hamba.avro", "fields":[{"name": "a", "type": {"type":"record", "name": "test2", "fields":[{"name": "b", "type": "int"},{"name": "c", "type": "int", "default": "test"}]}, "default": {"b": 1}}]}`,
   430  			wantErr: assert.Error,
   431  		},
   432  	}
   433  
   434  	for _, test := range tests {
   435  		test := test
   436  		t.Run(test.name, func(t *testing.T) {
   437  			t.Parallel()
   438  
   439  			_, err := avro.ParseWithCache(test.schema, "", &avro.SchemaCache{})
   440  
   441  			test.wantErr(t, err)
   442  		})
   443  	}
   444  }
   445  
   446  func TestRecordSchema_ValidatesOrder(t *testing.T) {
   447  	tests := []struct {
   448  		name    string
   449  		schema  string
   450  		want    avro.Order
   451  		wantErr assert.ErrorAssertionFunc
   452  	}{
   453  		{
   454  			name:    "empty",
   455  			schema:  `{"type":"record", "name":"test", "fields":[{"name": "a", "type": "string"}]}`,
   456  			want:    avro.Asc,
   457  			wantErr: assert.NoError,
   458  		},
   459  		{
   460  			name:    "asc",
   461  			schema:  `{"type":"record", "name":"test", "fields":[{"name": "a", "type": "string", "order": "ascending"}]}`,
   462  			want:    avro.Asc,
   463  			wantErr: assert.NoError,
   464  		},
   465  		{
   466  			name:    "desc",
   467  			schema:  `{"type":"record", "name":"test", "fields":[{"name": "a", "type": "string", "order": "descending"}]}`,
   468  			want:    avro.Desc,
   469  			wantErr: assert.NoError,
   470  		},
   471  		{
   472  			name:    "ignore",
   473  			schema:  `{"type":"record", "name":"test", "fields":[{"name": "a", "type": "string", "order": "ignore"}]}`,
   474  			want:    avro.Ignore,
   475  			wantErr: assert.NoError,
   476  		},
   477  		{
   478  			name:    "invalid",
   479  			schema:  `{"type":"record", "name":"test", "fields":[{"name": "a", "type": "string", "order": "blah"}]}`,
   480  			wantErr: assert.Error,
   481  		},
   482  	}
   483  
   484  	for _, test := range tests {
   485  		test := test
   486  		t.Run(test.name, func(t *testing.T) {
   487  			t.Parallel()
   488  
   489  			schema, err := avro.ParseWithCache(test.schema, "", &avro.SchemaCache{})
   490  
   491  			test.wantErr(t, err)
   492  			if test.want != "" {
   493  				rs := schema.(*avro.RecordSchema)
   494  				require.Len(t, rs.Fields(), 1)
   495  				assert.Equal(t, test.want, rs.Fields()[0].Order())
   496  			}
   497  		})
   498  	}
   499  }
   500  
   501  func TestRecordSchema_HandlesProps(t *testing.T) {
   502  	schm := `
   503  {
   504     "type": "record",
   505     "name": "valid_name",
   506     "namespace": "org.hamba.avro",
   507     "doc": "foo",
   508     "foo": "bar1",
   509     "fields": [
   510         {"name": "intField", "doc": "bar", "type": "int", "foo": "bar2"}
   511     ]
   512  }
   513  `
   514  
   515  	s, err := avro.Parse(schm)
   516  	require.NoError(t, err)
   517  
   518  	rs := s.(*avro.RecordSchema)
   519  	assert.Equal(t, avro.Record, s.Type())
   520  	assert.Equal(t, "foo", rs.Doc())
   521  	assert.Equal(t, "bar1", rs.Prop("foo"))
   522  	require.Len(t, rs.Fields(), 1)
   523  	assert.Equal(t, "bar", rs.Fields()[0].Doc())
   524  	assert.Equal(t, "bar2", rs.Fields()[0].Prop("foo"))
   525  }
   526  
   527  func TestRecordSchema_WithReference(t *testing.T) {
   528  	schm := `
   529  {
   530     "type": "record",
   531     "name": "valid_name",
   532     "namespace": "org.hamba.avro",
   533     "fields": [
   534         {"name": "intField", "type": "int"},
   535         {"name": "Ref", "type": "valid_name"}
   536     ]
   537  }
   538  `
   539  
   540  	s, err := avro.Parse(schm)
   541  
   542  	require.NoError(t, err)
   543  	assert.Equal(t, avro.Record, s.Type())
   544  	assert.Equal(t, avro.Ref, s.(*avro.RecordSchema).Fields()[1].Type().Type())
   545  	assert.Equal(t, s.Fingerprint(), s.(*avro.RecordSchema).Fields()[1].Type().Fingerprint())
   546  }
   547  
   548  func TestRecordSchema_WithReferenceFullName(t *testing.T) {
   549  	schm := `
   550  {
   551     "type": "record",
   552     "name": "org.hamba.avro.ValidName",
   553      "fields": [
   554          {
   555              "name": "recordType1",
   556              "type": {
   557                  "name": "refIntType",
   558                  "type": "record",
   559                  "fields": [
   560                      {"name": "intField", "type": "int"}
   561                  ]
   562              }
   563          },
   564          {
   565              "name": "recordType2",
   566              "type": "org.hamba.avro.refIntType"
   567          }
   568      ]
   569  }
   570  `
   571  
   572  	s, err := avro.Parse(schm)
   573  
   574  	require.NoError(t, err)
   575  	assert.Equal(t, avro.Record, s.Type())
   576  	assert.Equal(t, avro.Ref, s.(*avro.RecordSchema).Fields()[1].Type().Type())
   577  	assert.Equal(t, s.(*avro.RecordSchema).Fields()[0].Type().Fingerprint(), s.(*avro.RecordSchema).Fields()[1].Type().Fingerprint())
   578  }
   579  
   580  func TestRecordSchema_WithAliasReference(t *testing.T) {
   581  	schm := `
   582  {
   583     "type": "record",
   584     "name": "valid_name",
   585     "namespace": "org.hamba.avro",
   586     "aliases": ["valid_alias"],
   587     "fields": [
   588         {"name": "intField", "type": "int"},
   589         {"name": "ref", "type": "valid_alias"}
   590     ]
   591  }
   592  `
   593  
   594  	s, err := avro.Parse(schm)
   595  
   596  	require.NoError(t, err)
   597  	assert.Equal(t, avro.Record, s.Type())
   598  	assert.Equal(t, avro.Ref, s.(*avro.RecordSchema).Fields()[1].Type().Type())
   599  	assert.Equal(t, s.Fingerprint(), s.(*avro.RecordSchema).Fields()[1].Type().Fingerprint())
   600  }
   601  
   602  func TestEnumSchema(t *testing.T) {
   603  	tests := []struct {
   604  		name        string
   605  		schema      string
   606  		wantName    string
   607  		wantDefault string
   608  		wantErr     require.ErrorAssertionFunc
   609  	}{
   610  		{
   611  			name:     "Valid",
   612  			schema:   `{"type":"enum", "name":"test", "namespace": "org.hamba.avro", "symbols":["TEST"]}`,
   613  			wantName: "org.hamba.avro.test",
   614  			wantErr:  require.NoError,
   615  		},
   616  		{
   617  			name:        "Valid With Default",
   618  			schema:      `{"type":"enum", "name":"test", "namespace": "org.hamba.avro", "symbols":["TEST"], "default": "TEST"}`,
   619  			wantName:    "org.hamba.avro.test",
   620  			wantDefault: "TEST",
   621  			wantErr:     require.NoError,
   622  		},
   623  		{
   624  			name:    "Invalid Name",
   625  			schema:  `{"type":"enum", "name":"test+", "namespace": "org.hamba.avro", "symbols":["TEST"]}`,
   626  			wantErr: require.Error,
   627  		},
   628  		{
   629  			name:    "Empty Name",
   630  			schema:  `{"type":"enum", "name":"", "namespace": "org.hamba.avro", "symbols":["TEST"]}`,
   631  			wantErr: require.Error,
   632  		},
   633  		{
   634  			name:    "No Name",
   635  			schema:  `{"type":"enum", "namespace": "org.hamba.avro", "symbols":["TEST"]}`,
   636  			wantErr: require.Error,
   637  		},
   638  		{
   639  			name:    "Invalid Namespace",
   640  			schema:  `{"type":"enum", "name":"test", "namespace": "org.hamba.avro+", "symbols":["TEST"]}`,
   641  			wantErr: require.Error,
   642  		},
   643  		{
   644  			name:    "Empty Namespace",
   645  			schema:  `{"type":"enum", "name":"test", "namespace": "", "symbols":["TEST"]}`,
   646  			wantErr: require.Error,
   647  		},
   648  		{
   649  			name:    "No Symbols",
   650  			schema:  `{"type":"enum", "name":"test", "namespace": "org.hamba.avro"}`,
   651  			wantErr: require.Error,
   652  		},
   653  		{
   654  			name:    "Empty Symbols",
   655  			schema:  `{"type":"enum", "name":"test", "namespace": "org.hamba.avro", "symbols":[]}`,
   656  			wantErr: require.Error,
   657  		},
   658  		{
   659  			name:    "Invalid Symbol",
   660  			schema:  `{"type":"enum", "name":"test", "namespace": "org.hamba.avro", "symbols":["TEST+"]}`,
   661  			wantErr: require.Error,
   662  		},
   663  		{
   664  			name:    "Invalid Symbol Type",
   665  			schema:  `{"type":"enum", "name":"test", "namespace": "org.hamba.avro", "symbols":[1]}`,
   666  			wantErr: require.Error,
   667  		},
   668  		{
   669  			name:    "Invalid Default",
   670  			schema:  `{"type":"enum", "name":"test", "namespace": "org.hamba.avro", "symbols":["TEST"], "default": "foo"}`,
   671  			wantErr: require.Error,
   672  		},
   673  	}
   674  
   675  	for _, test := range tests {
   676  		test := test
   677  		t.Run(test.name, func(t *testing.T) {
   678  			t.Parallel()
   679  
   680  			schema, err := avro.ParseWithCache(test.schema, "", &avro.SchemaCache{})
   681  
   682  			test.wantErr(t, err)
   683  			if test.wantName != "" {
   684  				assert.Equal(t, avro.Enum, schema.Type())
   685  				named := schema.(*avro.EnumSchema)
   686  				assert.Equal(t, test.wantName, named.FullName())
   687  				assert.Equal(t, test.wantDefault, named.Default())
   688  			}
   689  		})
   690  	}
   691  }
   692  
   693  func TestEnumSchema_HandlesProps(t *testing.T) {
   694  	schm := `{"type":"enum", "name":"test", "namespace": "org.hamba.avro", "doc": "hello", "symbols":["TEST"], "foo":"bar"}`
   695  
   696  	s, err := avro.Parse(schm)
   697  	require.NoError(t, err)
   698  
   699  	es := s.(*avro.EnumSchema)
   700  	assert.Equal(t, avro.Enum, s.Type())
   701  	assert.Equal(t, "hello", es.Doc())
   702  	assert.Equal(t, "bar", es.Prop("foo"))
   703  }
   704  
   705  func TestArraySchema(t *testing.T) {
   706  	tests := []struct {
   707  		name    string
   708  		schema  string
   709  		want    avro.Schema
   710  		wantErr require.ErrorAssertionFunc
   711  	}{
   712  		{
   713  			name:    "Valid",
   714  			schema:  `{"type":"array", "items": "int"}`,
   715  			want:    avro.NewArraySchema(avro.NewPrimitiveSchema(avro.Int, nil)),
   716  			wantErr: require.NoError,
   717  		},
   718  		{
   719  			name:    "No Items",
   720  			schema:  `{"type":"array"}`,
   721  			wantErr: require.Error,
   722  		},
   723  		{
   724  			name:    "Invalid Items Type",
   725  			schema:  `{"type":"array", "items": "blah"}`,
   726  			wantErr: require.Error,
   727  		},
   728  	}
   729  
   730  	for _, test := range tests {
   731  		test := test
   732  		t.Run(test.name, func(t *testing.T) {
   733  			t.Parallel()
   734  
   735  			got, err := avro.ParseWithCache(test.schema, "", &avro.SchemaCache{})
   736  
   737  			test.wantErr(t, err)
   738  			assert.Equal(t, test.want, got)
   739  		})
   740  	}
   741  }
   742  
   743  func TestArraySchema_HandlesProps(t *testing.T) {
   744  	schm := `{"type":"array", "items": "int", "foo":"bar"}`
   745  
   746  	s, err := avro.Parse(schm)
   747  
   748  	require.NoError(t, err)
   749  	assert.Equal(t, avro.Array, s.Type())
   750  	assert.Equal(t, "bar", s.(*avro.ArraySchema).Prop("foo"))
   751  }
   752  
   753  func TestMapSchema(t *testing.T) {
   754  	tests := []struct {
   755  		name    string
   756  		schema  string
   757  		want    avro.Schema
   758  		wantErr require.ErrorAssertionFunc
   759  	}{
   760  		{
   761  			name:    "Valid",
   762  			schema:  `{"type":"map", "values": "int"}`,
   763  			want:    avro.NewMapSchema(avro.NewPrimitiveSchema(avro.Int, nil)),
   764  			wantErr: require.NoError,
   765  		},
   766  		{
   767  			name:    "No Values",
   768  			schema:  `{"type":"map"}`,
   769  			wantErr: require.Error,
   770  		},
   771  		{
   772  			name:    "Invalid Values Type",
   773  			schema:  `{"type":"map", "values": "blah"}`,
   774  			wantErr: require.Error,
   775  		},
   776  	}
   777  
   778  	for _, test := range tests {
   779  		test := test
   780  		t.Run(test.name, func(t *testing.T) {
   781  			t.Parallel()
   782  
   783  			got, err := avro.ParseWithCache(test.schema, "", &avro.SchemaCache{})
   784  
   785  			test.wantErr(t, err)
   786  			assert.Equal(t, test.want, got)
   787  		})
   788  	}
   789  }
   790  
   791  func TestMapSchema_HandlesProps(t *testing.T) {
   792  	schm := `{"type":"map", "values": "int", "foo":"bar"}`
   793  
   794  	s, err := avro.Parse(schm)
   795  
   796  	require.NoError(t, err)
   797  	assert.Equal(t, avro.Map, s.Type())
   798  	assert.Equal(t, "bar", s.(*avro.MapSchema).Prop("foo"))
   799  }
   800  
   801  func TestUnionSchema(t *testing.T) {
   802  	tests := []struct {
   803  		name            string
   804  		schema          string
   805  		wantFingerprint [32]byte
   806  		wantErr         require.ErrorAssertionFunc
   807  	}{
   808  		{
   809  			name:            "Valid Simple",
   810  			schema:          `["null", "int"]`,
   811  			wantFingerprint: [32]byte{0xb4, 0x94, 0x95, 0xc5, 0xb1, 0xc2, 0x6f, 0x4, 0x89, 0x6a, 0x5f, 0x68, 0x65, 0xf, 0xe2, 0xb7, 0x64, 0x23, 0x62, 0xc3, 0x41, 0x98, 0xd6, 0xbc, 0x74, 0x65, 0xa1, 0xd9, 0xf7, 0xe1, 0xaf, 0xce},
   812  			wantErr:         require.NoError,
   813  		},
   814  		{
   815  			name:            "Valid Complex",
   816  			schema:          `{"type":["null", "int"]}`,
   817  			wantFingerprint: [32]byte{0xb4, 0x94, 0x95, 0xc5, 0xb1, 0xc2, 0x6f, 0x4, 0x89, 0x6a, 0x5f, 0x68, 0x65, 0xf, 0xe2, 0xb7, 0x64, 0x23, 0x62, 0xc3, 0x41, 0x98, 0xd6, 0xbc, 0x74, 0x65, 0xa1, 0xd9, 0xf7, 0xe1, 0xaf, 0xce},
   818  			wantErr:         require.NoError,
   819  		},
   820  		{
   821  			name:    "No Nested Union Type",
   822  			schema:  `["null", ["string"]]`,
   823  			wantErr: require.Error,
   824  		},
   825  		{
   826  			name:    "No Duplicate Types",
   827  			schema:  `["string", "string"]`,
   828  			wantErr: require.Error,
   829  		},
   830  		{
   831  			name:    "No Duplicate Names",
   832  			schema:  `[{"type":"enum", "name":"test", "symbols":["TEST"]}, {"type":"enum", "name":"test", "symbols":["TEST"]}]`,
   833  			wantErr: require.Error,
   834  		},
   835  		{
   836  			name:    "Invalid Type",
   837  			schema:  `["null", "blah"]`,
   838  			wantErr: require.Error,
   839  		},
   840  	}
   841  
   842  	for _, test := range tests {
   843  		test := test
   844  		t.Run(test.name, func(t *testing.T) {
   845  			t.Parallel()
   846  
   847  			schema, err := avro.ParseWithCache(test.schema, "", &avro.SchemaCache{})
   848  
   849  			test.wantErr(t, err)
   850  			if test.wantFingerprint != [32]byte{} {
   851  				assert.Equal(t, avro.Union, schema.Type())
   852  				assert.Equal(t, test.wantFingerprint, schema.Fingerprint())
   853  			}
   854  		})
   855  	}
   856  }
   857  
   858  func TestUnionSchema_Indices(t *testing.T) {
   859  	tests := []struct {
   860  		name   string
   861  		schema string
   862  		want   [2]int
   863  	}{
   864  		{
   865  			name:   "Null First",
   866  			schema: `["null", "string"]`,
   867  			want:   [2]int{0, 1},
   868  		},
   869  		{
   870  			name:   "Null Second",
   871  			schema: `["string", "null"]`,
   872  			want:   [2]int{1, 0},
   873  		},
   874  		{
   875  			name:   "Not Nullable",
   876  			schema: `["null", "string", "int"]`,
   877  			want:   [2]int{0, 0},
   878  		},
   879  	}
   880  
   881  	for _, test := range tests {
   882  		test := test
   883  		t.Run(test.name, func(t *testing.T) {
   884  			t.Parallel()
   885  
   886  			schema, err := avro.ParseWithCache(test.schema, "", &avro.SchemaCache{})
   887  
   888  			require.NoError(t, err)
   889  			null, typ := schema.(*avro.UnionSchema).Indices()
   890  			assert.Equal(t, test.want[0], null)
   891  			assert.Equal(t, test.want[1], typ)
   892  		})
   893  	}
   894  }
   895  
   896  func TestFixedSchema(t *testing.T) {
   897  	tests := []struct {
   898  		name            string
   899  		schema          string
   900  		wantName        string
   901  		wantFingerprint [32]byte
   902  		wantErr         require.ErrorAssertionFunc
   903  	}{
   904  		{
   905  			name:            "Valid",
   906  			schema:          `{"type":"fixed", "name":"test", "namespace": "org.hamba.avro", "size": 12}`,
   907  			wantName:        "org.hamba.avro.test",
   908  			wantFingerprint: [32]uint8{0x8c, 0x9e, 0xcb, 0x4, 0x83, 0x2f, 0x3b, 0xa7, 0x58, 0x85, 0x9, 0x99, 0x41, 0xe, 0xbf, 0xd4, 0x7, 0xc7, 0x87, 0x4f, 0x8a, 0x12, 0xf4, 0xd0, 0x7f, 0x45, 0xdd, 0xaa, 0x10, 0x6b, 0x2f, 0xb3},
   909  			wantErr:         require.NoError,
   910  		},
   911  		{
   912  			name:    "Invalid Name",
   913  			schema:  `{"type":"fixed", "name":"test+", "namespace": "org.hamba.avro", "size": 12}`,
   914  			wantErr: require.Error,
   915  		},
   916  		{
   917  			name:    "Empty Name",
   918  			schema:  `{"type":"fixed", "name":"", "namespace": "org.hamba.avro", "size": 12}`,
   919  			wantErr: require.Error,
   920  		},
   921  		{
   922  			name:    "No Name",
   923  			schema:  `{"type":"fixed", "namespace": "org.hamba.avro", "size": 12}`,
   924  			wantErr: require.Error,
   925  		},
   926  		{
   927  			name:    "Invalid Namespace",
   928  			schema:  `{"type":"fixed", "name":"test", "namespace": "org.hamba.avro+", "size": 12}`,
   929  			wantErr: require.Error,
   930  		},
   931  		{
   932  			name:    "Empty Namespace",
   933  			schema:  `{"type":"fixed", "name":"test", "namespace": "", "size": 12}`,
   934  			wantErr: require.Error,
   935  		},
   936  		{
   937  			name:    "No Size",
   938  			schema:  `{"type":"fixed", "name":"test", "namespace": "org.hamba.avro"}`,
   939  			wantErr: require.Error,
   940  		},
   941  		{
   942  			name:    "Invalid Size Type",
   943  			schema:  `{"type":"fixed", "name":"test", "namespace": "org.hamba.avro", "size": "test"}`,
   944  			wantErr: require.Error,
   945  		},
   946  	}
   947  
   948  	for _, test := range tests {
   949  		test := test
   950  		t.Run(test.name, func(t *testing.T) {
   951  			t.Parallel()
   952  
   953  			schema, err := avro.ParseWithCache(test.schema, "", &avro.SchemaCache{})
   954  
   955  			test.wantErr(t, err)
   956  			if test.wantFingerprint != [32]byte{} {
   957  				assert.Equal(t, avro.Fixed, schema.Type())
   958  				named := schema.(avro.NamedSchema)
   959  				assert.Equal(t, test.wantName, named.FullName())
   960  				assert.Equal(t, test.wantFingerprint, named.Fingerprint())
   961  			}
   962  		})
   963  	}
   964  }
   965  
   966  func TestFixedSchema_HandlesProps(t *testing.T) {
   967  	schm := `{"type":"fixed", "name":"test", "namespace": "org.hamba.avro", "size": 12, "foo":"bar"}`
   968  
   969  	s, err := avro.Parse(schm)
   970  
   971  	require.NoError(t, err)
   972  	assert.Equal(t, avro.Fixed, s.Type())
   973  	assert.Equal(t, "bar", s.(*avro.FixedSchema).Prop("foo"))
   974  }
   975  
   976  func TestSchema_LogicalTypes(t *testing.T) {
   977  	tests := []struct {
   978  		name            string
   979  		schema          string
   980  		wantType        avro.Type
   981  		wantLogical     bool
   982  		wantLogicalType avro.LogicalType
   983  		assertFn        func(t *testing.T, ls avro.LogicalSchema)
   984  	}{
   985  		{
   986  			name:        "Invalid",
   987  			schema:      `{"type": "int", "logicalType": "test"}`,
   988  			wantType:    avro.Int,
   989  			wantLogical: false,
   990  		},
   991  		{
   992  			name:            "Date",
   993  			schema:          `{"type": "int", "logicalType": "date"}`,
   994  			wantType:        avro.Int,
   995  			wantLogical:     true,
   996  			wantLogicalType: avro.Date,
   997  		},
   998  		{
   999  			name:            "Time Millis",
  1000  			schema:          `{"type": "int", "logicalType": "time-millis"}`,
  1001  			wantType:        avro.Int,
  1002  			wantLogical:     true,
  1003  			wantLogicalType: avro.TimeMillis,
  1004  		},
  1005  		{
  1006  			name:            "Time Micros",
  1007  			schema:          `{"type": "long", "logicalType": "time-micros"}`,
  1008  			wantType:        avro.Long,
  1009  			wantLogical:     true,
  1010  			wantLogicalType: avro.TimeMicros,
  1011  		},
  1012  		{
  1013  			name:            "Timestamp Millis",
  1014  			schema:          `{"type": "long", "logicalType": "timestamp-millis"}`,
  1015  			wantType:        avro.Long,
  1016  			wantLogical:     true,
  1017  			wantLogicalType: avro.TimestampMillis,
  1018  		},
  1019  		{
  1020  			name:            "Timestamp Micros",
  1021  			schema:          `{"type": "long", "logicalType": "timestamp-micros"}`,
  1022  			wantType:        avro.Long,
  1023  			wantLogical:     true,
  1024  			wantLogicalType: avro.TimestampMicros,
  1025  		},
  1026  		{
  1027  			name:            "Local Timestamp Millis",
  1028  			schema:          `{"type": "long", "logicalType": "local-timestamp-millis"}`,
  1029  			wantType:        avro.Long,
  1030  			wantLogical:     true,
  1031  			wantLogicalType: avro.LocalTimestampMillis,
  1032  		},
  1033  		{
  1034  			name:            "Local Timestamp Micros",
  1035  			schema:          `{"type": "long", "logicalType": "local-timestamp-micros"}`,
  1036  			wantType:        avro.Long,
  1037  			wantLogical:     true,
  1038  			wantLogicalType: avro.LocalTimestampMicros,
  1039  		},
  1040  		{
  1041  			name:            "UUID",
  1042  			schema:          `{"type": "string", "logicalType": "uuid"}`,
  1043  			wantType:        avro.String,
  1044  			wantLogical:     true,
  1045  			wantLogicalType: avro.UUID,
  1046  		},
  1047  		{
  1048  			name:            "Duration",
  1049  			schema:          `{"type": "fixed", "name":"test", "size": 12, "logicalType": "duration"}`,
  1050  			wantType:        avro.Fixed,
  1051  			wantLogical:     true,
  1052  			wantLogicalType: avro.Duration,
  1053  		},
  1054  		{
  1055  			name:        "Invalid Duration",
  1056  			schema:      `{"type": "fixed", "name":"test", "size": 11, "logicalType": "duration"}`,
  1057  			wantType:    avro.Fixed,
  1058  			wantLogical: false,
  1059  		},
  1060  		{
  1061  			name:            "Bytes Decimal",
  1062  			schema:          `{"type": "bytes", "logicalType": "decimal", "precision": 4, "scale": 2}`,
  1063  			wantType:        avro.Bytes,
  1064  			wantLogical:     true,
  1065  			wantLogicalType: avro.Decimal,
  1066  			assertFn: func(t *testing.T, ls avro.LogicalSchema) {
  1067  				dec, ok := ls.(*avro.DecimalLogicalSchema)
  1068  				require.True(t, ok)
  1069  				assert.Equal(t, 4, dec.Precision())
  1070  				assert.Equal(t, 2, dec.Scale())
  1071  			},
  1072  		},
  1073  		{
  1074  			name:            "Bytes Decimal No Scale",
  1075  			schema:          `{"type": "bytes", "logicalType": "decimal", "precision": 4}`,
  1076  			wantType:        avro.Bytes,
  1077  			wantLogical:     true,
  1078  			wantLogicalType: avro.Decimal,
  1079  			assertFn: func(t *testing.T, ls avro.LogicalSchema) {
  1080  				dec, ok := ls.(*avro.DecimalLogicalSchema)
  1081  				require.True(t, ok)
  1082  				assert.Equal(t, 4, dec.Precision())
  1083  				assert.Equal(t, 0, dec.Scale())
  1084  			},
  1085  		},
  1086  		{
  1087  			name:        "Bytes Decimal Negative Precision",
  1088  			schema:      `{"type": "bytes", "logicalType": "decimal", "precision": 0}`,
  1089  			wantType:    avro.Bytes,
  1090  			wantLogical: false,
  1091  		},
  1092  		{
  1093  			name:        "Bytes Decimal Negative Scale",
  1094  			schema:      `{"type": "bytes", "logicalType": "decimal", "precision": 1, "scale": -1}`,
  1095  			wantType:    avro.Bytes,
  1096  			wantLogical: false,
  1097  		},
  1098  		{
  1099  			name:        "Bytes Decimal Scale Larger Than Precision",
  1100  			schema:      `{"type": "bytes", "logicalType": "decimal", "precision": 4, "scale": 6}`,
  1101  			wantType:    avro.Bytes,
  1102  			wantLogical: false,
  1103  		},
  1104  		{
  1105  			name:            "Fixed Decimal",
  1106  			schema:          `{"type": "fixed", "name":"test", "size": 12, "logicalType": "decimal", "precision": 4, "scale": 2}`,
  1107  			wantType:        avro.Fixed,
  1108  			wantLogical:     true,
  1109  			wantLogicalType: avro.Decimal,
  1110  			assertFn: func(t *testing.T, ls avro.LogicalSchema) {
  1111  				dec, ok := ls.(*avro.DecimalLogicalSchema)
  1112  				require.True(t, ok)
  1113  				assert.Equal(t, 4, dec.Precision())
  1114  				assert.Equal(t, 2, dec.Scale())
  1115  			},
  1116  		},
  1117  		{
  1118  			name:            "Fixed Decimal No Scale",
  1119  			schema:          `{"type": "fixed", "name":"test", "size": 12, "logicalType": "decimal", "precision": 4}`,
  1120  			wantType:        avro.Fixed,
  1121  			wantLogical:     true,
  1122  			wantLogicalType: avro.Decimal,
  1123  			assertFn: func(t *testing.T, ls avro.LogicalSchema) {
  1124  				dec, ok := ls.(*avro.DecimalLogicalSchema)
  1125  				require.True(t, ok)
  1126  				assert.Equal(t, 4, dec.Precision())
  1127  				assert.Equal(t, 0, dec.Scale())
  1128  			},
  1129  		},
  1130  		{
  1131  			name:        "Fixed Decimal Negative Precision",
  1132  			schema:      `{"type": "fixed", "name":"test", "size": 12, "logicalType": "decimal", "precision": 0}`,
  1133  			wantType:    avro.Fixed,
  1134  			wantLogical: false,
  1135  		},
  1136  		{
  1137  			name:        "Fixed Decimal Precision Too Large",
  1138  			schema:      `{"type": "fixed", "name":"test", "size": 4, "logicalType": "decimal", "precision": 10}`,
  1139  			wantType:    avro.Fixed,
  1140  			wantLogical: false,
  1141  		},
  1142  		{
  1143  			name:        "Fixed Decimal Scale Larger Than Precision",
  1144  			schema:      `{"type": "fixed", "name":"test", "size": 12, "logicalType": "decimal", "precision": 4, "scale": 6}`,
  1145  			wantType:    avro.Fixed,
  1146  			wantLogical: false,
  1147  		},
  1148  	}
  1149  
  1150  	for _, test := range tests {
  1151  		test := test
  1152  		t.Run(test.name, func(t *testing.T) {
  1153  			t.Parallel()
  1154  
  1155  			schema, err := avro.ParseWithCache(test.schema, "", &avro.SchemaCache{})
  1156  			require.NoError(t, err)
  1157  
  1158  			assert.Equal(t, test.wantType, schema.Type())
  1159  
  1160  			lts, ok := schema.(avro.LogicalTypeSchema)
  1161  			if !ok {
  1162  				assert.Fail(t, "logical type schema expected")
  1163  				return
  1164  			}
  1165  
  1166  			ls := lts.Logical()
  1167  			require.Equal(t, test.wantLogical, ls != nil)
  1168  			if !test.wantLogical {
  1169  				return
  1170  			}
  1171  
  1172  			assert.Equal(t, test.wantLogicalType, ls.Type())
  1173  
  1174  			if test.assertFn != nil {
  1175  				test.assertFn(t, ls)
  1176  			}
  1177  		})
  1178  	}
  1179  }
  1180  
  1181  func TestSchema_FingerprintUsing(t *testing.T) {
  1182  	tests := []struct {
  1183  		name   string
  1184  		schema string
  1185  		typ    avro.FingerprintType
  1186  		want   []byte
  1187  	}{
  1188  
  1189  		{
  1190  			name:   "Null CRC64",
  1191  			schema: "null",
  1192  			typ:    avro.CRC64Avro,
  1193  			want:   []byte{0x63, 0xdd, 0x24, 0xe7, 0xcc, 0x25, 0x8f, 0x8a},
  1194  		},
  1195  		{
  1196  			name:   "Null MD5",
  1197  			schema: "null",
  1198  			typ:    avro.MD5,
  1199  			want:   []byte{0x9b, 0x41, 0xef, 0x67, 0x65, 0x1c, 0x18, 0x48, 0x8a, 0x8b, 0x8, 0xbb, 0x67, 0xc7, 0x56, 0x99},
  1200  		},
  1201  		{
  1202  			name:   "Null SHA256",
  1203  			schema: "null",
  1204  			typ:    avro.SHA256,
  1205  			want:   []byte{0xf0, 0x72, 0xcb, 0xec, 0x3b, 0xf8, 0x84, 0x18, 0x71, 0xd4, 0x28, 0x42, 0x30, 0xc5, 0xe9, 0x83, 0xdc, 0x21, 0x1a, 0x56, 0x83, 0x7a, 0xed, 0x86, 0x24, 0x87, 0x14, 0x8f, 0x94, 0x7d, 0x1a, 0x1f},
  1206  		},
  1207  		{
  1208  			name:   "Primitive CRC64",
  1209  			schema: "string",
  1210  			typ:    avro.CRC64Avro,
  1211  			want:   []byte{0x8f, 0x1, 0x48, 0x72, 0x63, 0x45, 0x3, 0xc7},
  1212  		},
  1213  		{
  1214  			name:   "Record CRC64",
  1215  			schema: `{"type":"record", "name":"test", "namespace": "org.hamba.avro", "doc": "docs", "fields":[{"name": "field", "type": "int"}]}`,
  1216  			typ:    avro.CRC64Avro,
  1217  			want:   []byte{0xaf, 0x30, 0x30, 0xf0, 0x1c, 0x99, 0x76, 0xda},
  1218  		},
  1219  		{
  1220  			name:   "Enum CRC64",
  1221  			schema: `{"type":"enum", "name":"test", "namespace": "org.hamba.avro", "symbols":["TEST"]}`,
  1222  			typ:    avro.CRC64Avro,
  1223  			want:   []byte{0xc, 0xb0, 0xa2, 0xa6, 0x5f, 0x96, 0x8, 0xd1},
  1224  		},
  1225  		{
  1226  			name:   "Array CRC64",
  1227  			schema: `{"type":"array", "items": "int"}`,
  1228  			typ:    avro.CRC64Avro,
  1229  			want:   []byte{0x52, 0x2b, 0x81, 0x4f, 0xc9, 0x63, 0xb4, 0xbe},
  1230  		},
  1231  		{
  1232  			name:   "Map CRC64",
  1233  			schema: `{"type":"map", "values": "int"}`,
  1234  			typ:    avro.CRC64Avro,
  1235  			want:   []byte{0xdb, 0x39, 0xe2, 0xc2, 0x53, 0x4c, 0x89, 0x73},
  1236  		},
  1237  		{
  1238  			name:   "Union CRC64",
  1239  			schema: `["null", "int"]`,
  1240  			typ:    avro.CRC64Avro,
  1241  			want:   []byte{0xd5, 0x1c, 0xc0, 0x92, 0x2b, 0x46, 0xb1, 0xd7},
  1242  		},
  1243  		{
  1244  			name:   "Fixed CRC64",
  1245  			schema: `{"type":"fixed", "name":"test", "namespace": "org.hamba.avro", "size": 12}`,
  1246  			typ:    avro.CRC64Avro,
  1247  			want:   []byte{0x1, 0x7c, 0x1f, 0x7f, 0xa7, 0x6d, 0xa0, 0xa1},
  1248  		},
  1249  	}
  1250  
  1251  	for _, test := range tests {
  1252  		test := test
  1253  		t.Run(test.name, func(t *testing.T) {
  1254  			t.Parallel()
  1255  
  1256  			schema, err := avro.ParseWithCache(test.schema, "", &avro.SchemaCache{})
  1257  			require.NoError(t, err)
  1258  
  1259  			got, err := schema.FingerprintUsing(test.typ)
  1260  
  1261  			require.NoError(t, err)
  1262  			assert.Equal(t, test.want, got)
  1263  		})
  1264  	}
  1265  }
  1266  
  1267  func TestSchema_FingerprintUsingReference(t *testing.T) {
  1268  	schema := avro.MustParse(`
  1269  {
  1270     "type": "record",
  1271     "name": "valid_name",
  1272     "namespace": "org.hamba.avro",
  1273     "fields": [
  1274         {"name": "intField", "type": "int"},
  1275         {"name": "Ref", "type": "valid_name"}
  1276     ]
  1277  }
  1278  `)
  1279  
  1280  	got, err := schema.(*avro.RecordSchema).Fields()[1].Type().FingerprintUsing(avro.CRC64Avro)
  1281  
  1282  	require.NoError(t, err)
  1283  	assert.Equal(t, []byte{0xe1, 0xd6, 0x1e, 0x7c, 0x2f, 0xe3, 0x3c, 0x2b}, got)
  1284  }
  1285  
  1286  func TestSchema_FingerprintUsingInvalidType(t *testing.T) {
  1287  	schema := avro.MustParse("string")
  1288  
  1289  	_, err := schema.FingerprintUsing("test")
  1290  
  1291  	assert.Error(t, err)
  1292  }
  1293  
  1294  func TestSchema_MultiFile(t *testing.T) {
  1295  	got, err := avro.ParseFiles("testdata/superhero-part1.avsc", "testdata/superhero-part2.avsc")
  1296  
  1297  	require.NoError(t, err)
  1298  	want, err := avro.ParseFiles("testdata/superhero.avsc")
  1299  	require.NoError(t, err)
  1300  	assert.Equal(t, want, got)
  1301  }
  1302  
  1303  func TestSchema_DoesNotAllowDuplicateFullNames(t *testing.T) {
  1304  	schm := `
  1305  {
  1306     "type": "record",
  1307     "name": "Interop",
  1308     "namespace": "org.hamba.avro",
  1309     "fields": [
  1310         {
  1311             "name": "intField",
  1312             "type": {
  1313               "type": "record",
  1314               "name": "Interop",
  1315               "fields": []
  1316             }
  1317         }
  1318    ]
  1319  }
  1320  `
  1321  
  1322  	_, err := avro.Parse(schm)
  1323  
  1324  	assert.Error(t, err)
  1325  }
  1326  
  1327  func TestSchema_Interop(t *testing.T) {
  1328  	schm := `
  1329  {
  1330     "type": "record",
  1331     "name": "Interop",
  1332     "namespace": "org.hamba.avro",
  1333     "fields": [
  1334         {
  1335             "name": "intField",
  1336             "type": "int"
  1337         },
  1338         {
  1339             "name": "longField",
  1340             "type": "long"
  1341         },
  1342         {
  1343             "name": "stringField",
  1344             "type": "string"
  1345         },
  1346         {
  1347             "name": "boolField",
  1348             "type": "boolean"
  1349         },
  1350         {
  1351             "name": "floatField",
  1352             "type": "float"
  1353         },
  1354         {
  1355             "name": "doubleField",
  1356             "type": "double"
  1357         },
  1358         {
  1359             "name": "bytesField",
  1360             "type": "bytes"
  1361         },
  1362         {
  1363             "name": "nullField",
  1364             "type": "null"
  1365         },
  1366         {
  1367             "name": "arrayField",
  1368             "type": {
  1369                 "type": "array",
  1370                 "items": "double"
  1371             }
  1372         },
  1373         {
  1374             "name": "mapField",
  1375             "type": {
  1376                 "type": "map",
  1377                 "values": {
  1378                     "type": "record",
  1379                     "name": "Foo",
  1380                     "fields": [
  1381                         {
  1382                             "name": "label",
  1383                             "type": "string"
  1384                         }
  1385                     ]
  1386                 }
  1387             }
  1388         },
  1389         {
  1390             "name": "unionField",
  1391             "type": [
  1392                 "boolean",
  1393                 "double",
  1394                 {
  1395                     "type": "array",
  1396                     "items": "bytes"
  1397                 }
  1398             ]
  1399         },
  1400         {
  1401             "name": "enumField",
  1402             "type": {
  1403                 "type": "enum",
  1404                 "name": "Kind",
  1405                 "symbols": [
  1406                     "A",
  1407                     "B",
  1408                     "C"
  1409                 ]
  1410             }
  1411         },
  1412         {
  1413             "name": "fixedField",
  1414             "type": {
  1415                 "type": "fixed",
  1416                 "name": "MD5",
  1417                 "size": 16
  1418             }
  1419         },
  1420         {
  1421             "name": "recordField",
  1422             "type": {
  1423                 "type": "record",
  1424                 "name": "Node",
  1425                 "fields": [
  1426                     {
  1427                         "name": "label",
  1428                         "type": "string"
  1429                     },
  1430                     {
  1431                         "name": "child",
  1432                         "type": {"type": "org.hamba.avro.Node"}
  1433                     },
  1434                     {
  1435                         "name": "children",
  1436                         "type": {
  1437                             "type": "array",
  1438                             "items": "Node"
  1439                         }
  1440                     }
  1441                 ]
  1442             }
  1443         }
  1444     ]
  1445  }`
  1446  
  1447  	_, err := avro.Parse(schm)
  1448  
  1449  	assert.NoError(t, err)
  1450  }
  1451  
  1452  func TestSchema_ParseBackAndForth(t *testing.T) {
  1453  	schemaStr := `
  1454  {
  1455      "name": "a.b.rootType",
  1456      "type": "record",
  1457      "fields": [
  1458          {
  1459              "name": "someEnum1",
  1460              "type": {
  1461                  "name": "a.b.rootType.classEnum",
  1462                  "type": "enum",
  1463                  "symbols": ["A", "B", "C"]
  1464              }
  1465          },
  1466          {
  1467              "name": "someEnum2",
  1468              "type": "a.b.rootType.classEnum"
  1469          }
  1470      ]
  1471  }`
  1472  
  1473  	schema, err := avro.Parse(schemaStr)
  1474  	require.NoError(t, err)
  1475  
  1476  	b, err := json.Marshal(schema)
  1477  	require.NoError(t, err)
  1478  
  1479  	schema2, err := avro.ParseBytes(b)
  1480  
  1481  	assert.NoError(t, err)
  1482  	assert.Equal(t, schema, schema2)
  1483  }