-
Notifications
You must be signed in to change notification settings - Fork 10
/
teststorage.go
137 lines (123 loc) · 3.48 KB
/
teststorage.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
package blobstore
// Copyright 2019 Vivino. All rights reserved
//
// See LICENSE file for license details
import (
"context"
"fmt"
"math/rand"
"sync"
)
// TestStorage provides callbacks and interceptors for easy testing of blob store failures.
type TestStorage struct {
get func(ctx context.Context, set, key string) ([]byte, error)
GetCallback func(set, key string)
set func(ctx context.Context, set, key string, val []byte) error
SetCallback func(set, key string, b []byte)
delete func(ctx context.Context, set, key string) error
DeleteCallback func(set, key string)
}
// NewTestStore is a store that can be used for testing.
// Without modifications it allows
func NewTestStore(store Store) *TestStorage {
return &TestStorage{
get: store.Get,
set: store.Set,
delete: store.Delete,
}
}
// Get a blob.
func (t *TestStorage) Get(ctx context.Context, set, key string) ([]byte, error) {
if t.GetCallback != nil {
t.GetCallback(set, key)
}
return t.get(ctx, set, key)
}
// Set a blob.
func (t *TestStorage) Set(ctx context.Context, set, key string, b []byte) error {
if t.SetCallback != nil {
t.SetCallback(set, key, b)
}
return t.set(ctx, set, key, b)
}
// Delete a blob.
func (t *TestStorage) Delete(ctx context.Context, set, key string) error {
if t.DeleteCallback != nil {
t.DeleteCallback(set, key)
}
return t.delete(ctx, set, key)
}
// FailGetKey will call the provided function.
// If nil is returned, the original store is queried,
// otherwise the error is returned.
func (t *TestStorage) FailGet(check func(set, key string) error) {
up := t.get
t.get = func(ctx context.Context, set, key string) ([]byte, error) {
if err := check(set, key); err != nil {
return nil, err
}
return up(ctx, set, key)
}
}
// FailSet will call the provided function.
// If nil is returned, the original store is queried,
// otherwise the error is returned.
func (t *TestStorage) FailSet(check func(set, key string) error) {
up := t.set
t.set = func(ctx context.Context, set, key string, b []byte) error {
if err := check(set, key); err != nil {
return err
}
return up(ctx, set, key, b)
}
}
// FailDelete will call the provided function.
// If nil is returned, the original store is queried,
// otherwise the error is returned.
func (t *TestStorage) FailDelete(check func(set, key string) error) {
up := t.delete
t.delete = func(ctx context.Context, set, key string) error {
if err := check(set, key); err != nil {
return err
}
return up(ctx, set, key)
}
}
// ErrTestIntentional is used for intentional test failures.
var ErrTestIntentional = fmt.Errorf("intentional failure for test")
// TestAlwaysFail will make all calls to blob store fail.
// ErrTestIntentional will be returned.
var TestAlwaysFail = func(set, key string) error {
return ErrTestIntentional
}
// TestFailAfterN will fail after N calls.
// ErrTestIntentional will be returned.
func TestFailAfterN(n int) func(set, key string) error {
var mu sync.Mutex
var cnt int
return func(set, key string) error {
mu.Lock()
now := cnt
cnt++
mu.Unlock()
if now == n {
return ErrTestIntentional
}
return nil
}
}
// TestFailPct will fail the percentage of calls.
// ErrTestIntentional will be returned.
func TestFailPct(n uint32, seed int64) func(set, key string) error {
rng := rand.New(rand.NewSource(seed))
var mu sync.Mutex
return func(set, key string) error {
mu.Lock()
r := rng.Uint32()
mu.Unlock()
if r%100 < n {
return ErrTestIntentional
}
return nil
}
}