github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/m3ninx/doc/document_test.go (about)

     1  // Copyright (c) 2018 Uber Technologies, Inc.
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to deal
     5  // in the Software without restriction, including without limitation the rights
     6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7  // copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in
    11  // all copies or substantial portions of the Software.
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    19  // THE SOFTWARE.
    20  
    21  package doc
    22  
    23  import (
    24  	"fmt"
    25  	"sort"
    26  	"testing"
    27  
    28  	"github.com/stretchr/testify/require"
    29  )
    30  
    31  func TestSortingFields(t *testing.T) {
    32  	tests := []struct {
    33  		name            string
    34  		input, expected Fields
    35  	}{
    36  		{
    37  			name:     "empty list should be unchanged",
    38  			input:    Fields{},
    39  			expected: Fields{},
    40  		},
    41  		{
    42  			name: "sorted fields should remain sorted",
    43  			input: Fields{
    44  				Field{
    45  					Name:  []byte("apple"),
    46  					Value: []byte("red"),
    47  				},
    48  				Field{
    49  					Name:  []byte("banana"),
    50  					Value: []byte("yellow"),
    51  				},
    52  			},
    53  			expected: Fields{
    54  				Field{
    55  					Name:  []byte("apple"),
    56  					Value: []byte("red"),
    57  				},
    58  				Field{
    59  					Name:  []byte("banana"),
    60  					Value: []byte("yellow"),
    61  				},
    62  			},
    63  		},
    64  		{
    65  			name: "unsorted fields should be sorted",
    66  			input: Fields{
    67  				Field{
    68  					Name:  []byte("banana"),
    69  					Value: []byte("yellow"),
    70  				},
    71  				Field{
    72  					Name:  []byte("apple"),
    73  					Value: []byte("red"),
    74  				},
    75  			},
    76  			expected: Fields{
    77  				Field{
    78  					Name:  []byte("apple"),
    79  					Value: []byte("red"),
    80  				},
    81  				Field{
    82  					Name:  []byte("banana"),
    83  					Value: []byte("yellow"),
    84  				},
    85  			},
    86  		},
    87  	}
    88  
    89  	for _, test := range tests {
    90  		t.Run(test.name, func(t *testing.T) {
    91  			actual := test.input
    92  			sort.Sort(actual)
    93  			require.Equal(t, test.expected, actual)
    94  		})
    95  	}
    96  }
    97  
    98  func TestDocumentGetField(t *testing.T) {
    99  	tests := []struct {
   100  		name        string
   101  		input       Metadata
   102  		fieldName   []byte
   103  		expectedOk  bool
   104  		expectedVal []byte
   105  	}{
   106  		{
   107  			name: "get existing field",
   108  			input: Metadata{
   109  				Fields: []Field{
   110  					Field{
   111  						Name:  []byte("apple"),
   112  						Value: []byte("red"),
   113  					},
   114  				},
   115  			},
   116  			fieldName:   []byte("apple"),
   117  			expectedOk:  true,
   118  			expectedVal: []byte("red"),
   119  		},
   120  		{
   121  			name: "get nonexisting field",
   122  			input: Metadata{
   123  				Fields: []Field{
   124  					Field{
   125  						Name:  []byte("apple"),
   126  						Value: []byte("red"),
   127  					},
   128  				},
   129  			},
   130  			fieldName:  []byte("banana"),
   131  			expectedOk: false,
   132  		},
   133  	}
   134  
   135  	for _, test := range tests {
   136  		t.Run(test.name, func(t *testing.T) {
   137  			val, ok := test.input.Get(test.fieldName)
   138  			if test.expectedOk {
   139  				require.True(t, ok)
   140  				require.Equal(t, test.expectedVal, val)
   141  				return
   142  			}
   143  			require.False(t, ok)
   144  		})
   145  	}
   146  }
   147  
   148  func TestDocumentCompare(t *testing.T) {
   149  	tests := []struct {
   150  		name     string
   151  		l, r     Metadata
   152  		expected int
   153  	}{
   154  		{
   155  			name:     "empty documents are equal",
   156  			l:        Metadata{},
   157  			r:        Metadata{},
   158  			expected: 0,
   159  		},
   160  		{
   161  			name: "documents with the same id and the same fields in the same order are equal",
   162  			l: Metadata{
   163  				ID: []byte("831992"),
   164  				Fields: []Field{
   165  					Field{
   166  						Name:  []byte("apple"),
   167  						Value: []byte("red"),
   168  					},
   169  					Field{
   170  						Name:  []byte("banana"),
   171  						Value: []byte("yellow"),
   172  					},
   173  				},
   174  			},
   175  			r: Metadata{
   176  				ID: []byte("831992"),
   177  				Fields: []Field{
   178  					Field{
   179  						Name:  []byte("apple"),
   180  						Value: []byte("red"),
   181  					},
   182  					Field{
   183  						Name:  []byte("banana"),
   184  						Value: []byte("yellow"),
   185  					},
   186  				},
   187  			},
   188  			expected: 0,
   189  		},
   190  		{
   191  			name: "documents are ordered by their IDs",
   192  			l: Metadata{
   193  				ID: []byte("831992"),
   194  				Fields: []Field{
   195  					Field{
   196  						Name:  []byte("banana"),
   197  						Value: []byte("yellow"),
   198  					},
   199  				},
   200  			},
   201  			r: Metadata{
   202  				ID: []byte("831991"),
   203  				Fields: []Field{
   204  					Field{
   205  						Name:  []byte("apple"),
   206  						Value: []byte("red"),
   207  					},
   208  				},
   209  			},
   210  			expected: 1,
   211  		},
   212  		{
   213  			name: "documents are ordered by their field names",
   214  			l: Metadata{
   215  				ID: []byte("831992"),
   216  				Fields: []Field{
   217  					Field{
   218  						Name:  []byte("banana"),
   219  						Value: []byte("yellow"),
   220  					},
   221  				},
   222  			},
   223  			r: Metadata{
   224  				ID: []byte("831992"),
   225  				Fields: []Field{
   226  					Field{
   227  						Name:  []byte("apple"),
   228  						Value: []byte("red"),
   229  					},
   230  				},
   231  			},
   232  			expected: 1,
   233  		},
   234  		{
   235  			name: "documents are ordered by their field values",
   236  			l: Metadata{
   237  				ID: []byte("831992"),
   238  				Fields: []Field{
   239  					Field{
   240  						Name:  []byte("apple"),
   241  						Value: []byte("green"),
   242  					},
   243  				},
   244  			},
   245  			r: Metadata{
   246  				ID: []byte("831992"),
   247  				Fields: []Field{
   248  					Field{
   249  						Name:  []byte("apple"),
   250  						Value: []byte("red"),
   251  					},
   252  				},
   253  			},
   254  			expected: -1,
   255  		},
   256  		{
   257  			name: "documents are ordered by their lengths",
   258  			l: Metadata{
   259  				ID: []byte("831992"),
   260  				Fields: []Field{
   261  					Field{
   262  						Name:  []byte("apple"),
   263  						Value: []byte("red"),
   264  					},
   265  				},
   266  			},
   267  			r: Metadata{
   268  				ID: []byte("831992"),
   269  				Fields: []Field{
   270  					Field{
   271  						Name:  []byte("apple"),
   272  						Value: []byte("red"),
   273  					},
   274  					Field{
   275  						Name:  []byte("banana"),
   276  						Value: []byte("yellow"),
   277  					},
   278  				},
   279  			},
   280  			expected: -1,
   281  		},
   282  	}
   283  
   284  	for _, test := range tests {
   285  		t.Run(test.name, func(t *testing.T) {
   286  			require.Equal(t, test.expected, test.l.Compare(test.r))
   287  		})
   288  	}
   289  }
   290  func TestDocumentEquality(t *testing.T) {
   291  	tests := []struct {
   292  		name     string
   293  		l, r     Metadata
   294  		expected bool
   295  	}{
   296  		{
   297  			name:     "empty documents are equal",
   298  			l:        Metadata{},
   299  			r:        Metadata{},
   300  			expected: true,
   301  		},
   302  		{
   303  			name: "documents with the same fields in the same order are equal",
   304  			l: Metadata{
   305  				ID: []byte("831992"),
   306  				Fields: []Field{
   307  					Field{
   308  						Name:  []byte("apple"),
   309  						Value: []byte("red"),
   310  					},
   311  					Field{
   312  						Name:  []byte("banana"),
   313  						Value: []byte("yellow"),
   314  					},
   315  				},
   316  			},
   317  			r: Metadata{
   318  				ID: []byte("831992"),
   319  				Fields: []Field{
   320  					Field{
   321  						Name:  []byte("apple"),
   322  						Value: []byte("red"),
   323  					},
   324  					Field{
   325  						Name:  []byte("banana"),
   326  						Value: []byte("yellow"),
   327  					},
   328  				},
   329  			},
   330  			expected: true,
   331  		},
   332  		{
   333  			name: "documents with the same fields in different order are equal",
   334  			l: Metadata{
   335  				ID: []byte("831992"),
   336  				Fields: []Field{
   337  					Field{
   338  						Name:  []byte("banana"),
   339  						Value: []byte("yellow"),
   340  					},
   341  					Field{
   342  						Name:  []byte("apple"),
   343  						Value: []byte("red"),
   344  					},
   345  				},
   346  			},
   347  			r: Metadata{
   348  				ID: []byte("831992"),
   349  				Fields: []Field{
   350  					Field{
   351  						Name:  []byte("apple"),
   352  						Value: []byte("red"),
   353  					},
   354  					Field{
   355  						Name:  []byte("banana"),
   356  						Value: []byte("yellow"),
   357  					},
   358  				},
   359  			},
   360  			expected: true,
   361  		},
   362  		{
   363  			name: "documents with different fields are unequal",
   364  			l: Metadata{
   365  				ID: []byte("831992"),
   366  				Fields: []Field{
   367  					Field{
   368  						Name:  []byte("apple"),
   369  						Value: []byte("red"),
   370  					},
   371  					Field{
   372  						Name:  []byte("banana"),
   373  						Value: []byte("yellow"),
   374  					},
   375  				},
   376  			},
   377  			r: Metadata{
   378  				ID: []byte("831992"),
   379  				Fields: []Field{
   380  					Field{
   381  						Name:  []byte("apple"),
   382  						Value: []byte("red"),
   383  					},
   384  					Field{
   385  						Name:  []byte("carrot"),
   386  						Value: []byte("orange"),
   387  					},
   388  				},
   389  			},
   390  			expected: false,
   391  		},
   392  		{
   393  			name: "documents with different IDs are unequal",
   394  			l: Metadata{
   395  				ID: []byte("831992"),
   396  				Fields: []Field{
   397  					Field{
   398  						Name:  []byte("apple"),
   399  						Value: []byte("red"),
   400  					},
   401  				},
   402  			},
   403  			r: Metadata{
   404  				ID: []byte("080292"),
   405  				Fields: []Field{
   406  					Field{
   407  						Name:  []byte("apple"),
   408  						Value: []byte("red"),
   409  					},
   410  				},
   411  			},
   412  			expected: false,
   413  		},
   414  	}
   415  
   416  	for _, test := range tests {
   417  		t.Run(test.name, func(t *testing.T) {
   418  			require.Equal(t, test.expected, test.l.Equal(test.r))
   419  		})
   420  	}
   421  }
   422  
   423  func TestDocumentValidation(t *testing.T) {
   424  	tests := []struct {
   425  		name        string
   426  		input       Metadata
   427  		expectedErr bool
   428  	}{
   429  		{
   430  			name:        "empty document",
   431  			input:       Metadata{},
   432  			expectedErr: true,
   433  		},
   434  		{
   435  			name: "empty document w/ ID",
   436  			input: Metadata{
   437  				ID: []byte("foobar"),
   438  			},
   439  			expectedErr: false,
   440  		},
   441  		{
   442  			name: "invalid UTF-8 in field name",
   443  			input: Metadata{
   444  				Fields: []Field{
   445  					Field{
   446  						Name:  []byte("\xff"),
   447  						Value: []byte("bar"),
   448  					},
   449  				},
   450  			},
   451  			expectedErr: true,
   452  		},
   453  		{
   454  			name: "invalid UTF-8 in field value",
   455  			input: Metadata{
   456  				Fields: []Field{
   457  					Field{
   458  						Name:  []byte("\xff"),
   459  						Value: []byte("bar"),
   460  					},
   461  				},
   462  			},
   463  			expectedErr: true,
   464  		},
   465  		{
   466  			name: "document contains field with reserved field name",
   467  			input: Metadata{
   468  				Fields: []Field{
   469  					Field{
   470  						Name:  []byte("apple"),
   471  						Value: []byte("red"),
   472  					},
   473  					Field{
   474  						Name:  IDReservedFieldName,
   475  						Value: []byte("123"),
   476  					},
   477  				},
   478  			},
   479  			expectedErr: true,
   480  		},
   481  		{
   482  			name: "valid document",
   483  			input: Metadata{
   484  				Fields: []Field{
   485  					Field{
   486  						Name:  []byte("apple"),
   487  						Value: []byte("red"),
   488  					},
   489  				},
   490  			},
   491  			expectedErr: false,
   492  		},
   493  	}
   494  
   495  	for _, test := range tests {
   496  		t.Run(test.name, func(t *testing.T) {
   497  			err := test.input.Validate()
   498  			if test.expectedErr {
   499  				require.Error(t, err)
   500  				return
   501  			}
   502  			require.NoError(t, err)
   503  		})
   504  	}
   505  }
   506  
   507  func TestDocumentHasID(t *testing.T) {
   508  	tests := []struct {
   509  		name     string
   510  		input    Metadata
   511  		expected bool
   512  	}{
   513  		{
   514  			name: "nil ID",
   515  			input: Metadata{
   516  				ID: nil,
   517  			},
   518  			expected: false,
   519  		},
   520  		{
   521  			name: "zero-length ID",
   522  			input: Metadata{
   523  				ID: make([]byte, 0, 16),
   524  			},
   525  			expected: false,
   526  		},
   527  		{
   528  			name: "valid ID",
   529  			input: Metadata{
   530  				ID: []byte("831992"),
   531  			},
   532  			expected: true,
   533  		},
   534  	}
   535  
   536  	for _, test := range tests {
   537  		t.Run(test.name, func(t *testing.T) {
   538  			require.Equal(t, test.expected, test.input.HasID())
   539  		})
   540  	}
   541  }
   542  
   543  func TestSortingDocuments(t *testing.T) {
   544  	tests := []struct {
   545  		name            string
   546  		input, expected Documents
   547  	}{
   548  		{
   549  			name: "unordered documents",
   550  			input: Documents{
   551  				Metadata{
   552  					ID: []byte("831992"),
   553  					Fields: []Field{
   554  						Field{
   555  							Name:  []byte("banana"),
   556  							Value: []byte("yellow"),
   557  						},
   558  					},
   559  				},
   560  				Metadata{
   561  					ID: []byte("831992"),
   562  					Fields: []Field{
   563  						Field{
   564  							Name:  []byte("apple"),
   565  							Value: []byte("red"),
   566  						},
   567  					},
   568  				},
   569  			},
   570  			expected: Documents{
   571  				Metadata{
   572  					ID: []byte("831992"),
   573  					Fields: []Field{
   574  						Field{
   575  							Name:  []byte("apple"),
   576  							Value: []byte("red"),
   577  						},
   578  					},
   579  				},
   580  				Metadata{
   581  					ID: []byte("831992"),
   582  					Fields: []Field{
   583  						Field{
   584  							Name:  []byte("banana"),
   585  							Value: []byte("yellow"),
   586  						},
   587  					},
   588  				},
   589  			},
   590  		},
   591  	}
   592  
   593  	for _, test := range tests {
   594  		t.Run(test.name, func(t *testing.T) {
   595  			actual := test.input
   596  			sort.Sort(actual)
   597  			require.Equal(t, len(test.expected), len(actual))
   598  			fmt.Println(actual)
   599  			for i := range test.expected {
   600  				require.True(t, test.expected[i].Equal(actual[i]))
   601  			}
   602  		})
   603  	}
   604  }