github.com/flower-corp/rosedb@v1.1.2-0.20230117132829-21dc4f7b319a/hash_test.go (about) 1 package rosedb 2 3 import ( 4 "errors" 5 "github.com/stretchr/testify/assert" 6 "math" 7 "path/filepath" 8 "testing" 9 ) 10 11 func TestRoseDB_HSet(t *testing.T) { 12 t.Run("fileio", func(t *testing.T) { 13 testRoseDBHSet(t, FileIO, KeyOnlyMemMode) 14 }) 15 t.Run("mmap", func(t *testing.T) { 16 testRoseDBHSet(t, MMap, KeyValueMemMode) 17 }) 18 t.Run("mmap-key-only", func(t *testing.T) { 19 testRoseDBHSet(t, MMap, KeyOnlyMemMode) 20 }) 21 } 22 23 func testRoseDBHSet(t *testing.T, ioType IOType, mode DataIndexMode) { 24 path := filepath.Join("/tmp", "rosedb") 25 opts := DefaultOptions(path) 26 opts.IoType = ioType 27 opts.IndexMode = mode 28 db, err := Open(opts) 29 assert.Nil(t, err) 30 defer destroyDB(db) 31 32 type args struct { 33 key []byte 34 arg [][]byte 35 } 36 tests := []struct { 37 name string 38 db *RoseDB 39 args args 40 wantErr bool 41 }{ 42 { 43 "nil-key-and-field", db, args{key: nil, arg: [][]byte{nil, []byte("val-0")}}, false, 44 }, 45 { 46 "wrong-num-of-args", db, args{key: GetKey(2), arg: [][]byte{[]byte("field-0")}}, true, 47 }, 48 { 49 "normal-single-pair", db, args{key: GetKey(3), arg: [][]byte{[]byte("field-0"), []byte("val-0")}}, false, 50 }, 51 { 52 "normal-mulit-pair", db, args{key: GetKey(4), arg: [][]byte{[]byte("field-0"), []byte("val-0"), 53 []byte("field-1"), []byte("val-1")}}, false, 54 }, 55 } 56 57 for _, tt := range tests { 58 t.Run(tt.name, func(t *testing.T) { 59 err := tt.db.HSet(tt.args.key, tt.args.arg...) 60 if (err != nil) != tt.wantErr { 61 t.Errorf("HSet() error = %v, wantErr %v", err, tt.wantErr) 62 } 63 if tt.wantErr && !errors.Is(err, ErrWrongNumberOfArgs) { 64 t.Errorf("HSet() error = %v, expected error = %v", err, ErrWrongNumberOfArgs) 65 } 66 }) 67 } 68 69 t.Run("check-nil-key-and-field", func(t *testing.T) { 70 val, err := db.HGet(nil, nil) 71 assert.Nil(t, err) 72 assert.Equal(t, []byte(nil), val) 73 }) 74 75 t.Run("check-single-field", func(t *testing.T) { 76 val, err := db.HGet(GetKey(3), []byte("field-0")) 77 assert.Nil(t, err) 78 assert.Equal(t, []byte("val-0"), val, "single field not same") 79 }) 80 81 t.Run("check-mulit-field", func(t *testing.T) { 82 value, err := db.HMGet(GetKey(4), []byte("field-0"), []byte("field-1")) 83 assert.Nil(t, err) 84 assert.Equal(t, [][]byte{[]byte("val-0"), []byte("val-1")}, value, "multi field not same") 85 }) 86 } 87 88 func TestRoseDB_HSetNX(t *testing.T) { 89 t.Run("fileio", func(t *testing.T) { 90 testRoseDBHSetNX(t, FileIO, KeyOnlyMemMode) 91 }) 92 t.Run("mmap", func(t *testing.T) { 93 testRoseDBHSetNX(t, MMap, KeyValueMemMode) 94 }) 95 } 96 97 func testRoseDBHSetNX(t *testing.T, ioType IOType, mode DataIndexMode) { 98 path := filepath.Join("/tmp", "rosedb") 99 opts := DefaultOptions(path) 100 opts.IoType = ioType 101 opts.IndexMode = mode 102 db, err := Open(opts) 103 assert.Nil(t, err) 104 defer destroyDB(db) 105 106 _ = db.HSet([]byte("key-1"), []byte("field-1"), []byte("value-1")) 107 _ = db.HSet([]byte("key-1"), []byte("field-2"), []byte("value-2")) 108 testCases := []struct { 109 name string 110 db *RoseDB 111 key []byte 112 field []byte 113 value []byte 114 expRes bool 115 expErr error 116 }{ 117 { 118 name: "Non-exist key", 119 db: db, 120 key: []byte("key-2"), 121 field: []byte("field-2"), 122 value: []byte("value-2"), 123 expRes: true, 124 expErr: nil, 125 }, 126 { 127 name: "Exist key", 128 db: db, 129 key: []byte("key-1"), 130 field: []byte("field-3"), 131 value: []byte("value-3"), 132 expRes: true, 133 expErr: nil, 134 }, 135 { 136 name: "Non-exist field", 137 db: db, 138 key: []byte("key-1"), 139 field: []byte("field-4"), 140 value: []byte("value-4"), 141 expRes: true, 142 expErr: nil, 143 }, 144 { 145 name: "Exist field", 146 db: db, 147 key: []byte("key-1"), 148 field: []byte("field-2"), 149 value: []byte("value-3"), 150 expRes: false, 151 expErr: nil, 152 }, 153 } 154 155 for _, tc := range testCases { 156 t.Run(tc.name, func(t *testing.T) { 157 ok, err := tc.db.HSetNX(tc.key, tc.field, tc.value) 158 assert.Equal(t, tc.expErr, err) 159 assert.Equal(t, tc.expRes, ok) 160 }) 161 } 162 } 163 164 func TestRoseDB_HGet(t *testing.T) { 165 t.Run("fileio", func(t *testing.T) { 166 testRoseDBHGet(t, FileIO, KeyOnlyMemMode) 167 }) 168 t.Run("mmap", func(t *testing.T) { 169 testRoseDBHGet(t, MMap, KeyValueMemMode) 170 }) 171 } 172 173 func testRoseDBHGet(t *testing.T, ioType IOType, mode DataIndexMode) { 174 path := filepath.Join("/tmp", "rosedb") 175 opts := DefaultOptions(path) 176 opts.IoType = ioType 177 opts.IndexMode = mode 178 db, err := Open(opts) 179 assert.Nil(t, err) 180 defer destroyDB(db) 181 182 setKey := []byte("my_set") 183 err = db.HSet(setKey, GetKey(1), GetKey(111)) 184 assert.Nil(t, err) 185 v1, err := db.HGet(setKey, GetKey(1)) 186 assert.Nil(t, err) 187 assert.Equal(t, GetKey(111), v1) 188 189 err = db.HSet(setKey, GetKey(1), GetKey(222)) 190 assert.Nil(t, err) 191 192 v2, err := db.HGet(setKey, GetKey(1)) 193 assert.Nil(t, err) 194 assert.Equal(t, GetKey(222), v2) 195 } 196 197 func TestRoseDB_HMGet(t *testing.T) { 198 t.Run("fileio", func(t *testing.T) { 199 testRoseDBHMGet(t, FileIO, KeyOnlyMemMode) 200 }) 201 t.Run("mmap", func(t *testing.T) { 202 testRoseDBHMGet(t, MMap, KeyValueMemMode) 203 }) 204 } 205 206 func testRoseDBHMGet(t *testing.T, ioType IOType, mode DataIndexMode) { 207 path := filepath.Join("/tmp", "rosedb") 208 opts := DefaultOptions(path) 209 opts.IoType = ioType 210 opts.IndexMode = mode 211 db, err := Open(opts) 212 assert.Nil(t, err) 213 defer destroyDB(db) 214 215 setKey := []byte("my_set") 216 err = db.HSet(setKey, GetKey(1), GetKey(111)) 217 assert.Nil(t, err) 218 219 v1, err := db.HMGet(setKey, GetKey(1)) 220 assert.Nil(t, err) 221 assert.Equal(t, [][]byte{GetKey(111)}, v1) 222 //-------------------------------------------------------------------------- 223 // newe cases 224 //-------------------------------------------------------------------------- 225 key := []byte("my_hash") 226 227 db.HSet(key, []byte("a"), []byte("hash_data_01")) 228 db.HSet(key, []byte("b"), []byte("hash_data_02")) 229 db.HSet(key, []byte("c"), []byte("hash_data_03")) 230 231 type args struct { 232 key []byte 233 field [][]byte 234 } 235 236 tests := []struct { 237 name string 238 args args 239 wantLen int 240 want [][]byte 241 wantErr bool 242 }{ 243 { 244 "nil", args{key: key, field: nil}, 0, nil, false, 245 }, 246 { 247 "not-exist-key", args{key: []byte("not-exist"), field: [][]byte{[]byte("a"), []byte("b")}}, 2, [][]byte{nil, nil}, false, 248 }, 249 { 250 "not-exist-field", args{key: key, field: [][]byte{[]byte("e")}}, 1, [][]byte{nil}, false, 251 }, 252 { 253 "normal", args{key: key, field: [][]byte{[]byte("a"), []byte("b"), []byte("c")}}, 3, 254 [][]byte{[]byte("hash_data_01"), []byte("hash_data_02"), []byte("hash_data_03")}, false, 255 }, 256 { 257 "normal-2", args{key: key, field: [][]byte{[]byte("a"), []byte("e"), []byte("c")}}, 3, 258 [][]byte{[]byte("hash_data_01"), nil, []byte("hash_data_03")}, false, 259 }, 260 } 261 // test 1 field get 262 val, err := db.HMGet(key, []byte("a")) 263 assert.Nil(t, err) 264 assert.Equal(t, [][]byte{[]byte("hash_data_01")}, val) 265 266 for _, tt := range tests { 267 t.Run(tt.name, func(t *testing.T) { 268 vals, err := db.HMGet(tt.args.key, tt.args.field...) 269 assert.Equal(t, tt.wantLen, len(vals), "the result len is not the same!") 270 assert.Equal(t, tt.want, vals, "the result is not the same!") 271 if (err != nil) != tt.wantErr { 272 t.Errorf("db.HMGet() error = %v, wantErr= %v", err, tt.wantErr) 273 } 274 }) 275 } 276 } 277 278 func TestRoseDB_HDel(t *testing.T) { 279 t.Run("fileio", func(t *testing.T) { 280 testRoseDBHDel(t, FileIO, KeyOnlyMemMode) 281 }) 282 t.Run("mmap", func(t *testing.T) { 283 testRoseDBHDel(t, MMap, KeyValueMemMode) 284 }) 285 } 286 287 func testRoseDBHDel(t *testing.T, ioType IOType, mode DataIndexMode) { 288 path := filepath.Join("/tmp", "rosedb") 289 opts := DefaultOptions(path) 290 opts.IoType = ioType 291 opts.IndexMode = mode 292 db, err := Open(opts) 293 assert.Nil(t, err) 294 defer destroyDB(db) 295 296 // not exist 297 setKey := []byte("my_set") 298 c1, err := db.HDel(setKey, GetKey(1), GetKey(2)) 299 assert.Nil(t, err) 300 assert.Equal(t, 0, c1) 301 302 err = db.HSet(setKey, GetKey(1), GetValue16B()) 303 assert.Nil(t, err) 304 err = db.HSet(setKey, GetKey(2), GetValue16B()) 305 assert.Nil(t, err) 306 err = db.HSet(setKey, GetKey(3), GetValue16B()) 307 assert.Nil(t, err) 308 309 c2, err := db.HDel(setKey, GetKey(3)) 310 assert.Nil(t, err) 311 assert.Equal(t, 1, c2) 312 313 v1, err := db.HGet(setKey, GetKey(3)) 314 assert.Nil(t, err) 315 assert.Nil(t, v1) 316 } 317 318 func TestRoseDB_HExists(t *testing.T) { 319 t.Run("fileio", func(t *testing.T) { 320 testRoseDBHExists(t, FileIO, KeyOnlyMemMode) 321 }) 322 t.Run("mmap", func(t *testing.T) { 323 testRoseDBHExists(t, MMap, KeyValueMemMode) 324 }) 325 } 326 327 func testRoseDBHExists(t *testing.T, ioType IOType, mode DataIndexMode) { 328 path := filepath.Join("/tmp", "rosedb") 329 opts := DefaultOptions(path) 330 opts.IoType = ioType 331 opts.IndexMode = mode 332 db, err := Open(opts) 333 assert.Nil(t, err) 334 defer destroyDB(db) 335 336 setKey := []byte("my_set") 337 err = db.HSet(setKey, GetKey(1), GetValue16B()) 338 assert.Nil(t, err) 339 340 c1, err := db.HExists(setKey, GetKey(1)) 341 assert.Nil(t, err) 342 assert.Equal(t, c1, true) 343 344 c2, err := db.HExists(setKey, GetKey(2)) 345 assert.Nil(t, err) 346 assert.Equal(t, c2, false) 347 } 348 349 func TestRoseDB_HLen(t *testing.T) { 350 path := filepath.Join("/tmp", "rosedb") 351 opts := DefaultOptions(path) 352 db, err := Open(opts) 353 assert.Nil(t, err) 354 defer destroyDB(db) 355 356 hashKey := []byte("my_hash") 357 l1 := db.HLen(hashKey) 358 assert.Equal(t, 0, l1) 359 360 err = db.HSet(hashKey, GetKey(1), GetValue16B()) 361 assert.Nil(t, err) 362 l2 := db.HLen(hashKey) 363 assert.Equal(t, 1, l2) 364 365 err = db.HSet(hashKey, GetKey(1), GetValue128B()) 366 assert.Nil(t, err) 367 368 err = db.HSet(hashKey, GetKey(2), GetValue16B()) 369 assert.Nil(t, err) 370 l3 := db.HLen(hashKey) 371 assert.Equal(t, 2, l3) 372 373 writeCount := 1000 374 for i := 0; i < writeCount; i++ { 375 err := db.HSet(hashKey, GetKey(i+100), GetValue16B()) 376 assert.Nil(t, err) 377 } 378 l4 := db.HLen(hashKey) 379 assert.Equal(t, writeCount+2, l4) 380 } 381 382 func TestRoseDB_DiscardStat_Hash(t *testing.T) { 383 helper := func(isDelete bool) { 384 path := filepath.Join("/tmp", "rosedb") 385 opts := DefaultOptions(path) 386 opts.LogFileSizeThreshold = 64 << 20 387 db, err := Open(opts) 388 assert.Nil(t, err) 389 defer destroyDB(db) 390 391 hashKey := []byte("my_hash") 392 writeCount := 500000 393 for i := 0; i < writeCount; i++ { 394 err := db.HSet(hashKey, GetKey(i), GetValue128B()) 395 assert.Nil(t, err) 396 } 397 398 if isDelete { 399 for i := 0; i < writeCount/2; i++ { 400 _, err := db.HDel(hashKey, GetKey(i)) 401 assert.Nil(t, err) 402 } 403 } else { 404 for i := 0; i < writeCount/2; i++ { 405 err := db.HSet(hashKey, GetKey(i), GetValue128B()) 406 assert.Nil(t, err) 407 } 408 } 409 _ = db.Sync() 410 ccl, err := db.discards[Hash].getCCL(10, 0.5) 411 assert.Nil(t, err) 412 assert.Equal(t, 1, len(ccl)) 413 } 414 415 t.Run("rewrite", func(t *testing.T) { 416 helper(false) 417 }) 418 419 t.Run("delete", func(t *testing.T) { 420 helper(true) 421 }) 422 } 423 424 func TestRoseDB_HashGC(t *testing.T) { 425 path := filepath.Join("/tmp", "rosedb") 426 opts := DefaultOptions(path) 427 opts.LogFileSizeThreshold = 64 << 20 428 db, err := Open(opts) 429 assert.Nil(t, err) 430 defer destroyDB(db) 431 432 hashKey := []byte("my_hash") 433 writeCount := 500000 434 for i := 0; i < writeCount; i++ { 435 err := db.HSet(hashKey, GetKey(i), GetValue16B()) 436 assert.Nil(t, err) 437 } 438 for i := 0; i < writeCount/2; i++ { 439 _, err := db.HDel(hashKey, GetKey(i)) 440 assert.Nil(t, err) 441 } 442 443 err = db.RunLogFileGC(Hash, 0, 0.4) 444 assert.Nil(t, err) 445 446 l1 := db.HLen(hashKey) 447 assert.Equal(t, writeCount/2, l1) 448 } 449 450 func TestRoseDB_HKeys(t *testing.T) { 451 path := filepath.Join("/tmp", "rosedb") 452 opts := DefaultOptions(path) 453 db, err := Open(opts) 454 assert.Nil(t, err) 455 defer destroyDB(db) 456 457 hashKey := []byte("my_hash") 458 keys, err := db.HKeys(hashKey) 459 assert.Nil(t, err) 460 assert.Equal(t, 0, len(keys)) 461 462 err = db.HSet(hashKey, GetKey(1), GetValue16B()) 463 assert.Nil(t, err) 464 keys, err = db.HKeys(hashKey) 465 assert.Nil(t, err) 466 assert.Equal(t, 1, len(keys)) 467 assert.Equal(t, GetKey(1), keys[0]) 468 469 err = db.HSet(hashKey, GetKey(1), GetValue128B()) 470 assert.Nil(t, err) 471 keys, err = db.HKeys(hashKey) 472 assert.Nil(t, err) 473 assert.Equal(t, 1, len(keys)) 474 assert.Equal(t, GetKey(1), keys[0]) 475 476 err = db.HSet(hashKey, GetKey(2), GetValue16B()) 477 assert.Nil(t, err) 478 keys, err = db.HKeys(hashKey) 479 assert.Nil(t, err) 480 assert.Equal(t, 2, len(keys)) 481 assert.Equal(t, [][]byte{GetKey(1), GetKey(2)}, keys) 482 483 writeCount := 1000 484 for i := 0; i < writeCount; i++ { 485 err := db.HSet(hashKey, GetKey(i+100), GetValue16B()) 486 assert.Nil(t, err) 487 } 488 keys, err = db.HKeys(hashKey) 489 assert.Nil(t, err) 490 for i := 0; i < writeCount; i++ { 491 assert.Equal(t, GetKey(i+100), keys[i+2]) 492 } 493 } 494 495 func TestRoseDB_HVals(t *testing.T) { 496 cases := []struct { 497 IOType 498 DataIndexMode 499 }{ 500 {FileIO, KeyValueMemMode}, 501 {FileIO, KeyOnlyMemMode}, 502 {MMap, KeyValueMemMode}, 503 {MMap, KeyOnlyMemMode}, 504 } 505 506 oneRun := func(t *testing.T, opts Options) { 507 db, err := Open(opts) 508 assert.Nil(t, err) 509 defer destroyDB(db) 510 511 hashKey := []byte("my_hash") 512 vals, err := db.HVals(hashKey) 513 assert.Nil(t, err) 514 assert.Equal(t, 0, len(vals)) 515 516 val16B := GetValue16B() 517 err = db.HSet(hashKey, GetKey(1), val16B) 518 assert.Nil(t, err) 519 vals, err = db.HVals(hashKey) 520 assert.Nil(t, err) 521 assert.Equal(t, 1, len(vals)) 522 assert.Equal(t, val16B, vals[0]) 523 524 val128B := GetValue128B() 525 err = db.HSet(hashKey, GetKey(1), val128B) 526 assert.Nil(t, err) 527 vals, err = db.HVals(hashKey) 528 assert.Nil(t, err) 529 assert.Equal(t, 1, len(vals)) 530 assert.Equal(t, val128B, vals[0]) 531 532 err = db.HSet(hashKey, GetKey(2), val16B) 533 assert.Nil(t, err) 534 vals, err = db.HVals(hashKey) 535 assert.Nil(t, err) 536 assert.Equal(t, 2, len(vals)) 537 assert.Equal(t, [][]byte{val128B, val16B}, vals) 538 539 val16B = GetValue16B() 540 writeCount := 1000 541 for i := 0; i < writeCount; i++ { 542 err := db.HSet(hashKey, GetKey(i+100), val16B) 543 assert.Nil(t, err) 544 } 545 vals, err = db.HVals(hashKey) 546 assert.Nil(t, err) 547 for i := 0; i < writeCount; i++ { 548 assert.Equal(t, val16B, vals[i+2]) 549 } 550 } 551 552 for _, c := range cases { 553 path := filepath.Join("/tmp", "rosedb") 554 opts := DefaultOptions(path) 555 opts.IoType = c.IOType 556 opts.IndexMode = c.DataIndexMode 557 oneRun(t, opts) 558 } 559 } 560 561 func TestRoseDB_HGetAll(t *testing.T) { 562 cases := []struct { 563 IOType 564 DataIndexMode 565 }{ 566 {FileIO, KeyValueMemMode}, 567 {FileIO, KeyOnlyMemMode}, 568 {MMap, KeyValueMemMode}, 569 {MMap, KeyOnlyMemMode}, 570 } 571 572 oneRun := func(t *testing.T, opts Options) { 573 db, err := Open(opts) 574 assert.Nil(t, err) 575 defer destroyDB(db) 576 577 hashKey := []byte("my_hash") 578 pairs, err := db.HGetAll(hashKey) 579 assert.Nil(t, err) 580 assert.Equal(t, 0, len(pairs)) 581 582 // one 583 val16B := GetValue16B() 584 err = db.HSet(hashKey, GetKey(1), val16B) 585 assert.Nil(t, err) 586 pairs, err = db.HGetAll(hashKey) 587 assert.Nil(t, err) 588 assert.Equal(t, 2, len(pairs)) 589 assert.Equal(t, [][]byte{GetKey(1), val16B}, pairs) 590 591 val128B := GetValue128B() 592 err = db.HSet(hashKey, GetKey(1), val128B) 593 assert.Nil(t, err) 594 pairs, err = db.HGetAll(hashKey) 595 assert.Nil(t, err) 596 assert.Equal(t, 2, len(pairs)) 597 assert.Equal(t, [][]byte{GetKey(1), val128B}, pairs) 598 599 // two 600 err = db.HSet(hashKey, GetKey(2), val16B) 601 assert.Nil(t, err) 602 pairs, err = db.HGetAll(hashKey) 603 assert.Nil(t, err) 604 assert.Equal(t, 4, len(pairs)) 605 assert.Equal(t, GetKey(1), pairs[0]) 606 assert.Equal(t, [][]byte{GetKey(1), val128B, GetKey(2), val16B}, pairs) 607 } 608 609 for _, c := range cases { 610 path := filepath.Join("/tmp", "rosedb") 611 opts := DefaultOptions(path) 612 opts.IoType = c.IOType 613 opts.IndexMode = c.DataIndexMode 614 oneRun(t, opts) 615 } 616 } 617 618 func TestRoseDB_HStrLen(t *testing.T) { 619 cases := []struct { 620 IOType 621 DataIndexMode 622 }{ 623 {FileIO, KeyValueMemMode}, 624 {FileIO, KeyOnlyMemMode}, 625 {MMap, KeyValueMemMode}, 626 {MMap, KeyOnlyMemMode}, 627 } 628 oneRun := func(t *testing.T, opts Options) { 629 db, err := Open(opts) 630 assert.Nil(t, err) 631 defer destroyDB(db) 632 633 hashKey := []byte("my_hash") 634 key1 := GetKey(1) 635 kLen := db.HStrLen(hashKey, key1) 636 assert.Nil(t, err) 637 assert.Equal(t, 0, kLen) 638 639 for i := 0; i < 10; i++ { 640 key := GetKey(i) 641 val := GetValue(i) 642 err = db.HSet(hashKey, key, val) 643 assert.Nil(t, err) 644 kLen = db.HStrLen(hashKey, key) 645 assert.Nil(t, err) 646 assert.Equal(t, kLen, len(val)) 647 } 648 } 649 650 for _, c := range cases { 651 path := filepath.Join("/tmp", "rosedb") 652 opts := DefaultOptions(path) 653 opts.IoType = c.IOType 654 opts.IndexMode = c.DataIndexMode 655 oneRun(t, opts) 656 } 657 } 658 659 func TestRoseDB_HScan(t *testing.T) { 660 path := filepath.Join("/tmp", "rosedb") 661 opts := DefaultOptions(path) 662 db, err := Open(opts) 663 assert.Nil(t, err) 664 defer destroyDB(db) 665 666 setKey := []byte("my_set") 667 err = db.HSet(setKey, GetKey(32), GetValue16B()) 668 assert.Nil(t, err) 669 err = db.HSet(setKey, GetKey(21), GetValue16B()) 670 assert.Nil(t, err) 671 err = db.HSet(setKey, GetKey(14), GetValue16B()) 672 assert.Nil(t, err) 673 err = db.HSet(setKey, GetKey(43), GetValue16B()) 674 assert.Nil(t, err) 675 676 values, err := db.HScan(setKey, []byte("kv"), "", 100) 677 assert.Nil(t, err) 678 assert.Equal(t, 8, len(values)) 679 } 680 681 func TestRoseDB_HIncrBy(t *testing.T) { 682 cases := []struct { 683 IOType 684 DataIndexMode 685 }{ 686 {FileIO, KeyValueMemMode}, 687 {FileIO, KeyOnlyMemMode}, 688 {MMap, KeyValueMemMode}, 689 {MMap, KeyOnlyMemMode}, 690 } 691 692 oneRun := func(t *testing.T, opts Options) { 693 db, err := Open(opts) 694 assert.Nil(t, err) 695 defer destroyDB(db) 696 697 // both key and field do not exist 698 hashKey := []byte("my_hash") 699 field1 := []byte("field1") 700 valInt64, err := db.HIncrBy(hashKey, field1, 1) 701 assert.Nil(t, err) 702 assert.Equal(t, int64(1), valInt64) 703 valByte, err := db.HGet(hashKey, field1) 704 assert.Nil(t, err) 705 assert.Equal(t, []byte("1"), valByte) 706 707 // field does not exist 708 field2 := []byte("field2") 709 valInt64, err = db.HIncrBy(hashKey, field2, 2) 710 assert.Nil(t, err) 711 assert.Equal(t, int64(2), valInt64) 712 valByte, err = db.HGet(hashKey, field2) 713 assert.Nil(t, err) 714 assert.Equal(t, []byte("2"), valByte) 715 716 // increment(1 + 2) 717 valInt64, err = db.HIncrBy(hashKey, field1, 2) 718 assert.Nil(t, err) 719 assert.Equal(t, int64(3), valInt64) 720 valByte, err = db.HGet(hashKey, field1) 721 assert.Nil(t, err) 722 assert.Equal(t, []byte("3"), valByte) 723 724 // negative incr(3 - 4) 725 valInt64, err = db.HIncrBy(hashKey, field1, -4) 726 assert.Nil(t, err) 727 assert.Equal(t, int64(-1), valInt64) 728 valByte, err = db.HGet(hashKey, field1) 729 assert.Nil(t, err) 730 assert.Equal(t, []byte("-1"), valByte) 731 732 // overflow value-min(-1 + math.MinInt64) 733 _, err = db.HIncrBy(hashKey, field1, math.MinInt64) 734 assert.Equal(t, ErrIntegerOverflow, err) 735 736 // overflow value-max(2 + math.MaxInt64) 737 _, err = db.HIncrBy(hashKey, field2, math.MaxInt64) 738 assert.Equal(t, ErrIntegerOverflow, err) 739 740 // wrong value type 741 wrongField := []byte("wrong_field") 742 err = db.HSet(hashKey, wrongField, []byte("wrong_val")) 743 assert.Nil(t, err) 744 _, err = db.HIncrBy(hashKey, wrongField, 1) 745 assert.Equal(t, ErrWrongValueType, err) 746 } 747 748 for _, c := range cases { 749 path := filepath.Join("/tmp", "rosedb") 750 opts := DefaultOptions(path) 751 opts.IoType = c.IOType 752 opts.IndexMode = c.DataIndexMode 753 oneRun(t, opts) 754 } 755 } 756 757 func TestRoseDB_HRandField(t *testing.T) { 758 cases := []struct { 759 IOType 760 DataIndexMode 761 }{ 762 {FileIO, KeyValueMemMode}, 763 {FileIO, KeyOnlyMemMode}, 764 {MMap, KeyValueMemMode}, 765 {MMap, KeyOnlyMemMode}, 766 } 767 768 hashKey := []byte("my_hash") 769 field1, field2, field3, field4, field5 := []byte("field1"), []byte("field2"), []byte("field3"), []byte("field4"), []byte("field5") 770 value1, value2, value3, value4, value5 := []byte("value1"), []byte("value2"), []byte("value3"), []byte("value4"), []byte("value5") 771 fields := [][]byte{field1, field2, field3, field4, field5} 772 values := [][]byte{value1, value2, value3, value4, value5} 773 774 distinctFunc := func(pairs [][]byte, count int, withValues bool) { 775 if count > len(fields) { 776 count = len(fields) 777 } 778 var pairLength = 1 779 if withValues { 780 pairLength = 2 781 } 782 pairCount := len(pairs) / pairLength 783 assert.Equal(t, count, pairCount) 784 // only key of the pair should be able to be compared. 785 for i := 0; i < pairCount; i++ { 786 assert.Contains(t, fields, pairs[i*pairLength]) 787 for j := 0; j < pairCount; j++ { 788 if i == j { 789 continue 790 } 791 assert.NotEqual(t, pairs[i*pairLength], pairs[j*pairLength]) 792 } 793 } 794 } 795 duplicationFunc := func(pairs [][]byte, count int, withValues bool) { 796 var pairLength = 1 797 if withValues { 798 pairLength = 2 799 } 800 pairCount := len(pairs) / pairLength 801 assert.Equal(t, count, pairCount) 802 for i := 0; i < pairCount; i++ { 803 assert.Contains(t, fields, pairs[i*pairLength]) 804 if withValues { 805 assert.Contains(t, values, pairs[i*pairLength+1]) 806 } 807 } 808 } 809 810 run := func(t *testing.T, opts Options) { 811 db, err := Open(opts) 812 assert.Nil(t, err) 813 defer destroyDB(db) 814 const withValues = false 815 816 _ = db.HSet(hashKey, field1, value1, field2, value2, field3, value3, field4, value4, field5, value5) 817 818 // empty 819 keys, err := db.HRandField(hashKey, 0, withValues) 820 assert.Nil(t, err) 821 assert.Equal(t, 0, len(keys)) 822 823 // key not found 824 keys, err = db.HRandField([]byte("key-not-found"), 1, withValues) 825 assert.Nil(t, err) 826 assert.Equal(t, 0, len(keys)) 827 828 // return a random field from the hash value 829 keys, err = db.HRandField(hashKey, 1, withValues) 830 assert.Nil(t, err) 831 assert.Equal(t, 1, len(keys)) 832 assert.Contains(t, fields, keys[0]) 833 834 // return random fields from the hash value by count i 835 for i := 1; i <= 10; i++ { 836 keys, err = db.HRandField(hashKey, i, false) 837 assert.Nil(t, err) 838 distinctFunc(keys, i, withValues) 839 } 840 841 // return the same field multiple times by count -i 842 for i := 1; i <= 10; i++ { 843 keys, err = db.HRandField(hashKey, -i, withValues) 844 assert.Nil(t, err) 845 duplicationFunc(keys, i, withValues) 846 } 847 } 848 849 runWithValues := func(t *testing.T, opts Options) { 850 db, err := Open(opts) 851 assert.Nil(t, err) 852 defer destroyDB(db) 853 const withValues = true 854 855 _ = db.HSet(hashKey, field1, value1, field2, value2, field3, value3, field4, value4, field5, value5) 856 857 // empty 858 pairs, err := db.HRandField(hashKey, 0, withValues) 859 assert.Nil(t, err) 860 assert.Equal(t, 0, len(pairs)) 861 862 // key not found 863 pairs, err = db.HRandField([]byte("key-not-found"), 1, withValues) 864 assert.Nil(t, err) 865 assert.Equal(t, 0, len(pairs)) 866 867 // return a random field from the hash value 868 pairs, err = db.HRandField(hashKey, 1, withValues) 869 assert.Nil(t, err) 870 assert.Equal(t, 2, len(pairs)) 871 assert.Contains(t, fields, pairs[0]) 872 assert.Contains(t, values, pairs[1]) 873 874 // return random pairs from the hash value by count i 875 for i := 1; i <= 10; i++ { 876 pairs, err = db.HRandField(hashKey, i, withValues) 877 assert.Nil(t, err) 878 distinctFunc(pairs, i, withValues) 879 } 880 881 // return the same pairs multiple times by count -i 882 for i := 1; i <= 10; i++ { 883 pairs, err = db.HRandField(hashKey, -i, withValues) 884 assert.Nil(t, err) 885 duplicationFunc(pairs, i, withValues) 886 } 887 } 888 889 for _, c := range cases { 890 path := filepath.Join("/tmp", "rosedb") 891 opts := DefaultOptions(path) 892 opts.IoType = c.IOType 893 opts.IndexMode = c.DataIndexMode 894 run(t, opts) 895 runWithValues(t, opts) 896 } 897 }