github.com/flower-corp/rosedb@v1.1.2-0.20230117132829-21dc4f7b319a/strs_test.go (about)

     1  package rosedb
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"math"
     7  	"math/rand"
     8  	"path/filepath"
     9  	"reflect"
    10  	"sort"
    11  	"strconv"
    12  	"testing"
    13  	"time"
    14  
    15  	"github.com/stretchr/testify/assert"
    16  )
    17  
    18  func TestRoseDB_Set(t *testing.T) {
    19  	t.Run("default", func(t *testing.T) {
    20  		testRoseDBSet(t, FileIO, KeyOnlyMemMode)
    21  	})
    22  
    23  	t.Run("mmap", func(t *testing.T) {
    24  		testRoseDBSet(t, MMap, KeyOnlyMemMode)
    25  	})
    26  
    27  	t.Run("key-val-mem-mode", func(t *testing.T) {
    28  		testRoseDBSet(t, FileIO, KeyValueMemMode)
    29  	})
    30  }
    31  
    32  func TestRoseDB_Set_LogFileThreshold(t *testing.T) {
    33  	path := filepath.Join("/tmp", "rosedb")
    34  	opts := DefaultOptions(path)
    35  	opts.IoType = MMap
    36  	opts.LogFileSizeThreshold = 32 << 20
    37  	db, err := Open(opts)
    38  	assert.Nil(t, err)
    39  	defer destroyDB(db)
    40  
    41  	for i := 0; i < 600000; i++ {
    42  		err := db.Set(GetKey(i), GetValue128B())
    43  		assert.Nil(t, err)
    44  	}
    45  }
    46  
    47  func TestRoseDB_Get(t *testing.T) {
    48  	t.Run("default", func(t *testing.T) {
    49  		testRoseDBGet(t, FileIO, KeyOnlyMemMode)
    50  	})
    51  
    52  	t.Run("mmap", func(t *testing.T) {
    53  		testRoseDBGet(t, MMap, KeyOnlyMemMode)
    54  	})
    55  
    56  	t.Run("key-val-mem-mode", func(t *testing.T) {
    57  		testRoseDBGet(t, MMap, KeyValueMemMode)
    58  	})
    59  }
    60  
    61  func TestRoseDB_Get_LogFileThreshold(t *testing.T) {
    62  	path := filepath.Join("/tmp", "rosedb")
    63  	opts := DefaultOptions(path)
    64  	opts.IoType = MMap
    65  	opts.LogFileSizeThreshold = 32 << 20
    66  	db, err := Open(opts)
    67  	assert.Nil(t, err)
    68  	defer destroyDB(db)
    69  
    70  	writeCount := 600000
    71  	for i := 0; i <= writeCount; i++ {
    72  		err := db.Set(GetKey(i), GetValue128B())
    73  		assert.Nil(t, err)
    74  	}
    75  
    76  	rand.Seed(time.Now().Unix())
    77  	for i := 0; i < 10000; i++ {
    78  		key := GetKey(rand.Intn(writeCount))
    79  		v, err := db.Get(key)
    80  		assert.Nil(t, err)
    81  		assert.NotNil(t, v)
    82  	}
    83  }
    84  
    85  func testRoseDBSet(t *testing.T, ioType IOType, mode DataIndexMode) {
    86  	path := filepath.Join("/tmp", "rosedb")
    87  	opts := DefaultOptions(path)
    88  	opts.IoType = ioType
    89  	opts.IndexMode = mode
    90  	db, err := Open(opts)
    91  	assert.Nil(t, err)
    92  	defer destroyDB(db)
    93  
    94  	type args struct {
    95  		key   []byte
    96  		value []byte
    97  	}
    98  	tests := []struct {
    99  		name    string
   100  		db      *RoseDB
   101  		args    args
   102  		wantErr bool
   103  	}{
   104  		{
   105  			"nil-key", db, args{key: nil, value: []byte("val-1")}, false,
   106  		},
   107  		{
   108  			"nil-value", db, args{key: []byte("key-1"), value: nil}, false,
   109  		},
   110  		{
   111  			"normal", db, args{key: []byte("key-1111"), value: []byte("value-1111")}, false,
   112  		},
   113  	}
   114  	for _, tt := range tests {
   115  		t.Run(tt.name, func(t *testing.T) {
   116  			if err := tt.db.Set(tt.args.key, tt.args.value); (err != nil) != tt.wantErr {
   117  				t.Errorf("Set() error = %v, wantErr %v", err, tt.wantErr)
   118  			}
   119  		})
   120  	}
   121  }
   122  
   123  func testRoseDBGet(t *testing.T, ioType IOType, mode DataIndexMode) {
   124  	path := filepath.Join("/tmp", "rosedb")
   125  	opts := DefaultOptions(path)
   126  	opts.IoType = ioType
   127  	opts.IndexMode = mode
   128  	db, err := Open(opts)
   129  	assert.Nil(t, err)
   130  	defer destroyDB(db)
   131  
   132  	db.Set(nil, []byte("v-1111"))
   133  	db.Set([]byte("k-1"), []byte("v-1"))
   134  	db.Set([]byte("k-2"), []byte("v-2"))
   135  	db.Set([]byte("k-3"), []byte("v-3"))
   136  	db.Set([]byte("k-3"), []byte("v-333"))
   137  
   138  	type args struct {
   139  		key []byte
   140  	}
   141  	tests := []struct {
   142  		name    string
   143  		db      *RoseDB
   144  		args    args
   145  		want    []byte
   146  		wantErr bool
   147  	}{
   148  		{
   149  			"nil-key", db, args{key: nil}, nil, true,
   150  		},
   151  		{
   152  			"normal", db, args{key: []byte("k-1")}, []byte("v-1"), false,
   153  		},
   154  		{
   155  			"normal-rewrite", db, args{key: []byte("k-3")}, []byte("v-333"), false,
   156  		},
   157  	}
   158  	for _, tt := range tests {
   159  		t.Run(tt.name, func(t *testing.T) {
   160  			got, err := tt.db.Get(tt.args.key)
   161  			if (err != nil) != tt.wantErr {
   162  				t.Errorf("Get() error = %v, wantErr %v", err, tt.wantErr)
   163  				return
   164  			}
   165  			if !reflect.DeepEqual(got, tt.want) {
   166  				t.Errorf("Get() got = %v, want %v", got, tt.want)
   167  			}
   168  		})
   169  	}
   170  }
   171  
   172  func TestRoseDB_MGet(t *testing.T) {
   173  	t.Run("default", func(t *testing.T) {
   174  		testRoseDBMGet(t, FileIO, KeyOnlyMemMode)
   175  	})
   176  
   177  	t.Run("mmap", func(t *testing.T) {
   178  		testRoseDBMGet(t, MMap, KeyOnlyMemMode)
   179  	})
   180  
   181  	t.Run("key-val-mem-mode", func(t *testing.T) {
   182  		testRoseDBMGet(t, MMap, KeyValueMemMode)
   183  	})
   184  }
   185  
   186  func testRoseDBMGet(t *testing.T, ioType IOType, mode DataIndexMode) {
   187  	path := filepath.Join("/tmp", "rosedb")
   188  	opts := DefaultOptions(path)
   189  	opts.IoType = ioType
   190  	opts.IndexMode = mode
   191  	db, err := Open(opts)
   192  	assert.Nil(t, err)
   193  	defer destroyDB(db)
   194  
   195  	db.Set(nil, []byte("v-1111"))
   196  	db.Set([]byte("k-1"), []byte("v-1"))
   197  	db.Set([]byte("k-2"), []byte("v-2"))
   198  	db.Set([]byte("k-3"), []byte("v-3"))
   199  	db.Set([]byte("k-3"), []byte("v-333"))
   200  	db.Set([]byte("k-4"), []byte("v-4"))
   201  	db.Set([]byte("k-5"), []byte("v-5"))
   202  
   203  	type args struct {
   204  		keys [][]byte
   205  	}
   206  
   207  	tests := []struct {
   208  		name    string
   209  		db      *RoseDB
   210  		args    args
   211  		want    [][]byte
   212  		wantErr bool
   213  	}{
   214  		{
   215  			name:    "nil-key",
   216  			db:      db,
   217  			args:    args{keys: [][]byte{nil}},
   218  			want:    [][]byte{nil},
   219  			wantErr: false,
   220  		},
   221  		{
   222  			name:    "normal",
   223  			db:      db,
   224  			args:    args{keys: [][]byte{[]byte("k-1")}},
   225  			want:    [][]byte{[]byte("v-1")},
   226  			wantErr: false,
   227  		},
   228  		{
   229  			name:    "normal-rewrite",
   230  			db:      db,
   231  			args:    args{keys: [][]byte{[]byte("k-1"), []byte("k-3")}},
   232  			want:    [][]byte{[]byte("v-1"), []byte("v-333")},
   233  			wantErr: false,
   234  		},
   235  		{
   236  			name: "multiple key",
   237  			db:   db,
   238  			args: args{keys: [][]byte{
   239  				[]byte("k-1"),
   240  				[]byte("k-2"),
   241  				[]byte("k-4"),
   242  				[]byte("k-5"),
   243  			}},
   244  			want: [][]byte{
   245  				[]byte("v-1"),
   246  				[]byte("v-2"),
   247  				[]byte("v-4"),
   248  				[]byte("v-5"),
   249  			},
   250  			wantErr: false,
   251  		},
   252  		{
   253  			name:    "missed one key",
   254  			db:      db,
   255  			args:    args{keys: [][]byte{[]byte("missed-k")}},
   256  			want:    [][]byte{nil},
   257  			wantErr: false,
   258  		},
   259  		{
   260  			name: "missed multiple keys",
   261  			db:   db,
   262  			args: args{keys: [][]byte{
   263  				[]byte("missed-k-1"),
   264  				[]byte("missed-k-2"),
   265  				[]byte("missed-k-3"),
   266  			}},
   267  			want:    [][]byte{nil, nil, nil},
   268  			wantErr: false,
   269  		},
   270  		{
   271  			name: "missed one key in multiple keys",
   272  			db:   db,
   273  			args: args{keys: [][]byte{
   274  				[]byte("k-1"),
   275  				[]byte("missed-k-1"),
   276  				[]byte("k-2"),
   277  			}},
   278  			want:    [][]byte{[]byte("v-1"), nil, []byte("v-2")},
   279  			wantErr: false,
   280  		},
   281  		{
   282  			name:    "nil key in multiple keys",
   283  			db:      db,
   284  			args:    args{keys: [][]byte{nil, []byte("k-1")}},
   285  			want:    [][]byte{nil, []byte("v-1")},
   286  			wantErr: false,
   287  		},
   288  		{
   289  			name:    "empty key",
   290  			db:      db,
   291  			args:    args{keys: [][]byte{}},
   292  			wantErr: true,
   293  		},
   294  	}
   295  
   296  	for _, tt := range tests {
   297  		t.Run(tt.name, func(t *testing.T) {
   298  			got, err := tt.db.MGet(tt.args.keys)
   299  			if (err != nil) != tt.wantErr {
   300  				t.Errorf("MGet() error = %v, wantErr %v", err, tt.wantErr)
   301  				return
   302  			}
   303  			if !reflect.DeepEqual(got, tt.want) {
   304  				t.Errorf("MGet() got = %v, want %v", got, tt.want)
   305  			}
   306  		})
   307  	}
   308  }
   309  
   310  func TestRoseDB_GetRange(t *testing.T) {
   311  	t.Run("default", func(t *testing.T) {
   312  		testRoseDBGetRange(t, FileIO, KeyOnlyMemMode)
   313  	})
   314  
   315  	t.Run("mmap", func(t *testing.T) {
   316  		testRoseDBGetRange(t, MMap, KeyOnlyMemMode)
   317  	})
   318  
   319  	t.Run("key-val-mem-mode", func(t *testing.T) {
   320  		testRoseDBGetRange(t, MMap, KeyValueMemMode)
   321  	})
   322  }
   323  
   324  func testRoseDBGetRange(t *testing.T, ioType IOType, mode DataIndexMode) {
   325  	path := filepath.Join("/tmp", "rosedb")
   326  	opts := DefaultOptions(path)
   327  	opts.IoType = ioType
   328  	opts.IndexMode = mode
   329  	db, err := Open(opts)
   330  	assert.Nil(t, err)
   331  	defer destroyDB(db)
   332  
   333  	key := []byte("key")
   334  	val := []byte("test-val")
   335  	db.Set(key, val)
   336  
   337  	keyEmpty := []byte("key-empty")
   338  	valEmpty := []byte("")
   339  	db.Set(keyEmpty, valEmpty)
   340  
   341  	type args struct {
   342  		key   []byte
   343  		start int
   344  		end   int
   345  	}
   346  	tests := []struct {
   347  		name    string
   348  		args    args
   349  		want    []byte
   350  		wantErr bool
   351  	}{
   352  		{
   353  			name:    "key not found",
   354  			args:    args{key: []byte("missing key"), start: 0, end: 7},
   355  			want:    nil,
   356  			wantErr: true,
   357  		},
   358  		{
   359  			name:    "empty",
   360  			args:    args{key: keyEmpty, start: 0, end: 0},
   361  			want:    valEmpty,
   362  			wantErr: false,
   363  		},
   364  		{
   365  			name:    "all strings",
   366  			args:    args{key: key, start: 0, end: 7},
   367  			want:    val,
   368  			wantErr: false,
   369  		},
   370  		{
   371  			name:    "trim 1 length",
   372  			args:    args{key: key, start: 1, end: 6},
   373  			want:    []byte("est-va"),
   374  			wantErr: false,
   375  		},
   376  		{
   377  			name:    "all strings with end neg",
   378  			args:    args{key: key, start: 0, end: -1},
   379  			want:    val,
   380  			wantErr: false,
   381  		},
   382  		{
   383  			name:    "start neg",
   384  			args:    args{key: key, start: -1, end: 7},
   385  			want:    []byte("l"),
   386  			wantErr: false,
   387  		},
   388  		{
   389  			name:    "over start neg limit",
   390  			args:    args{key: key, start: -9, end: 0},
   391  			want:    []byte("t"),
   392  			wantErr: false,
   393  		},
   394  		{
   395  			name:    "end neg",
   396  			args:    args{key: key, start: 7, end: -1},
   397  			want:    []byte("l"),
   398  			wantErr: false,
   399  		},
   400  		{
   401  			name:    "over end neg limit",
   402  			args:    args{key: key, start: 0, end: -9},
   403  			want:    []byte("t"),
   404  			wantErr: false,
   405  		},
   406  		{
   407  			name:    "over end limit",
   408  			args:    args{key: key, start: 0, end: 8},
   409  			want:    val,
   410  			wantErr: false,
   411  		},
   412  		{
   413  			name:    "over start limit",
   414  			args:    args{key: key, start: 8, end: 8},
   415  			want:    []byte{},
   416  			wantErr: false,
   417  		},
   418  		{
   419  			name:    "start over end",
   420  			args:    args{key: key, start: 1, end: 0},
   421  			want:    []byte{},
   422  			wantErr: false,
   423  		},
   424  		{
   425  			name:    "start and end both are positive numbers, and start > end",
   426  			args:    args{key: key, start: 3, end: 1},
   427  			want:    []byte{},
   428  			wantErr: false,
   429  		},
   430  	}
   431  	for _, tt := range tests {
   432  		t.Run(tt.name, func(t *testing.T) {
   433  			got, err := db.GetRange(tt.args.key, tt.args.start, tt.args.end)
   434  			if (err != nil) != tt.wantErr {
   435  				t.Errorf("GetRange() error = %v, wantErr %v", err, tt.wantErr)
   436  				return
   437  			}
   438  			if !reflect.DeepEqual(got, tt.want) {
   439  				t.Errorf("GetRange() got = %v, want %v", got, tt.want)
   440  			}
   441  		})
   442  	}
   443  }
   444  
   445  func TestRoseDB_Delete(t *testing.T) {
   446  	t.Run("default", func(t *testing.T) {
   447  		testRoseDBDelete(t, FileIO, KeyOnlyMemMode)
   448  	})
   449  
   450  	t.Run("mmap", func(t *testing.T) {
   451  		testRoseDBDelete(t, MMap, KeyOnlyMemMode)
   452  	})
   453  
   454  	t.Run("key-val-mem-mode", func(t *testing.T) {
   455  		testRoseDBDelete(t, MMap, KeyValueMemMode)
   456  	})
   457  }
   458  
   459  func TestRoseDB_Delete_MultiFiles(t *testing.T) {
   460  	path := filepath.Join("/tmp", "rosedb")
   461  	opts := DefaultOptions(path)
   462  	opts.IoType = FileIO
   463  	opts.LogFileSizeThreshold = 32 << 20
   464  	db, err := Open(opts)
   465  	assert.Nil(t, err)
   466  	defer destroyDB(db)
   467  
   468  	writeCount := 600000
   469  	for i := 0; i <= writeCount; i++ {
   470  		err := db.Set(GetKey(i), GetValue128B())
   471  		assert.Nil(t, err)
   472  	}
   473  
   474  	var deletedKeys [][]byte
   475  	rand.Seed(time.Now().Unix())
   476  	for i := 0; i < 10000; i++ {
   477  		key := GetKey(rand.Intn(writeCount))
   478  		err := db.Delete(key)
   479  		assert.Nil(t, err)
   480  		deletedKeys = append(deletedKeys, key)
   481  	}
   482  	for _, k := range deletedKeys {
   483  		_, err := db.Get(k)
   484  		assert.Equal(t, ErrKeyNotFound, err)
   485  	}
   486  }
   487  
   488  func testRoseDBDelete(t *testing.T, ioType IOType, mode DataIndexMode) {
   489  	path := filepath.Join("/tmp", "rosedb")
   490  	opts := DefaultOptions(path)
   491  	opts.IoType = ioType
   492  	opts.IndexMode = mode
   493  	db, err := Open(opts)
   494  	assert.Nil(t, err)
   495  	defer destroyDB(db)
   496  
   497  	db.Set(nil, []byte("v-1111"))
   498  	db.Set([]byte("k-1"), []byte("v-1"))
   499  	db.Set([]byte("k-3"), []byte("v-3"))
   500  	db.Set([]byte("k-3"), []byte("v-333"))
   501  
   502  	type args struct {
   503  		key []byte
   504  	}
   505  	tests := []struct {
   506  		name    string
   507  		db      *RoseDB
   508  		args    args
   509  		wantErr bool
   510  	}{
   511  		{
   512  			"nil", db, args{key: nil}, false,
   513  		},
   514  		{
   515  			"normal-1", db, args{key: []byte("k-1")}, false,
   516  		},
   517  		{
   518  			"normal-2", db, args{key: []byte("k-3")}, false,
   519  		},
   520  	}
   521  	for _, tt := range tests {
   522  		t.Run(tt.name, func(t *testing.T) {
   523  			if err := tt.db.Delete(tt.args.key); (err != nil) != tt.wantErr {
   524  				t.Errorf("Delete() error = %v, wantErr %v", err, tt.wantErr)
   525  			}
   526  		})
   527  	}
   528  }
   529  
   530  func TestRoseDB_SetEx(t *testing.T) {
   531  	t.Run("key-only", func(t *testing.T) {
   532  		testRoseDBSetEx(t, KeyOnlyMemMode)
   533  	})
   534  
   535  	t.Run("key-value", func(t *testing.T) {
   536  		testRoseDBSetEx(t, KeyValueMemMode)
   537  	})
   538  }
   539  
   540  func testRoseDBSetEx(t *testing.T, mode DataIndexMode) {
   541  	path := filepath.Join("/tmp", "rosedb")
   542  	opts := DefaultOptions(path)
   543  	opts.IndexMode = mode
   544  	db, err := Open(opts)
   545  	assert.Nil(t, err)
   546  	defer destroyDB(db)
   547  
   548  	err = db.SetEX(GetKey(1), GetValue16B(), time.Millisecond*200)
   549  	assert.Nil(t, err)
   550  	time.Sleep(time.Millisecond * 205)
   551  	v, err := db.Get(GetKey(1))
   552  	assert.Equal(t, 0, len(v))
   553  	assert.Equal(t, ErrKeyNotFound, err)
   554  
   555  	err = db.SetEX(GetKey(2), GetValue16B(), time.Second*200)
   556  	assert.Nil(t, err)
   557  	time.Sleep(time.Millisecond * 200)
   558  	v1, err := db.Get(GetKey(2))
   559  	assert.NotNil(t, v1)
   560  	assert.Nil(t, err)
   561  
   562  	// set an existed key.
   563  	err = db.Set(GetKey(3), GetValue16B())
   564  	assert.Nil(t, err)
   565  
   566  	err = db.SetEX(GetKey(3), GetValue16B(), time.Millisecond*200)
   567  	assert.Nil(t, err)
   568  	time.Sleep(time.Millisecond * 205)
   569  	v2, err := db.Get(GetKey(3))
   570  	assert.Equal(t, 0, len(v2))
   571  	assert.Equal(t, ErrKeyNotFound, err)
   572  }
   573  
   574  func TestRoseDB_SetNX(t *testing.T) {
   575  	t.Run("default", func(t *testing.T) {
   576  		testRoseDBSetNX(t, FileIO, KeyOnlyMemMode)
   577  	})
   578  
   579  	t.Run("mmap", func(t *testing.T) {
   580  		testRoseDBSetNX(t, MMap, KeyOnlyMemMode)
   581  	})
   582  
   583  	t.Run("key-val-mem-mode", func(t *testing.T) {
   584  		testRoseDBSetNX(t, FileIO, KeyValueMemMode)
   585  	})
   586  }
   587  
   588  func testRoseDBSetNX(t *testing.T, ioType IOType, mode DataIndexMode) {
   589  	path := filepath.Join("/tmp", "rosedb")
   590  	opts := DefaultOptions(path)
   591  	opts.IoType = ioType
   592  	opts.IndexMode = mode
   593  	db, err := Open(opts)
   594  	assert.Nil(t, err)
   595  	defer destroyDB(db)
   596  
   597  	type args struct {
   598  		key     []byte
   599  		value   []byte
   600  		wantErr bool
   601  	}
   602  	tests := []struct {
   603  		name string
   604  		db   *RoseDB
   605  		args []args
   606  	}{
   607  		{
   608  			name: "nil-key",
   609  			db:   db,
   610  			args: []args{{key: nil, value: []byte("val-1")}},
   611  		},
   612  		{
   613  			name: "nil-value",
   614  			db:   db,
   615  			args: []args{{key: []byte("key-1"), value: nil}},
   616  		},
   617  		{
   618  			name: "not exist in db",
   619  			db:   db,
   620  			args: []args{
   621  				{
   622  					key:     []byte("key-1"),
   623  					value:   []byte("val-1"),
   624  					wantErr: false,
   625  				},
   626  			},
   627  		},
   628  		{
   629  			name: "exist in db",
   630  			db:   db,
   631  			args: []args{
   632  				{
   633  					key:     []byte("key-1"),
   634  					value:   []byte("val-1"),
   635  					wantErr: false,
   636  				},
   637  				{
   638  					key:     []byte("key-1"),
   639  					value:   []byte("val-1"),
   640  					wantErr: false,
   641  				},
   642  			},
   643  		},
   644  		{
   645  			name: "not exist in multiple valued db",
   646  			db:   db,
   647  			args: []args{
   648  				{
   649  					key:     []byte("key-1"),
   650  					value:   []byte("value-1"),
   651  					wantErr: false,
   652  				},
   653  				{
   654  					key:     []byte("key-2"),
   655  					value:   []byte("value-2"),
   656  					wantErr: false,
   657  				},
   658  			},
   659  		},
   660  	}
   661  	for _, tt := range tests {
   662  		t.Run(tt.name, func(t *testing.T) {
   663  			for _, arg := range tt.args {
   664  				if err := tt.db.SetNX(arg.key, arg.value); (err != nil) != arg.wantErr {
   665  					t.Errorf("Set() error = %v, wantErr %v", err, arg.wantErr)
   666  				}
   667  			}
   668  		})
   669  	}
   670  }
   671  
   672  func TestRoseDB_MSet(t *testing.T) {
   673  	t.Run("default", func(t *testing.T) {
   674  		testRoseDBMSet(t, FileIO, KeyOnlyMemMode)
   675  	})
   676  
   677  	t.Run("mmap", func(t *testing.T) {
   678  		testRoseDBMSet(t, MMap, KeyOnlyMemMode)
   679  	})
   680  
   681  	t.Run("key-val-mem-mode", func(t *testing.T) {
   682  		testRoseDBMSet(t, FileIO, KeyValueMemMode)
   683  	})
   684  }
   685  
   686  func testRoseDBMSet(t *testing.T, ioType IOType, mode DataIndexMode) {
   687  	path := filepath.Join("/tmp", "rosedb")
   688  	opts := DefaultOptions(path)
   689  	opts.IoType = ioType
   690  	opts.IndexMode = mode
   691  	db, err := Open(opts)
   692  	assert.Nil(t, err)
   693  	defer destroyDB(db)
   694  
   695  	tests := []struct {
   696  		name    string
   697  		db      *RoseDB
   698  		args    [][]byte
   699  		wantErr bool
   700  	}{
   701  		{
   702  			name:    "nil-key",
   703  			db:      db,
   704  			args:    [][]byte{nil, []byte("val-1")},
   705  			wantErr: false,
   706  		},
   707  		{
   708  			name:    "nil-value",
   709  			db:      db,
   710  			args:    [][]byte{[]byte("key-1"), nil},
   711  			wantErr: false,
   712  		},
   713  		{
   714  			name:    "empty pair",
   715  			db:      db,
   716  			args:    [][]byte{},
   717  			wantErr: true,
   718  		},
   719  		{
   720  			name:    "one pair",
   721  			db:      db,
   722  			args:    [][]byte{[]byte("key-1"), []byte("value-1")},
   723  			wantErr: false,
   724  		},
   725  		{
   726  			name: "multiple pair",
   727  			db:   db,
   728  			args: [][]byte{
   729  				[]byte("key-1"), []byte("value-1"),
   730  				[]byte("key-2"), []byte("value-2"),
   731  				[]byte("key-3"), []byte("value-3"),
   732  			},
   733  			wantErr: false,
   734  		},
   735  		{
   736  			name: "wrong number of key-value",
   737  			db:   db,
   738  			args: [][]byte{
   739  				[]byte("key-1"), []byte("value-1"),
   740  				[]byte("key-2"), []byte("value-2"),
   741  				[]byte("key-3"),
   742  			},
   743  			wantErr: true,
   744  		},
   745  	}
   746  	for _, tt := range tests {
   747  		t.Run(tt.name, func(t *testing.T) {
   748  			err := tt.db.MSet(tt.args...)
   749  			if (err != nil) != tt.wantErr {
   750  				t.Errorf("Set() error = %v, wantErr %v", err, tt.wantErr)
   751  			}
   752  			if tt.wantErr == true && !errors.Is(err, ErrWrongNumberOfArgs) {
   753  				t.Errorf("Set() error = %v, expected error = %v", err, ErrWrongNumberOfArgs)
   754  			}
   755  		})
   756  	}
   757  }
   758  
   759  func TestRoseDB_Append(t *testing.T) {
   760  	t.Run("default", func(t *testing.T) {
   761  		testRoseDBAppend(t, FileIO, KeyOnlyMemMode)
   762  	})
   763  
   764  	t.Run("mmap", func(t *testing.T) {
   765  		testRoseDBAppend(t, MMap, KeyOnlyMemMode)
   766  	})
   767  
   768  	t.Run("key-val-mem-mode", func(t *testing.T) {
   769  		testRoseDBAppend(t, FileIO, KeyValueMemMode)
   770  	})
   771  }
   772  
   773  func testRoseDBAppend(t *testing.T, ioType IOType, mode DataIndexMode) {
   774  	path := filepath.Join("/tmp", "rosedb")
   775  	opts := DefaultOptions(path)
   776  	opts.IoType = ioType
   777  	opts.IndexMode = mode
   778  	db, err := Open(opts)
   779  	assert.Nil(t, err)
   780  	defer destroyDB(db)
   781  
   782  	type args struct {
   783  		key   []byte
   784  		value []byte
   785  	}
   786  	tests := []struct {
   787  		name    string
   788  		db      *RoseDB
   789  		args    args
   790  		wantErr bool
   791  	}{
   792  		{
   793  			"nil-key", db, args{key: nil, value: []byte("val-1")}, false,
   794  		},
   795  		{
   796  			"nil-value", db, args{key: []byte("key-1"), value: nil}, false,
   797  		},
   798  		{
   799  			"not exist in db", db, args{key: []byte("key-2"), value: []byte("val-2")}, false,
   800  		},
   801  		{
   802  			"exist in db", db, args{key: []byte("key-2"), value: []byte("val-2")}, false,
   803  		},
   804  	}
   805  	for _, tt := range tests {
   806  		t.Run(tt.name, func(t *testing.T) {
   807  			if err := tt.db.Append(tt.args.key, tt.args.value); (err != nil) != tt.wantErr {
   808  				t.Errorf("Set() error = %v, wantErr %v", err, tt.wantErr)
   809  			}
   810  		})
   811  	}
   812  }
   813  
   814  func TestRoseDB_MSetNX(t *testing.T) {
   815  	t.Run("default", func(t *testing.T) {
   816  		testRoseDBMSetNX(t, FileIO, KeyOnlyMemMode)
   817  	})
   818  
   819  	t.Run("mmap", func(t *testing.T) {
   820  		testRoseDBMSetNX(t, MMap, KeyOnlyMemMode)
   821  	})
   822  
   823  	t.Run("key-val-mem-mode", func(t *testing.T) {
   824  		testRoseDBMSetNX(t, FileIO, KeyValueMemMode)
   825  	})
   826  }
   827  
   828  func testRoseDBMSetNX(t *testing.T, ioType IOType, mode DataIndexMode) {
   829  	path := filepath.Join("/tmp", "rosedb")
   830  	opts := DefaultOptions(path)
   831  	opts.IoType = ioType
   832  	opts.IndexMode = mode
   833  	db, err := Open(opts)
   834  	assert.Nil(t, err)
   835  	defer destroyDB(db)
   836  
   837  	_ = db.Set([]byte("key-10"), []byte("value-10"))
   838  	tests := []struct {
   839  		name            string
   840  		db              *RoseDB
   841  		args            [][]byte
   842  		expDuplicateKey []byte
   843  		expDuplicateVal []byte
   844  		wantErr         bool
   845  	}{
   846  		{
   847  			name:    "nil-key",
   848  			db:      db,
   849  			args:    [][]byte{nil, []byte("val-1")},
   850  			wantErr: false,
   851  		},
   852  		{
   853  			name:    "nil-value",
   854  			db:      db,
   855  			args:    [][]byte{[]byte("key-1"), nil},
   856  			wantErr: false,
   857  		},
   858  		{
   859  			name:    "empty pair",
   860  			db:      db,
   861  			args:    [][]byte{},
   862  			wantErr: true,
   863  		},
   864  		{
   865  			name:    "one pair",
   866  			db:      db,
   867  			args:    [][]byte{[]byte("key-1"), []byte("value-1")},
   868  			wantErr: false,
   869  		},
   870  		{
   871  			name: "multiple pair - no duplicate",
   872  			db:   db,
   873  			args: [][]byte{
   874  				[]byte("key-1"), []byte("value-1"),
   875  				[]byte("key-2"), []byte("value-2"),
   876  				[]byte("key-3"), []byte("value-3"),
   877  			},
   878  			wantErr: false,
   879  		},
   880  		{
   881  			name: "multiple pair - duplicate exists",
   882  			db:   db,
   883  			args: [][]byte{
   884  				[]byte("key-11"), []byte("value-1"),
   885  				[]byte("key-12"), []byte("value-2"),
   886  				[]byte("key-12"), []byte("value-3")},
   887  			expDuplicateKey: []byte("key-12"),
   888  			expDuplicateVal: []byte("value-2"),
   889  			wantErr:         false,
   890  		},
   891  		{
   892  			name: "multiple pair - already exists",
   893  			db:   db,
   894  			args: [][]byte{
   895  				[]byte("key-1"), []byte("value-1"),
   896  				[]byte("key-2"), []byte("value-2"),
   897  				[]byte("key-10"), []byte("value-20"),
   898  			},
   899  			expDuplicateKey: []byte("key-10"),
   900  			expDuplicateVal: []byte("value-10"),
   901  			wantErr:         false,
   902  		},
   903  		{
   904  			name: "wrong number of key-value",
   905  			db:   db,
   906  			args: [][]byte{
   907  				[]byte("key-1"), []byte("value-1"),
   908  				[]byte("key-2"), []byte("value-2"),
   909  				[]byte("key-3"),
   910  			},
   911  			wantErr: true,
   912  		},
   913  	}
   914  	for _, tt := range tests {
   915  		t.Run(tt.name, func(t *testing.T) {
   916  			err = tt.db.MSetNX(tt.args...)
   917  			if (err != nil) != tt.wantErr {
   918  				t.Errorf("MSetNX() error = %v, wantErr %v", err, tt.wantErr)
   919  			}
   920  			if tt.wantErr == true && !errors.Is(err, ErrWrongNumberOfArgs) {
   921  				t.Errorf("MSetNX() error = %v, expected error = %v", err, ErrWrongNumberOfArgs)
   922  			}
   923  			if tt.expDuplicateVal != nil {
   924  				val, _ := tt.db.Get(tt.expDuplicateKey)
   925  				if !bytes.Equal(val, tt.expDuplicateVal) {
   926  					t.Errorf("expected duplicate value = %v, got = %v", string(tt.expDuplicateVal), string(val))
   927  				}
   928  			}
   929  		})
   930  	}
   931  }
   932  
   933  func TestRoseDB_Decr(t *testing.T) {
   934  	t.Run("default", func(t *testing.T) {
   935  		testRoseDBDecr(t, FileIO, KeyOnlyMemMode)
   936  	})
   937  
   938  	t.Run("mmap", func(t *testing.T) {
   939  		testRoseDBDecr(t, MMap, KeyOnlyMemMode)
   940  	})
   941  
   942  	t.Run("key-val-mem-mode", func(t *testing.T) {
   943  		testRoseDBDecr(t, FileIO, KeyValueMemMode)
   944  	})
   945  }
   946  
   947  func testRoseDBDecr(t *testing.T, ioType IOType, mode DataIndexMode) {
   948  	path := filepath.Join("/tmp", "rosedb")
   949  	opts := DefaultOptions(path)
   950  	opts.IoType = ioType
   951  	opts.IndexMode = mode
   952  	db, err := Open(opts)
   953  	assert.Nil(t, err)
   954  	defer destroyDB(db)
   955  
   956  	_ = db.MSet([]byte("nil-value"), nil,
   957  		[]byte("ten"), []byte("10"),
   958  		[]byte("min"), []byte(strconv.Itoa(math.MinInt64)),
   959  		[]byte("str-key"), []byte("str-val"))
   960  	tests := []struct {
   961  		name    string
   962  		db      *RoseDB
   963  		key     []byte
   964  		expVal  int64
   965  		expByte []byte
   966  		expErr  error
   967  		wantErr bool
   968  	}{
   969  		{
   970  			name:    "nil value",
   971  			db:      db,
   972  			key:     []byte("nil-value"),
   973  			expVal:  -1,
   974  			expByte: []byte("-1"),
   975  			wantErr: false,
   976  		},
   977  		{
   978  			name:    "exist key",
   979  			db:      db,
   980  			key:     []byte("ten"),
   981  			expVal:  9,
   982  			expByte: []byte("9"),
   983  			wantErr: false,
   984  		},
   985  		{
   986  			name:    "non-exist key",
   987  			db:      db,
   988  			key:     []byte("zero"),
   989  			expVal:  -1,
   990  			expByte: []byte("-1"),
   991  			wantErr: false,
   992  		},
   993  		{
   994  			name:    "overflow value",
   995  			db:      db,
   996  			key:     []byte("min"),
   997  			expVal:  0,
   998  			expByte: []byte(strconv.Itoa(math.MinInt64)),
   999  			expErr:  ErrIntegerOverflow,
  1000  			wantErr: true,
  1001  		},
  1002  		{
  1003  			name:    "wrong type",
  1004  			db:      db,
  1005  			key:     []byte("str-key"),
  1006  			expVal:  0,
  1007  			expErr:  ErrWrongValueType,
  1008  			wantErr: true,
  1009  		},
  1010  	}
  1011  
  1012  	for _, tt := range tests {
  1013  		t.Run(tt.name, func(t *testing.T) {
  1014  			newVal, err := tt.db.Decr(tt.key)
  1015  			if (err != nil) != tt.wantErr || err != tt.expErr {
  1016  				t.Errorf("Decr() error = %v, wantErr = %v", err, tt.expErr)
  1017  			}
  1018  			if newVal != tt.expVal {
  1019  				t.Errorf("Decr() expected value = %v, actual value = %v", tt.expVal, newVal)
  1020  			}
  1021  			val, _ := tt.db.Get(tt.key)
  1022  			if tt.expByte != nil && !bytes.Equal(val, tt.expByte) {
  1023  				t.Errorf("Decr() expected value = %v, actual = %v", tt.expByte, val)
  1024  			}
  1025  		})
  1026  	}
  1027  }
  1028  
  1029  func TestRoseDB_DecrBy(t *testing.T) {
  1030  	t.Run("default", func(t *testing.T) {
  1031  		testRoseDBDecrBy(t, FileIO, KeyOnlyMemMode)
  1032  	})
  1033  
  1034  	t.Run("mmap", func(t *testing.T) {
  1035  		testRoseDBDecrBy(t, MMap, KeyOnlyMemMode)
  1036  	})
  1037  
  1038  	t.Run("key-val-mem-mode", func(t *testing.T) {
  1039  		testRoseDBDecrBy(t, FileIO, KeyValueMemMode)
  1040  	})
  1041  }
  1042  
  1043  func testRoseDBDecrBy(t *testing.T, ioType IOType, mode DataIndexMode) {
  1044  	path := filepath.Join("/tmp", "rosedb")
  1045  	opts := DefaultOptions(path)
  1046  	opts.IoType = ioType
  1047  	opts.IndexMode = mode
  1048  	db, err := Open(opts)
  1049  	assert.Nil(t, err)
  1050  	defer destroyDB(db)
  1051  
  1052  	_ = db.MSet([]byte("nil-value"), nil,
  1053  		[]byte("ten"), []byte("10"),
  1054  		[]byte("min"), []byte(strconv.Itoa(math.MinInt64)),
  1055  		[]byte("max"), []byte(strconv.Itoa(math.MaxInt64)),
  1056  		[]byte("str-key"), []byte("str-val"),
  1057  		[]byte("neg"), []byte("11"))
  1058  	tests := []struct {
  1059  		name    string
  1060  		db      *RoseDB
  1061  		key     []byte
  1062  		decr    int64
  1063  		expVal  int64
  1064  		expByte []byte
  1065  		expErr  error
  1066  		wantErr bool
  1067  	}{
  1068  		{
  1069  			name:    "nil value",
  1070  			db:      db,
  1071  			key:     []byte("nil-value"),
  1072  			decr:    10,
  1073  			expVal:  -10,
  1074  			expByte: []byte("-10"),
  1075  			wantErr: false,
  1076  		},
  1077  		{
  1078  			name:    "exist key",
  1079  			db:      db,
  1080  			key:     []byte("ten"),
  1081  			decr:    25,
  1082  			expVal:  -15,
  1083  			expByte: []byte("-15"),
  1084  			wantErr: false,
  1085  		},
  1086  		{
  1087  			name:    "non-exist key",
  1088  			db:      db,
  1089  			key:     []byte("zero"),
  1090  			decr:    3,
  1091  			expVal:  -3,
  1092  			expByte: []byte("-3"),
  1093  			wantErr: false,
  1094  		},
  1095  		{
  1096  			name:    "overflow value-min",
  1097  			db:      db,
  1098  			key:     []byte("min"),
  1099  			decr:    3,
  1100  			expVal:  0,
  1101  			expByte: []byte(strconv.Itoa(math.MinInt64)),
  1102  			expErr:  ErrIntegerOverflow,
  1103  			wantErr: true,
  1104  		},
  1105  		{
  1106  			name:    "overflow value-max",
  1107  			db:      db,
  1108  			key:     []byte("max"),
  1109  			decr:    -10,
  1110  			expVal:  0,
  1111  			expByte: []byte(strconv.Itoa(math.MaxInt64)),
  1112  			expErr:  ErrIntegerOverflow,
  1113  			wantErr: true,
  1114  		},
  1115  		{
  1116  			name:    "wrong type",
  1117  			db:      db,
  1118  			key:     []byte("str-key"),
  1119  			decr:    5,
  1120  			expVal:  0,
  1121  			expErr:  ErrWrongValueType,
  1122  			wantErr: true,
  1123  		},
  1124  		{
  1125  			name:    "negative incr",
  1126  			db:      db,
  1127  			key:     []byte("neg"),
  1128  			decr:    -4,
  1129  			expVal:  15,
  1130  			expByte: []byte("15"),
  1131  			wantErr: false,
  1132  		},
  1133  	}
  1134  
  1135  	for _, tt := range tests {
  1136  		t.Run(tt.name, func(t *testing.T) {
  1137  			newVal, err := tt.db.DecrBy(tt.key, tt.decr)
  1138  			if (err != nil) != tt.wantErr || err != tt.expErr {
  1139  				t.Errorf("DecrBy() error = %v, wantErr = %v", err, tt.expErr)
  1140  			}
  1141  			if newVal != tt.expVal {
  1142  				t.Errorf("DecrBy() expected value = %v, actual value = %v", tt.expVal, newVal)
  1143  			}
  1144  			val, _ := tt.db.Get(tt.key)
  1145  			if tt.expByte != nil && !bytes.Equal(val, tt.expByte) {
  1146  				t.Errorf("DecrBy() expected value = %v, actual = %v", tt.expByte, val)
  1147  			}
  1148  		})
  1149  	}
  1150  }
  1151  
  1152  func TestRoseDB_Incr(t *testing.T) {
  1153  	t.Run("default", func(t *testing.T) {
  1154  		testRoseDBIncr(t, FileIO, KeyOnlyMemMode)
  1155  	})
  1156  
  1157  	t.Run("mmap", func(t *testing.T) {
  1158  		testRoseDBIncr(t, MMap, KeyOnlyMemMode)
  1159  	})
  1160  
  1161  	t.Run("key-val-mem-mode", func(t *testing.T) {
  1162  		testRoseDBIncr(t, FileIO, KeyValueMemMode)
  1163  	})
  1164  }
  1165  
  1166  func testRoseDBIncr(t *testing.T, ioType IOType, mode DataIndexMode) {
  1167  	path := filepath.Join("/tmp", "rosedb")
  1168  	opts := DefaultOptions(path)
  1169  	opts.IoType = ioType
  1170  	opts.IndexMode = mode
  1171  	db, err := Open(opts)
  1172  	assert.Nil(t, err)
  1173  	defer destroyDB(db)
  1174  
  1175  	_ = db.MSet([]byte("nil-value"), nil,
  1176  		[]byte("ten"), []byte("10"),
  1177  		[]byte("max"), []byte(strconv.Itoa(math.MaxInt64)),
  1178  		[]byte("str-key"), []byte("str-val"))
  1179  	tests := []struct {
  1180  		name    string
  1181  		db      *RoseDB
  1182  		key     []byte
  1183  		expVal  int64
  1184  		expByte []byte
  1185  		expErr  error
  1186  		wantErr bool
  1187  	}{
  1188  		{
  1189  			name:    "nil value",
  1190  			db:      db,
  1191  			key:     []byte("nil-value"),
  1192  			expVal:  1,
  1193  			expByte: []byte("1"),
  1194  			wantErr: false,
  1195  		},
  1196  		{
  1197  			name:    "exist key",
  1198  			db:      db,
  1199  			key:     []byte("ten"),
  1200  			expVal:  11,
  1201  			expByte: []byte("11"),
  1202  			wantErr: false,
  1203  		},
  1204  		{
  1205  			name:    "non-exist key",
  1206  			db:      db,
  1207  			key:     []byte("zero"),
  1208  			expVal:  1,
  1209  			expByte: []byte("1"),
  1210  			wantErr: false,
  1211  		},
  1212  		{
  1213  			name:    "overflow value-max",
  1214  			db:      db,
  1215  			key:     []byte("max"),
  1216  			expVal:  0,
  1217  			expByte: []byte(strconv.Itoa(math.MaxInt64)),
  1218  			expErr:  ErrIntegerOverflow,
  1219  			wantErr: true,
  1220  		},
  1221  		{
  1222  			name:    "wrong type",
  1223  			db:      db,
  1224  			key:     []byte("str-key"),
  1225  			expVal:  0,
  1226  			expErr:  ErrWrongValueType,
  1227  			wantErr: true,
  1228  		},
  1229  	}
  1230  
  1231  	for _, tt := range tests {
  1232  		t.Run(tt.name, func(t *testing.T) {
  1233  			newVal, err := tt.db.Incr(tt.key)
  1234  			if (err != nil) != tt.wantErr || err != tt.expErr {
  1235  				t.Errorf("Incr() error = %v, wantErr = %v", err, tt.expErr)
  1236  			}
  1237  			if newVal != tt.expVal {
  1238  				t.Errorf("Incr() expected value = %v, actual value = %v", tt.expVal, newVal)
  1239  			}
  1240  			val, _ := tt.db.Get(tt.key)
  1241  			if tt.expByte != nil && !bytes.Equal(val, tt.expByte) {
  1242  				t.Errorf("Incr() expected value = %v, actual = %v", tt.expByte, val)
  1243  			}
  1244  		})
  1245  	}
  1246  }
  1247  
  1248  func TestRoseDB_IncrBy(t *testing.T) {
  1249  	t.Run("default", func(t *testing.T) {
  1250  		testRoseDBIncrBy(t, FileIO, KeyOnlyMemMode)
  1251  	})
  1252  
  1253  	t.Run("mmap", func(t *testing.T) {
  1254  		testRoseDBIncrBy(t, MMap, KeyOnlyMemMode)
  1255  	})
  1256  
  1257  	t.Run("key-val-mem-mode", func(t *testing.T) {
  1258  		testRoseDBIncrBy(t, FileIO, KeyValueMemMode)
  1259  	})
  1260  }
  1261  
  1262  func testRoseDBIncrBy(t *testing.T, ioType IOType, mode DataIndexMode) {
  1263  	path := filepath.Join("/tmp", "rosedb")
  1264  	opts := DefaultOptions(path)
  1265  	opts.IoType = ioType
  1266  	opts.IndexMode = mode
  1267  	db, err := Open(opts)
  1268  	assert.Nil(t, err)
  1269  	defer destroyDB(db)
  1270  
  1271  	_ = db.MSet([]byte("nil-value"), nil,
  1272  		[]byte("ten"), []byte("10"),
  1273  		[]byte("min"), []byte(strconv.Itoa(math.MinInt64)),
  1274  		[]byte("max"), []byte(strconv.Itoa(math.MaxInt64)),
  1275  		[]byte("str-key"), []byte("str-val"),
  1276  		[]byte("neg"), []byte("11"))
  1277  	tests := []struct {
  1278  		name    string
  1279  		db      *RoseDB
  1280  		key     []byte
  1281  		incr    int64
  1282  		expVal  int64
  1283  		expByte []byte
  1284  		expErr  error
  1285  		wantErr bool
  1286  	}{
  1287  		{
  1288  			name:    "nil value",
  1289  			db:      db,
  1290  			key:     []byte("nil-value"),
  1291  			incr:    10,
  1292  			expVal:  10,
  1293  			expByte: []byte("10"),
  1294  			wantErr: false,
  1295  		},
  1296  		{
  1297  			name:    "exist key",
  1298  			db:      db,
  1299  			key:     []byte("ten"),
  1300  			incr:    25,
  1301  			expVal:  35,
  1302  			expByte: []byte("35"),
  1303  			wantErr: false,
  1304  		},
  1305  		{
  1306  			name:    "non-exist key",
  1307  			db:      db,
  1308  			key:     []byte("zero"),
  1309  			incr:    3,
  1310  			expVal:  3,
  1311  			expByte: []byte("3"),
  1312  			wantErr: false,
  1313  		},
  1314  		{
  1315  			name:    "overflow value-min",
  1316  			db:      db,
  1317  			key:     []byte("min"),
  1318  			incr:    -3,
  1319  			expVal:  0,
  1320  			expByte: []byte(strconv.Itoa(math.MinInt64)),
  1321  			expErr:  ErrIntegerOverflow,
  1322  			wantErr: true,
  1323  		},
  1324  		{
  1325  			name:    "overflow value-max",
  1326  			db:      db,
  1327  			key:     []byte("max"),
  1328  			incr:    10,
  1329  			expVal:  0,
  1330  			expByte: []byte(strconv.Itoa(math.MaxInt64)),
  1331  			expErr:  ErrIntegerOverflow,
  1332  			wantErr: true,
  1333  		},
  1334  		{
  1335  			name:    "wrong type",
  1336  			db:      db,
  1337  			key:     []byte("str-key"),
  1338  			incr:    5,
  1339  			expVal:  0,
  1340  			expErr:  ErrWrongValueType,
  1341  			wantErr: true,
  1342  		},
  1343  		{
  1344  			name:    "negative incr",
  1345  			db:      db,
  1346  			key:     []byte("neg"),
  1347  			incr:    -4,
  1348  			expVal:  7,
  1349  			expByte: []byte("7"),
  1350  			wantErr: false,
  1351  		},
  1352  	}
  1353  
  1354  	for _, tt := range tests {
  1355  		t.Run(tt.name, func(t *testing.T) {
  1356  			newVal, err := tt.db.IncrBy(tt.key, tt.incr)
  1357  			if (err != nil) != tt.wantErr || err != tt.expErr {
  1358  				t.Errorf("IncrBy() error = %v, wantErr = %v", err, tt.expErr)
  1359  			}
  1360  			if newVal != tt.expVal {
  1361  				t.Errorf("IncrBy() expected value = %v, actual value = %v", tt.expVal, newVal)
  1362  			}
  1363  			val, _ := tt.db.Get(tt.key)
  1364  			if tt.expByte != nil && !bytes.Equal(val, tt.expByte) {
  1365  				t.Errorf("IncrBy() expected value = %v, actual = %v", tt.expByte, val)
  1366  			}
  1367  		})
  1368  	}
  1369  }
  1370  
  1371  func TestRoseDB_StrLen(t *testing.T) {
  1372  	t.Run("default", func(t *testing.T) {
  1373  		testRoseDBStrLen(t, FileIO, KeyOnlyMemMode)
  1374  	})
  1375  
  1376  	t.Run("mmap", func(t *testing.T) {
  1377  		testRoseDBStrLen(t, MMap, KeyOnlyMemMode)
  1378  	})
  1379  
  1380  	t.Run("key-val-mem-mode", func(t *testing.T) {
  1381  		testRoseDBStrLen(t, FileIO, KeyValueMemMode)
  1382  	})
  1383  }
  1384  
  1385  func testRoseDBStrLen(t *testing.T, ioType IOType, mode DataIndexMode) {
  1386  	path := filepath.Join("/tmp", "rosedb")
  1387  	opts := DefaultOptions(path)
  1388  	opts.IoType = ioType
  1389  	opts.IndexMode = mode
  1390  	db, err := Open(opts)
  1391  	assert.Nil(t, err)
  1392  	defer destroyDB(db)
  1393  
  1394  	_ = db.MSet([]byte("string"), []byte("value"), []byte("empty"), []byte(""))
  1395  
  1396  	tests := []struct {
  1397  		name   string
  1398  		db     *RoseDB
  1399  		key    []byte
  1400  		expLen int
  1401  	}{
  1402  		{
  1403  			name:   "Empty",
  1404  			db:     db,
  1405  			key:    []byte("empty"),
  1406  			expLen: 0,
  1407  		},
  1408  		{
  1409  			name:   "not exist",
  1410  			db:     db,
  1411  			key:    []byte("not-exist-key"),
  1412  			expLen: 0,
  1413  		},
  1414  		{
  1415  			name:   "normal string",
  1416  			db:     db,
  1417  			key:    []byte("string"),
  1418  			expLen: 5,
  1419  		},
  1420  	}
  1421  
  1422  	for _, tt := range tests {
  1423  		t.Run(tt.name, func(t *testing.T) {
  1424  			strLen := tt.db.StrLen(tt.key)
  1425  			if strLen != tt.expLen {
  1426  				t.Errorf("StrLen() expected length = %v, actual length = %v", tt.expLen, strLen)
  1427  			}
  1428  		})
  1429  	}
  1430  }
  1431  
  1432  func TestRoseDB_GetDel(t *testing.T) {
  1433  	t.Run("default", func(t *testing.T) {
  1434  		testRoseDBGetDel(t, FileIO, KeyOnlyMemMode)
  1435  	})
  1436  
  1437  	t.Run("mmap", func(t *testing.T) {
  1438  		testRoseDBGetDel(t, MMap, KeyOnlyMemMode)
  1439  	})
  1440  
  1441  	t.Run("key-val-mem-mode", func(t *testing.T) {
  1442  		testRoseDBGetDel(t, FileIO, KeyValueMemMode)
  1443  	})
  1444  }
  1445  
  1446  func testRoseDBGetDel(t *testing.T, ioType IOType, mode DataIndexMode) {
  1447  	path := filepath.Join("/tmp", "rosedb")
  1448  	opts := DefaultOptions(path)
  1449  	opts.IoType = ioType
  1450  	opts.IndexMode = mode
  1451  	db, err := Open(opts)
  1452  	assert.Nil(t, err)
  1453  	defer destroyDB(db)
  1454  
  1455  	_ = db.MSet(
  1456  		[]byte("nil-value"), nil,
  1457  		[]byte("key-1"), []byte("value-1"),
  1458  		[]byte("key-2"), []byte("value-2"),
  1459  		[]byte("key-3"), []byte("value-3"),
  1460  		[]byte("key-4"), []byte("value-4"),
  1461  	)
  1462  	tests := []struct {
  1463  		name   string
  1464  		db     *RoseDB
  1465  		key    []byte
  1466  		expVal []byte
  1467  		expErr error
  1468  	}{
  1469  		{
  1470  			name:   "nil value",
  1471  			db:     db,
  1472  			key:    []byte("nil-value"),
  1473  			expVal: nil,
  1474  			expErr: nil,
  1475  		},
  1476  		{
  1477  			name:   "not exist in db",
  1478  			db:     db,
  1479  			key:    []byte("not-exist-key"),
  1480  			expVal: nil,
  1481  			expErr: nil,
  1482  		},
  1483  		{
  1484  			name:   "exist in db",
  1485  			db:     db,
  1486  			key:    []byte("key-1"),
  1487  			expVal: []byte("value-1"),
  1488  			expErr: nil,
  1489  		},
  1490  	}
  1491  
  1492  	for _, tt := range tests {
  1493  		t.Run(tt.name, func(t *testing.T) {
  1494  			val, err := tt.db.GetDel(tt.key)
  1495  			if err != tt.expErr {
  1496  				t.Errorf("GetDel(): expected error: %+v, actual error: %+v", tt.expErr, err)
  1497  			}
  1498  			if !bytes.Equal(val, tt.expVal) {
  1499  				t.Errorf("GetDel(): expected val: %v, actual val: %v", tt.expVal, val)
  1500  			}
  1501  
  1502  			val, _ = tt.db.Get(tt.key)
  1503  			if val != nil {
  1504  				t.Errorf("GetDel(): expected val(after Get()): <nil>, actual val(after Get()): %v", val)
  1505  			}
  1506  		})
  1507  	}
  1508  }
  1509  
  1510  func TestRoseDB_DiscardStat_Strs(t *testing.T) {
  1511  	helper := func(isDelete bool) {
  1512  		path := filepath.Join("/tmp", "rosedb")
  1513  		opts := DefaultOptions(path)
  1514  		opts.LogFileSizeThreshold = 64 << 20
  1515  		db, err := Open(opts)
  1516  		assert.Nil(t, err)
  1517  		defer destroyDB(db)
  1518  
  1519  		writeCount := 500000
  1520  		for i := 0; i < writeCount/2; i++ {
  1521  			err := db.Set(GetKey(i), GetValue128B())
  1522  			assert.Nil(t, err)
  1523  		}
  1524  
  1525  		if isDelete {
  1526  			for i := 0; i < writeCount/2; i++ {
  1527  				err := db.Delete(GetKey(i))
  1528  				assert.Nil(t, err)
  1529  			}
  1530  		} else {
  1531  			for i := 0; i < writeCount/2; i++ {
  1532  				err := db.Set(GetKey(i), GetValue128B())
  1533  				assert.Nil(t, err)
  1534  			}
  1535  		}
  1536  		_ = db.Sync()
  1537  		ccl, err := db.discards[String].getCCL(10, 0.5)
  1538  		assert.Nil(t, err)
  1539  		assert.Equal(t, 1, len(ccl))
  1540  	}
  1541  
  1542  	t.Run("rewrite", func(t *testing.T) {
  1543  		helper(false)
  1544  	})
  1545  
  1546  	t.Run("delete", func(t *testing.T) {
  1547  		helper(true)
  1548  	})
  1549  }
  1550  
  1551  func TestRoseDB_StrsGC(t *testing.T) {
  1552  	path := filepath.Join("/tmp", "rosedb")
  1553  	opts := DefaultOptions(path)
  1554  	opts.LogFileSizeThreshold = 64 << 20
  1555  	db, err := Open(opts)
  1556  	assert.Nil(t, err)
  1557  	defer destroyDB(db)
  1558  
  1559  	writeCount := 1000000
  1560  	for i := 0; i < writeCount; i++ {
  1561  		err := db.Set(GetKey(i), GetValue16B())
  1562  		assert.Nil(t, err)
  1563  	}
  1564  	for i := 0; i < writeCount/4; i++ {
  1565  		err := db.Delete(GetKey(i))
  1566  		assert.Nil(t, err)
  1567  	}
  1568  
  1569  	err = db.RunLogFileGC(String, 0, 0.6)
  1570  	assert.Nil(t, err)
  1571  	size := db.strIndex.idxTree.Size()
  1572  	assert.Equal(t, writeCount-writeCount/4, size)
  1573  }
  1574  
  1575  func TestRoseDB_Count(t *testing.T) {
  1576  	path := filepath.Join("/tmp", "rosedb")
  1577  	opts := DefaultOptions(path)
  1578  	db, err := Open(opts)
  1579  	assert.Nil(t, err)
  1580  	defer destroyDB(db)
  1581  
  1582  	c1 := db.Count()
  1583  	assert.Equal(t, 0, c1)
  1584  
  1585  	for i := 0; i < 100; i++ {
  1586  		err = db.Set(GetKey(i), GetValue16B())
  1587  		assert.Nil(t, err)
  1588  	}
  1589  	c2 := db.Count()
  1590  	assert.Equal(t, 100, c2)
  1591  }
  1592  
  1593  func TestRoseDB_Scan(t *testing.T) {
  1594  	t.Run("fileio", func(t *testing.T) {
  1595  		testRoseDBScan(t, FileIO, KeyOnlyMemMode)
  1596  	})
  1597  
  1598  	t.Run("mmap", func(t *testing.T) {
  1599  		testRoseDBScan(t, MMap, KeyValueMemMode)
  1600  	})
  1601  }
  1602  
  1603  func testRoseDBScan(t *testing.T, ioType IOType, mode DataIndexMode) {
  1604  	path := filepath.Join("/tmp", "rosedb")
  1605  	opts := DefaultOptions(path)
  1606  	opts.IoType = ioType
  1607  	opts.IndexMode = mode
  1608  	db, err := Open(opts)
  1609  	assert.Nil(t, err)
  1610  	defer destroyDB(db)
  1611  
  1612  	for i := 0; i < 100; i++ {
  1613  		err = db.Set(GetKey(i), GetValue16B())
  1614  		assert.Nil(t, err)
  1615  	}
  1616  
  1617  	values, err := db.Scan(nil, "", 10)
  1618  	assert.Nil(t, err)
  1619  	assert.Equal(t, 20, len(values))
  1620  
  1621  	db.Set([]byte("aba"), GetValue16B())
  1622  	db.Set([]byte("aab"), GetValue16B())
  1623  	db.Set([]byte("aac"), GetValue16B())
  1624  	db.Set([]byte("abc"), GetValue16B())
  1625  
  1626  	values, err = db.Scan([]byte("ab"), "", 20)
  1627  	assert.Nil(t, err)
  1628  	assert.Equal(t, 4, len(values))
  1629  
  1630  	db.Set([]byte("1223"), GetValue16B())
  1631  	db.Set([]byte("55"), GetValue16B())
  1632  	db.Set([]byte("9001"), GetValue16B())
  1633  
  1634  	values, err = db.Scan(nil, "^[0-9]*$", 3)
  1635  	assert.Nil(t, err)
  1636  	assert.Equal(t, 6, len(values))
  1637  }
  1638  
  1639  func TestRoseDB_Expire(t *testing.T) {
  1640  	t.Run("fileio", func(t *testing.T) {
  1641  		testRoseDBExpire(t, FileIO, KeyOnlyMemMode)
  1642  	})
  1643  
  1644  	t.Run("mmap", func(t *testing.T) {
  1645  		testRoseDBExpire(t, MMap, KeyValueMemMode)
  1646  	})
  1647  }
  1648  
  1649  func testRoseDBExpire(t *testing.T, ioType IOType, mode DataIndexMode) {
  1650  	path := filepath.Join("/tmp", "rosedb")
  1651  	opts := DefaultOptions(path)
  1652  	opts.IoType = ioType
  1653  	opts.IndexMode = mode
  1654  	db, err := Open(opts)
  1655  	assert.Nil(t, err)
  1656  	defer destroyDB(db)
  1657  
  1658  	t.Run("normal", func(t *testing.T) {
  1659  		err = db.Expire(GetKey(31), time.Second*2)
  1660  		assert.Equal(t, ErrKeyNotFound, err)
  1661  
  1662  		err = db.Set(GetKey(55), GetValue16B())
  1663  		assert.Nil(t, err)
  1664  		err = db.Expire(GetKey(55), time.Second*1)
  1665  		assert.Nil(t, err)
  1666  
  1667  		time.Sleep(time.Second)
  1668  		_, err = db.Get(GetKey(55))
  1669  		assert.Equal(t, ErrKeyNotFound, err)
  1670  	})
  1671  
  1672  	t.Run("set-twice", func(t *testing.T) {
  1673  		err := db.Set(GetKey(66), GetValue16B())
  1674  		assert.Nil(t, err)
  1675  
  1676  		db.Expire(GetKey(66), time.Second*100)
  1677  		db.Expire(GetKey(66), time.Second*1)
  1678  		time.Sleep(time.Second)
  1679  		_, err = db.Get(GetKey(66))
  1680  		assert.Equal(t, ErrKeyNotFound, err)
  1681  	})
  1682  }
  1683  
  1684  func TestRoseDB_TTL(t *testing.T) {
  1685  	path := filepath.Join("/tmp", "rosedb")
  1686  	opts := DefaultOptions(path)
  1687  	db, err := Open(opts)
  1688  	assert.Nil(t, err)
  1689  	defer destroyDB(db)
  1690  
  1691  	t1, err := db.TTL(GetKey(111))
  1692  	assert.Equal(t, int64(0), t1)
  1693  	assert.Equal(t, ErrKeyNotFound, err)
  1694  
  1695  	err = db.SetEX(GetKey(123), GetValue16B(), time.Second*30)
  1696  	assert.Nil(t, err)
  1697  
  1698  	t2, err := db.TTL(GetKey(123))
  1699  	assert.Equal(t, int64(30), t2)
  1700  	assert.Nil(t, err)
  1701  
  1702  	err = db.Set(GetKey(007), GetValue16B())
  1703  	assert.Nil(t, err)
  1704  	db.Expire(GetKey(007), time.Second*50)
  1705  
  1706  	t3, err := db.TTL(GetKey(007))
  1707  	assert.Equal(t, int64(50), t3)
  1708  	assert.Nil(t, err)
  1709  
  1710  	db.SetEX(GetKey(999), GetValue16B(), time.Second*5)
  1711  	db.Expire(GetKey(999), time.Second*100)
  1712  	db.Expire(GetKey(999), time.Second*10)
  1713  
  1714  	t4, err := db.TTL(GetKey(999))
  1715  	assert.Equal(t, int64(10), t4)
  1716  	assert.Nil(t, err)
  1717  }
  1718  
  1719  func TestRoseDB_Persist(t *testing.T) {
  1720  	t.Run("fileio", func(t *testing.T) {
  1721  		testRoseDBPersist(t, FileIO, KeyOnlyMemMode)
  1722  	})
  1723  
  1724  	t.Run("mmap", func(t *testing.T) {
  1725  		testRoseDBPersist(t, MMap, KeyValueMemMode)
  1726  	})
  1727  }
  1728  
  1729  func testRoseDBPersist(t *testing.T, ioType IOType, mode DataIndexMode) {
  1730  	path := filepath.Join("/tmp", "rosedb")
  1731  	opts := DefaultOptions(path)
  1732  	opts.IoType = ioType
  1733  	opts.IndexMode = mode
  1734  	db, err := Open(opts)
  1735  	assert.Nil(t, err)
  1736  	defer destroyDB(db)
  1737  
  1738  	err = db.SetEX(GetKey(900), GetValue16B(), time.Second*1)
  1739  	assert.Nil(t, err)
  1740  	err = db.Persist(GetKey(900))
  1741  	assert.Nil(t, err)
  1742  
  1743  	time.Sleep(time.Second)
  1744  
  1745  	val, err := db.Get(GetKey(900))
  1746  	assert.Nil(t, err)
  1747  	assert.NotNil(t, val)
  1748  }
  1749  
  1750  func TestRoseDB_GetStrsKeys(t *testing.T) {
  1751  	t.Run("fileio", func(t *testing.T) {
  1752  		testRoseDBGetStrsKeys(t, FileIO, KeyOnlyMemMode)
  1753  	})
  1754  
  1755  	t.Run("mmap", func(t *testing.T) {
  1756  		testRoseDBGetStrsKeys(t, MMap, KeyValueMemMode)
  1757  	})
  1758  }
  1759  
  1760  func testRoseDBGetStrsKeys(t *testing.T, ioType IOType, mode DataIndexMode) {
  1761  	path := filepath.Join("/tmp", "rosedb")
  1762  	opts := DefaultOptions(path)
  1763  	opts.IoType = ioType
  1764  	opts.IndexMode = mode
  1765  	db, err := Open(opts)
  1766  	assert.Nil(t, err)
  1767  	defer destroyDB(db)
  1768  
  1769  	keys1, err := db.GetStrsKeys()
  1770  	assert.Nil(t, err)
  1771  	assert.Equal(t, 0, len(keys1))
  1772  
  1773  	var keys [][]byte
  1774  	for i := 0; i < 100; i++ {
  1775  		keys = append(keys, GetKey(i))
  1776  		err := db.Set(GetKey(i), GetValue16B())
  1777  		assert.Nil(t, err)
  1778  	}
  1779  	sort.Slice(keys, func(i, j int) bool {
  1780  		return bytes.Compare(keys[i], keys[j]) < 0
  1781  	})
  1782  
  1783  	keys2, err := db.GetStrsKeys()
  1784  	assert.Nil(t, err)
  1785  	assert.Equal(t, keys2, keys)
  1786  
  1787  	db.Expire(GetKey(19), time.Millisecond*200)
  1788  	db.Expire(GetKey(33), time.Millisecond*400)
  1789  	db.Expire(GetKey(99), time.Millisecond*500)
  1790  	time.Sleep(time.Second)
  1791  
  1792  	keys3, err := db.GetStrsKeys()
  1793  	assert.Nil(t, err)
  1794  	assert.Equal(t, 97, len(keys3))
  1795  }