github.com/weaviate/weaviate@v1.24.6/adapters/repos/db/lsmkv/strategies_map_test.go (about)

     1  //                           _       _
     2  // __      _____  __ ___   ___  __ _| |_ ___
     3  // \ \ /\ / / _ \/ _` \ \ / / |/ _` | __/ _ \
     4  //  \ V  V /  __/ (_| |\ V /| | (_| | ||  __/
     5  //   \_/\_/ \___|\__,_| \_/ |_|\__,_|\__\___|
     6  //
     7  //  Copyright © 2016 - 2024 Weaviate B.V. All rights reserved.
     8  //
     9  //  CONTACT: hello@weaviate.io
    10  //
    11  
    12  package lsmkv
    13  
    14  import (
    15  	"testing"
    16  
    17  	"github.com/stretchr/testify/assert"
    18  	"github.com/stretchr/testify/require"
    19  )
    20  
    21  func TestMapEncoderDecoderJourney(t *testing.T) {
    22  	// this test first encodes the map pairs, then decodes them and replace
    23  	// duplicates, remove tombstones, etc.
    24  	type test struct {
    25  		name string
    26  		in   []MapPair
    27  		out  []MapPair
    28  	}
    29  
    30  	tests := []test{
    31  		{
    32  			name: "single pair",
    33  			in: []MapPair{
    34  				{
    35  					Key:   []byte("foo"),
    36  					Value: []byte("bar"),
    37  				},
    38  			},
    39  			out: []MapPair{
    40  				{
    41  					Key:   []byte("foo"),
    42  					Value: []byte("bar"),
    43  				},
    44  			},
    45  		},
    46  		{
    47  			name: "single pair, updated value",
    48  			in: []MapPair{
    49  				{
    50  					Key:   []byte("foo"),
    51  					Value: []byte("bar"),
    52  				},
    53  				{
    54  					Key:   []byte("foo"),
    55  					Value: []byte("bar2"),
    56  				},
    57  			},
    58  			out: []MapPair{
    59  				{
    60  					Key:   []byte("foo"),
    61  					Value: []byte("bar2"),
    62  				},
    63  			},
    64  		},
    65  		{
    66  			name: "single pair, tombstone added",
    67  			in: []MapPair{
    68  				{
    69  					Key:   []byte("foo"),
    70  					Value: []byte("bar"),
    71  				},
    72  				{
    73  					Key:       []byte("foo"),
    74  					Tombstone: true,
    75  				},
    76  			},
    77  			out: []MapPair{},
    78  		},
    79  		{
    80  			name: "single pair, tombstone added, same value added again",
    81  			in: []MapPair{
    82  				{
    83  					Key:   []byte("foo"),
    84  					Value: []byte("bar"),
    85  				},
    86  				{
    87  					Key:       []byte("foo"),
    88  					Tombstone: true,
    89  				},
    90  				{
    91  					Key:   []byte("foo"),
    92  					Value: []byte("bar2"),
    93  				},
    94  			},
    95  			out: []MapPair{
    96  				{
    97  					Key:   []byte("foo"),
    98  					Value: []byte("bar2"),
    99  				},
   100  			},
   101  		},
   102  		{
   103  			name: "multiple values, combination of updates and tombstones",
   104  			in: []MapPair{
   105  				{
   106  					Key:   []byte("foo"),
   107  					Value: []byte("never-updated"),
   108  				},
   109  				{
   110  					Key:   []byte("foo1"),
   111  					Value: []byte("bar1"),
   112  				},
   113  				{
   114  					Key:   []byte("foo2"),
   115  					Value: []byte("bar2"),
   116  				},
   117  				{
   118  					Key:   []byte("foo2"),
   119  					Value: []byte("bar2.2"),
   120  				},
   121  				{
   122  					Key:       []byte("foo1"),
   123  					Tombstone: true,
   124  				},
   125  				{
   126  					Key:   []byte("foo2"),
   127  					Value: []byte("bar2.3"),
   128  				},
   129  				{
   130  					Key:   []byte("foo1"),
   131  					Value: []byte("bar1.2"),
   132  				},
   133  			},
   134  			out: []MapPair{
   135  				{
   136  					Key:   []byte("foo"),
   137  					Value: []byte("never-updated"),
   138  				},
   139  				{
   140  					Key:   []byte("foo1"),
   141  					Value: []byte("bar1.2"),
   142  				},
   143  				{
   144  					Key:   []byte("foo2"),
   145  					Value: []byte("bar2.3"),
   146  				},
   147  			},
   148  		},
   149  	}
   150  
   151  	for _, test := range tests {
   152  		t.Run(test.name, func(t *testing.T) {
   153  			encoded := make([]value, len(test.in))
   154  			for i, kv := range test.in {
   155  				enc, err := newMapEncoder().Do(kv)
   156  				require.Nil(t, err)
   157  				encoded[i] = enc[0]
   158  			}
   159  			res, err := newMapDecoder().Do(encoded, false)
   160  			require.Nil(t, err)
   161  			// NOTE: we are accepting that the order can be lost on updates
   162  			assert.ElementsMatch(t, test.out, res)
   163  		})
   164  	}
   165  }
   166  
   167  func TestDecoderRemoveTombstones(t *testing.T) {
   168  	t.Run("single entry, no tombstones", func(t *testing.T) {
   169  		m := newMapDecoder()
   170  		input := mustEncode([]MapPair{
   171  			{
   172  				Key:   []byte("hello"),
   173  				Value: []byte("world"),
   174  			},
   175  		})
   176  
   177  		actual, err := m.doSimplified(input)
   178  		require.Nil(t, err)
   179  
   180  		expected := []MapPair{
   181  			{
   182  				Key:   []byte("hello"),
   183  				Value: []byte("world"),
   184  			},
   185  		}
   186  		assert.Equal(t, expected, actual)
   187  	})
   188  
   189  	t.Run("single entry, single tombstone", func(t *testing.T) {
   190  		m := newMapDecoder()
   191  		input := mustEncode([]MapPair{
   192  			{
   193  				Key:   []byte("hello"),
   194  				Value: []byte("world"),
   195  			},
   196  			{
   197  				Key:       []byte("hello"),
   198  				Tombstone: true,
   199  			},
   200  		})
   201  
   202  		actual, err := m.doSimplified(input)
   203  		require.Nil(t, err)
   204  
   205  		expected := []MapPair{}
   206  		assert.Equal(t, expected, actual)
   207  	})
   208  
   209  	t.Run("single entry, single tombstone, then read", func(t *testing.T) {
   210  		m := newMapDecoder()
   211  		input := mustEncode([]MapPair{
   212  			{
   213  				Key:   []byte("hello"),
   214  				Value: []byte("world"),
   215  			},
   216  			{
   217  				Key:       []byte("hello"),
   218  				Tombstone: true,
   219  			},
   220  			{
   221  				Key:   []byte("hello"),
   222  				Value: []byte("world"),
   223  			},
   224  		})
   225  
   226  		actual, err := m.doSimplified(input)
   227  		require.Nil(t, err)
   228  
   229  		expected := []MapPair{
   230  			{
   231  				Key:   []byte("hello"),
   232  				Value: []byte("world"),
   233  			},
   234  		}
   235  		assert.Equal(t, expected, actual)
   236  	})
   237  
   238  	t.Run("three entries, two tombstones at the end", func(t *testing.T) {
   239  		m := newMapDecoder()
   240  		input := mustEncode([]MapPair{
   241  			{
   242  				Key:   []byte("hello"),
   243  				Value: []byte("world"),
   244  			},
   245  			{
   246  				Key:   []byte("bonjour"),
   247  				Value: []byte("world"),
   248  			},
   249  			{
   250  				Key:   []byte("guten tag"),
   251  				Value: []byte("world"),
   252  			},
   253  			{
   254  				Key:       []byte("hello"),
   255  				Tombstone: true,
   256  			},
   257  			{
   258  				Key:       []byte("bonjour"),
   259  				Tombstone: true,
   260  			},
   261  		})
   262  
   263  		actual, err := m.doSimplified(input)
   264  		require.Nil(t, err)
   265  
   266  		expected := []MapPair{
   267  			{
   268  				Key:   []byte("guten tag"),
   269  				Value: []byte("world"),
   270  			},
   271  		}
   272  		assert.Equal(t, expected, actual)
   273  	})
   274  
   275  	t.Run("three entries, two tombstones at the end, then recreate the first", func(t *testing.T) {
   276  		m := newMapDecoder()
   277  		input := mustEncode([]MapPair{
   278  			{
   279  				Key:   []byte("hello"),
   280  				Value: []byte("world"),
   281  			},
   282  			{
   283  				Key:   []byte("bonjour"),
   284  				Value: []byte("world"),
   285  			},
   286  			{
   287  				Key:   []byte("guten tag"),
   288  				Value: []byte("world"),
   289  			},
   290  			{
   291  				Key:       []byte("hello"),
   292  				Tombstone: true,
   293  			},
   294  			{
   295  				Key:       []byte("bonjour"),
   296  				Tombstone: true,
   297  			},
   298  			{
   299  				Key:   []byte("bonjour"),
   300  				Value: []byte("world"),
   301  			},
   302  			{
   303  				Key:   []byte("hello"),
   304  				Value: []byte("world"),
   305  			},
   306  		})
   307  
   308  		actual, err := m.doSimplified(input)
   309  		require.Nil(t, err)
   310  
   311  		expected := []MapPair{
   312  			{
   313  				Key:   []byte("guten tag"),
   314  				Value: []byte("world"),
   315  			},
   316  			{
   317  				Key:   []byte("bonjour"),
   318  				Value: []byte("world"),
   319  			},
   320  			{
   321  				Key:   []byte("hello"),
   322  				Value: []byte("world"),
   323  			},
   324  		}
   325  		assert.Equal(t, expected, actual)
   326  	})
   327  }
   328  
   329  func mustEncode(kvs []MapPair) []value {
   330  	res, err := newMapEncoder().DoMulti(kvs)
   331  	if err != nil {
   332  		panic(err)
   333  	}
   334  
   335  	return res
   336  }
   337  
   338  func Test_MapPair_EncodingBytes(t *testing.T) {
   339  	kv := MapPair{
   340  		Key:   []byte("hello-world-key1"),
   341  		Value: []byte("this is the value ;-)"),
   342  	}
   343  
   344  	control, err := kv.Bytes()
   345  	assert.Nil(t, err)
   346  
   347  	encoded := make([]byte, kv.Size())
   348  	err = kv.EncodeBytes(encoded)
   349  	assert.Nil(t, err)
   350  
   351  	assert.Equal(t, control, encoded)
   352  }