-
-
Notifications
You must be signed in to change notification settings - Fork 6
/
redis_cache_test.go
192 lines (160 loc) · 5.26 KB
/
redis_cache_test.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
package lcw
import (
"context"
"fmt"
"sort"
"sync/atomic"
"testing"
"time"
"github.com/alicebob/miniredis/v2"
"github.com/redis/go-redis/v9"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// newTestRedis returns a redis.Cmdable.
func newTestRedisServer() *miniredis.Miniredis {
mr, err := miniredis.Run()
if err != nil {
panic(err)
}
return mr
}
type fakeString string
func TestExpirableRedisCache(t *testing.T) {
server := newTestRedisServer()
defer server.Close()
client := redis.NewClient(&redis.Options{
Addr: server.Addr()})
defer client.Close()
rc, err := NewRedisCache(client, MaxKeys(5), TTL(time.Second*6))
require.NoError(t, err)
defer rc.Close()
require.NoError(t, err)
for i := 0; i < 5; i++ {
i := i
_, e := rc.Get(fmt.Sprintf("key-%d", i), func() (interface{}, error) {
return fmt.Sprintf("result-%d", i), nil
})
assert.NoError(t, e)
server.FastForward(1000 * time.Millisecond)
}
assert.Equal(t, 5, rc.Stat().Keys)
assert.Equal(t, int64(5), rc.Stat().Misses)
keys := rc.Keys()
sort.Slice(keys, func(i, j int) bool { return keys[i] < keys[j] })
assert.EqualValues(t, []string{"key-0", "key-1", "key-2", "key-3", "key-4"}, keys)
_, e := rc.Get("key-xx", func() (interface{}, error) {
return "result-xx", nil
})
assert.NoError(t, e)
assert.Equal(t, 5, rc.Stat().Keys)
assert.Equal(t, int64(6), rc.Stat().Misses)
server.FastForward(1000 * time.Millisecond)
assert.Equal(t, 4, rc.Stat().Keys)
server.FastForward(4000 * time.Millisecond)
assert.Equal(t, 0, rc.keys())
}
func TestRedisCache(t *testing.T) {
var coldCalls int32
server := newTestRedisServer()
defer server.Close()
client := redis.NewClient(&redis.Options{
Addr: server.Addr()})
defer client.Close()
rc, err := NewRedisCache(client, MaxKeys(5), MaxValSize(10), MaxKeySize(10))
require.NoError(t, err)
defer rc.Close()
// put 5 keys to cache
for i := 0; i < 5; i++ {
i := i
res, e := rc.Get(fmt.Sprintf("key-%d", i), func() (interface{}, error) {
atomic.AddInt32(&coldCalls, 1)
return fmt.Sprintf("result-%d", i), nil
})
assert.NoError(t, e)
assert.Equal(t, fmt.Sprintf("result-%d", i), res.(string))
assert.Equal(t, int32(i+1), atomic.LoadInt32(&coldCalls))
}
// check if really cached
res, err := rc.Get("key-3", func() (interface{}, error) {
return "result-blah", nil
})
assert.NoError(t, err)
assert.Equal(t, "result-3", res.(string), "should be cached")
// try to cache after maxKeys reached
res, err = rc.Get("key-X", func() (interface{}, error) {
return "result-X", nil
})
assert.NoError(t, err)
assert.Equal(t, "result-X", res.(string))
assert.Equal(t, int64(5), rc.backend.DBSize(context.Background()).Val())
// put to cache and make sure it cached
res, err = rc.Get("key-Z", func() (interface{}, error) {
return "result-Z", nil
})
assert.NoError(t, err)
assert.Equal(t, "result-Z", res.(string))
res, err = rc.Get("key-Z", func() (interface{}, error) {
return "result-Zzzz", nil
})
assert.NoError(t, err)
assert.Equal(t, "result-Zzzz", res.(string), "got non-cached value")
assert.Equal(t, 5, rc.keys())
res, err = rc.Get("key-Zzzzzzz", func() (interface{}, error) {
return "result-Zzzz", nil
})
assert.NoError(t, err)
assert.Equal(t, "result-Zzzz", res.(string), "got non-cached value")
assert.Equal(t, 5, rc.keys())
res, ok := rc.Peek("error-key-Z2")
assert.False(t, ok)
assert.Nil(t, res)
}
func TestRedisCacheErrors(t *testing.T) {
server := newTestRedisServer()
defer server.Close()
client := redis.NewClient(&redis.Options{
Addr: server.Addr()})
defer client.Close()
rc, err := NewRedisCache(client)
require.NoError(t, err)
defer rc.Close()
res, err := rc.Get("error-key-Z", func() (interface{}, error) {
return "error-result-Z", fmt.Errorf("some error")
})
assert.Error(t, err)
assert.Equal(t, "error-result-Z", res.(string))
assert.Equal(t, int64(1), rc.Stat().Errors)
res, err = rc.Get("error-key-Z2", func() (interface{}, error) {
return fakeString("error-result-Z2"), nil
})
assert.Error(t, err)
assert.Equal(t, fakeString("error-result-Z2"), res.(fakeString))
assert.Equal(t, int64(2), rc.Stat().Errors)
server.Close()
res, err = rc.Get("error-key-Z3", func() (interface{}, error) {
return fakeString("error-result-Z3"), nil
})
assert.Error(t, err)
assert.Equal(t, "", res.(string))
assert.Equal(t, int64(3), rc.Stat().Errors)
}
func TestRedisCache_BadOptions(t *testing.T) {
server := newTestRedisServer()
defer server.Close()
client := redis.NewClient(&redis.Options{
Addr: server.Addr()})
defer client.Close()
_, err := NewRedisCache(client, MaxCacheSize(-1))
assert.EqualError(t, err, "failed to set cache option: negative max cache size")
_, err = NewRedisCache(client, MaxCacheSize(-1))
assert.EqualError(t, err, "failed to set cache option: negative max cache size")
_, err = NewRedisCache(client, MaxKeys(-1))
assert.EqualError(t, err, "failed to set cache option: negative max keys")
_, err = NewRedisCache(client, MaxValSize(-1))
assert.EqualError(t, err, "failed to set cache option: negative max value size")
_, err = NewRedisCache(client, TTL(-1))
assert.EqualError(t, err, "failed to set cache option: negative ttl")
_, err = NewRedisCache(client, MaxKeySize(-1))
assert.EqualError(t, err, "failed to set cache option: negative max key size")
}