1
16
17 package disk
18
19 import (
20 "bytes"
21 "crypto/sha256"
22 "io"
23 "net/http"
24 "net/url"
25 "os"
26 "path/filepath"
27 "testing"
28
29 "github.com/peterbourgon/diskv"
30 "github.com/stretchr/testify/assert"
31 )
32
33
34 type testRoundTripper struct {
35 Request *http.Request
36 Response *http.Response
37 Err error
38 }
39
40 func (rt *testRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
41 rt.Request = req
42 return rt.Response, rt.Err
43 }
44
45 func BenchmarkDiskCache(b *testing.B) {
46 cacheDir, err := os.MkdirTemp("", "cache-rt")
47 if err != nil {
48 b.Fatal(err)
49 }
50 defer os.RemoveAll(cacheDir)
51
52 d := diskv.New(diskv.Options{
53 PathPerm: os.FileMode(0750),
54 FilePerm: os.FileMode(0660),
55 BasePath: cacheDir,
56 TempDir: filepath.Join(cacheDir, ".diskv-temp"),
57 })
58
59 k := "localhost:8080/apis/batch/v1.json"
60 v, err := os.ReadFile("../../testdata/apis/batch/v1.json")
61 if err != nil {
62 b.Fatal(err)
63 }
64
65 c := sumDiskCache{disk: d}
66
67 for n := 0; n < b.N; n++ {
68 c.Set(k, v)
69 c.Get(k)
70 c.Delete(k)
71 }
72 }
73
74 func TestCacheRoundTripper(t *testing.T) {
75 rt := &testRoundTripper{}
76 cacheDir, err := os.MkdirTemp("", "cache-rt")
77 defer os.RemoveAll(cacheDir)
78 if err != nil {
79 t.Fatal(err)
80 }
81 cache := newCacheRoundTripper(cacheDir, rt)
82
83
84 req := &http.Request{
85 Method: http.MethodGet,
86 URL: &url.URL{Host: "localhost"},
87 }
88 rt.Response = &http.Response{
89 Header: http.Header{"ETag": []string{`"123456"`}},
90 Body: io.NopCloser(bytes.NewReader([]byte("Content"))),
91 StatusCode: http.StatusOK,
92 }
93 resp, err := cache.RoundTrip(req)
94 if err != nil {
95 t.Fatal(err)
96 }
97 content, err := io.ReadAll(resp.Body)
98 if err != nil {
99 t.Fatal(err)
100 }
101 if string(content) != "Content" {
102 t.Errorf(`Expected Body to be "Content", got %q`, string(content))
103 }
104
105
106 req = &http.Request{
107 Method: http.MethodGet,
108 URL: &url.URL{Host: "localhost"},
109 }
110 rt.Response = &http.Response{
111 StatusCode: http.StatusNotModified,
112 Body: io.NopCloser(bytes.NewReader([]byte("Other Content"))),
113 }
114
115 resp, err = cache.RoundTrip(req)
116 if err != nil {
117 t.Fatal(err)
118 }
119
120
121 content, err = io.ReadAll(resp.Body)
122 resp.Body.Close()
123 if err != nil {
124 t.Fatal(err)
125 }
126 if string(content) != "Content" {
127 t.Errorf("Invalid content read from cache %q", string(content))
128 }
129 }
130
131 func TestCacheRoundTripperPathPerm(t *testing.T) {
132 assert := assert.New(t)
133
134 rt := &testRoundTripper{}
135 cacheDir, err := os.MkdirTemp("", "cache-rt")
136 os.RemoveAll(cacheDir)
137 defer os.RemoveAll(cacheDir)
138
139 if err != nil {
140 t.Fatal(err)
141 }
142 cache := newCacheRoundTripper(cacheDir, rt)
143
144
145 req := &http.Request{
146 Method: http.MethodGet,
147 URL: &url.URL{Host: "localhost"},
148 }
149 rt.Response = &http.Response{
150 Header: http.Header{"ETag": []string{`"123456"`}},
151 Body: io.NopCloser(bytes.NewReader([]byte("Content"))),
152 StatusCode: http.StatusOK,
153 }
154 resp, err := cache.RoundTrip(req)
155 if err != nil {
156 t.Fatal(err)
157 }
158 content, err := io.ReadAll(resp.Body)
159 if err != nil {
160 t.Fatal(err)
161 }
162 if string(content) != "Content" {
163 t.Errorf(`Expected Body to be "Content", got %q`, string(content))
164 }
165
166 err = filepath.Walk(cacheDir, func(path string, info os.FileInfo, err error) error {
167 if err != nil {
168 return err
169 }
170 if info.IsDir() {
171 assert.Equal(os.FileMode(0750), info.Mode().Perm())
172 } else {
173 assert.Equal(os.FileMode(0660), info.Mode().Perm())
174 }
175 return nil
176 })
177 assert.NoError(err)
178 }
179
180 func TestSumDiskCache(t *testing.T) {
181 assert := assert.New(t)
182
183
184 t.Run("NoSuchKey", func(t *testing.T) {
185 cacheDir, err := os.MkdirTemp("", "cache-test")
186 if err != nil {
187 t.Fatal(err)
188 }
189 defer os.RemoveAll(cacheDir)
190 d := diskv.New(diskv.Options{BasePath: cacheDir, TempDir: filepath.Join(cacheDir, ".diskv-temp")})
191 c := &sumDiskCache{disk: d}
192
193 key := "testing"
194
195 got, ok := c.Get(key)
196 assert.False(ok)
197 assert.Equal([]byte{}, got)
198 })
199
200
201 t.Run("EmptyFile", func(t *testing.T) {
202 cacheDir, err := os.MkdirTemp("", "cache-test")
203 if err != nil {
204 t.Fatal(err)
205 }
206 defer os.RemoveAll(cacheDir)
207 d := diskv.New(diskv.Options{BasePath: cacheDir, TempDir: filepath.Join(cacheDir, ".diskv-temp")})
208 c := &sumDiskCache{disk: d}
209
210 key := "testing"
211
212 f, err := os.Create(filepath.Join(cacheDir, sanitize(key)))
213 if err != nil {
214 t.Fatal(err)
215 }
216 f.Close()
217
218 got, ok := c.Get(key)
219 assert.False(ok)
220 assert.Equal([]byte{}, got)
221 })
222
223
224
225 t.Run("InvalidChecksum", func(t *testing.T) {
226 cacheDir, err := os.MkdirTemp("", "cache-test")
227 if err != nil {
228 t.Fatal(err)
229 }
230 defer os.RemoveAll(cacheDir)
231 d := diskv.New(diskv.Options{BasePath: cacheDir, TempDir: filepath.Join(cacheDir, ".diskv-temp")})
232 c := &sumDiskCache{disk: d}
233
234 key := "testing"
235 value := []byte("testing")
236 mismatchedValue := []byte("testink")
237 sum := sha256.Sum256(value)
238
239
240
241 f, err := os.Create(filepath.Join(cacheDir, sanitize(key)))
242 if err != nil {
243 t.Fatal(err)
244 }
245 f.Write(sum[:])
246 f.Write(mismatchedValue)
247 f.Close()
248
249
250 got, ok := c.Get(key)
251 assert.False(ok)
252 assert.Equal([]byte{}, got)
253 })
254
255
256
257
258
259
260 t.Run("OverwriteExistingKey", func(t *testing.T) {
261 cacheDir, err := os.MkdirTemp("", "cache-test")
262 if err != nil {
263 t.Fatal(err)
264 }
265 defer os.RemoveAll(cacheDir)
266 d := diskv.New(diskv.Options{BasePath: cacheDir, TempDir: filepath.Join(cacheDir, ".diskv-temp")})
267 c := &sumDiskCache{disk: d}
268
269 key := "testing"
270 value := []byte("cool value!")
271
272
273 c.Set(key, value)
274 got, ok := c.Get(key)
275
276
277 assert.True(ok)
278 assert.Equal(value, got)
279
280 differentValue := []byte("I'm different!")
281
282
283 c.Set(key, differentValue)
284 got, ok = c.Get(key)
285
286
287 assert.True(ok)
288 assert.Equal(differentValue, got)
289 })
290
291
292 t.Run("DeleteKey", func(t *testing.T) {
293 cacheDir, err := os.MkdirTemp("", "cache-test")
294 if err != nil {
295 t.Fatal(err)
296 }
297 defer os.RemoveAll(cacheDir)
298 d := diskv.New(diskv.Options{BasePath: cacheDir, TempDir: filepath.Join(cacheDir, ".diskv-temp")})
299 c := &sumDiskCache{disk: d}
300
301 key := "testing"
302 value := []byte("coolValue")
303
304 c.Set(key, value)
305
306
307 got, ok := c.Get(key)
308 assert.True(ok)
309 assert.Equal(value, got)
310
311 c.Delete(key)
312
313
314 got, ok = c.Get(key)
315 assert.False(ok)
316 assert.Equal([]byte{}, got)
317
318
319 c.Delete(key)
320 })
321 }
322
View as plain text