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

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