sourcegraph/internal/rcache/rcache_test.go
Julie Tibshirani e65e736cb1
Redis: simplify DeleteAllKeysWithPrefix (#64407)
The `DeleteAllKeysWithPrefix` method is now only used in tests to ensure
the test keyspace is clear. This PR makes it clear this method is only
used in tests, and simplifies the implementation so it no longer needs a
script + direct Redis connection.

Another tiny Redis refactor to prepare for multi-tenancy work.
2024-08-12 15:25:33 +03:00

224 lines
5.0 KiB
Go

package rcache
import (
"testing"
"time"
"github.com/stretchr/testify/assert"
)
func TestCache_namespace(t *testing.T) {
kv := SetupForTest(t)
type testcase struct {
prefix string
entries map[string]string
}
cases := []testcase{
{
prefix: "a",
entries: map[string]string{
"k0": "v0",
"k1": "v1",
"k2": "v2",
},
}, {
prefix: "b",
entries: map[string]string{
"k0": "v0",
"k1": "v1",
"k2": "v2",
},
}, {
prefix: "c",
entries: map[string]string{
"k0": "v0",
"k1": "v1",
"k2": "v2",
},
},
}
caches := make([]*Cache, len(cases))
for i, test := range cases {
caches[i] = New(kv, test.prefix)
for k, v := range test.entries {
caches[i].Set(k, []byte(v))
}
}
for i, test := range cases {
// test all the keys that should be present are found
for k, v := range test.entries {
b, ok := caches[i].Get(k)
if !ok {
t.Fatalf("error getting entry from redis (prefix=%s)", test.prefix)
}
if string(b) != v {
t.Errorf("expected %s, got %s", v, string(b))
}
}
// test not found case
if _, ok := caches[i].Get("not-found"); ok {
t.Errorf("expected not found")
}
}
}
func TestCache_simple(t *testing.T) {
kv := SetupForTest(t)
c := New(kv, "some_prefix")
_, ok := c.Get("a")
if ok {
t.Fatal("Initial Get should find nothing")
}
c.Set("a", []byte("b"))
b, ok := c.Get("a")
if !ok {
t.Fatal("Expect to get a after setting")
}
if string(b) != "b" {
t.Fatalf("got %v, want %v", string(b), "b")
}
c.Delete("a")
_, ok = c.Get("a")
if ok {
t.Fatal("Get after delete should of found nothing")
}
}
func TestCache_Increase(t *testing.T) {
kv := SetupForTest(t)
c := NewWithTTL(kv, "some_prefix", 1)
c.Increase("a")
got, ok := c.Get("a")
assert.True(t, ok)
assert.Equal(t, []byte("1"), got)
time.Sleep(time.Second)
// now wait upto another 5s. We do this because timing is hard.
assert.Eventually(t, func() bool {
_, ok = c.Get("a")
return !ok
}, 5*time.Second, 50*time.Millisecond, "rcache.increase did not respect expiration")
}
func TestCache_KeyTTL(t *testing.T) {
kv := SetupForTest(t)
c := NewWithTTL(kv, "some_prefix", 1)
c.Set("a", []byte("b"))
ttl, ok := c.KeyTTL("a")
assert.True(t, ok)
assert.Equal(t, 1, ttl)
time.Sleep(time.Second)
// now wait upto another 5s. We do this because timing is hard.
assert.Eventually(t, func() bool {
_, ok = c.KeyTTL("a")
return !ok
}, 5*time.Second, 50*time.Millisecond, "rcache.ketttl did not respect expiration")
c.SetWithTTL("c", []byte("d"), 0) // invalid TTL
_, ok = c.KeyTTL("c")
if ok {
t.Fatal("KeyTTL after setting invalid ttl should have found nothing")
}
}
func TestCache_SetWithTTL(t *testing.T) {
kv := SetupForTest(t)
c := NewWithTTL(kv, "some_prefix", 60)
c.SetWithTTL("a", []byte("b"), 30)
b, ok := c.Get("a")
if !ok {
t.Fatal("Expect to get a after setting")
}
if string(b) != "b" {
t.Fatalf("got %v, want %v", string(b), "b")
}
ttl, ok := c.KeyTTL("a")
if !ok {
t.Fatal("Expect to be able to read ttl after setting")
}
if ttl > 30 {
t.Fatalf("ttl got %v, want %v", ttl, 30)
}
c.Delete("a")
_, ok = c.Get("a")
if ok {
t.Fatal("Get after delete should have found nothing")
}
c.SetWithTTL("c", []byte("d"), 0) // invalid operation
_, ok = c.Get("c")
if ok {
t.Fatal("SetWithTTL should not create a key with invalid expiry")
}
}
func TestCache_Hashes(t *testing.T) {
kv := SetupForTest(t)
// Test SetHashItem
c := NewWithTTL(kv, "simple_hash", 1)
err := c.SetHashItem("key", "hashKey1", "value1")
assert.NoError(t, err)
err = c.SetHashItem("key", "hashKey2", "value2")
assert.NoError(t, err)
// Test GetHashItem
val1, err := c.GetHashItem("key", "hashKey1")
assert.NoError(t, err)
assert.Equal(t, "value1", val1)
val2, err := c.GetHashItem("key", "hashKey2")
assert.NoError(t, err)
assert.Equal(t, "value2", val2)
val3, err := c.GetHashItem("key", "hashKey3")
assert.Error(t, err)
assert.Equal(t, "", val3)
// Test GetHashAll
all, err := c.GetHashAll("key")
assert.NoError(t, err)
assert.Equal(t, map[string]string{"hashKey1": "value1", "hashKey2": "value2"}, all)
// Test DeleteHashItem
// Bit redundant, but double check that the key still exists
val1, err = c.GetHashItem("key", "hashKey1")
assert.NoError(t, err)
assert.Equal(t, "value1", val1)
del1, err := c.DeleteHashItem("key", "hashKey1")
assert.NoError(t, err)
assert.Equal(t, 1, del1)
// Verify that it no longer exists
val1, err = c.GetHashItem("key", "hashKey1")
assert.Error(t, err)
assert.Equal(t, "", val1)
// Delete nonexistent field: should return 0 (represents deleted items)
val3, err = c.GetHashItem("key", "hashKey3")
assert.Error(t, err)
assert.Equal(t, "", val3)
del3, err := c.DeleteHashItem("key", "hashKey3")
assert.NoError(t, err)
assert.Equal(t, 0, del3)
// Delete nonexistent key: should return 0 (represents deleted items)
val4, err := c.GetHashItem("nonexistentkey", "nonexistenthashkey")
assert.Error(t, err)
assert.Equal(t, "", val4)
del4, err := c.DeleteHashItem("nonexistentkey", "nonexistenthashkey")
assert.NoError(t, err)
assert.Equal(t, 0, del4)
}