1 package mocks
2
3 import (
4 "sync"
5 "time"
6
7 "github.com/launchdarkly/go-server-sdk/v6/subsystems/ldstoretypes"
8
9 "golang.org/x/exp/maps"
10 )
11
12
13
14 type MockDatabaseInstance struct {
15 dataByPrefix map[string]map[ldstoretypes.DataKind]map[string]ldstoretypes.SerializedItemDescriptor
16 initedByPrefix map[string]*bool
17 }
18
19
20 func NewMockDatabaseInstance() *MockDatabaseInstance {
21 return &MockDatabaseInstance{
22 dataByPrefix: make(map[string]map[ldstoretypes.DataKind]map[string]ldstoretypes.SerializedItemDescriptor),
23 initedByPrefix: make(map[string]*bool),
24 }
25 }
26
27
28 func (db *MockDatabaseInstance) Clear(prefix string) {
29 for _, m := range db.dataByPrefix[prefix] {
30 maps.Clear(m)
31 }
32 if v, ok := db.initedByPrefix[prefix]; ok {
33 *v = false
34 }
35 }
36
37
38 type MockPersistentDataStore struct {
39 data map[ldstoretypes.DataKind]map[string]ldstoretypes.SerializedItemDescriptor
40 persistOnlyAsString bool
41 fakeError error
42 available bool
43 inited *bool
44 InitQueriedCount int
45 queryDelay time.Duration
46 queryStartedCh chan struct{}
47 testTxHook func()
48 closed bool
49 lock sync.Mutex
50 }
51
52 func newData() map[ldstoretypes.DataKind]map[string]ldstoretypes.SerializedItemDescriptor {
53 return map[ldstoretypes.DataKind]map[string]ldstoretypes.SerializedItemDescriptor{
54 MockData: {},
55 MockOtherData: {},
56 }
57 }
58
59
60 func NewMockPersistentDataStore() *MockPersistentDataStore {
61 f := false
62 m := &MockPersistentDataStore{data: newData(), inited: &f, available: true}
63 return m
64 }
65
66
67
68 func NewMockPersistentDataStoreWithPrefix(
69 db *MockDatabaseInstance,
70 prefix string,
71 ) *MockPersistentDataStore {
72 m := &MockPersistentDataStore{available: true}
73 if _, ok := db.dataByPrefix[prefix]; !ok {
74 db.dataByPrefix[prefix] = newData()
75 f := false
76 db.initedByPrefix[prefix] = &f
77 }
78 m.data = db.dataByPrefix[prefix]
79 m.inited = db.initedByPrefix[prefix]
80 return m
81 }
82
83
84
85 func (m *MockPersistentDataStore) EnableInstrumentedQueries(queryDelay time.Duration) <-chan struct{} {
86 m.lock.Lock()
87 defer m.lock.Unlock()
88 m.queryDelay = queryDelay
89 m.queryStartedCh = make(chan struct{}, 10)
90 return m.queryStartedCh
91 }
92
93
94 func (m *MockPersistentDataStore) ForceGet(
95 kind ldstoretypes.DataKind,
96 key string,
97 ) ldstoretypes.SerializedItemDescriptor {
98 m.lock.Lock()
99 defer m.lock.Unlock()
100 if ret, ok := m.data[kind][key]; ok {
101 return ret
102 }
103 return ldstoretypes.SerializedItemDescriptor{}.NotFound()
104 }
105
106
107 func (m *MockPersistentDataStore) ForceSet(
108 kind ldstoretypes.DataKind,
109 key string,
110 item ldstoretypes.SerializedItemDescriptor,
111 ) {
112 m.lock.Lock()
113 defer m.lock.Unlock()
114 m.data[kind][key] = item
115 }
116
117
118 func (m *MockPersistentDataStore) ForceRemove(kind ldstoretypes.DataKind, key string) {
119 m.lock.Lock()
120 defer m.lock.Unlock()
121 delete(m.data[kind], key)
122 }
123
124
125 func (m *MockPersistentDataStore) ForceSetInited(inited bool) {
126 m.lock.Lock()
127 defer m.lock.Unlock()
128 *m.inited = inited
129 }
130
131
132 func (m *MockPersistentDataStore) SetAvailable(available bool) {
133 m.lock.Lock()
134 defer m.lock.Unlock()
135 m.available = available
136 }
137
138
139 func (m *MockPersistentDataStore) SetFakeError(fakeError error) {
140 m.lock.Lock()
141 defer m.lock.Unlock()
142 m.fakeError = fakeError
143 }
144
145
146
147
148
149 func (m *MockPersistentDataStore) SetPersistOnlyAsString(value bool) {
150 m.lock.Lock()
151 defer m.lock.Unlock()
152 m.persistOnlyAsString = value
153 }
154
155
156
157 func (m *MockPersistentDataStore) SetTestTxHook(hook func()) {
158 m.lock.Lock()
159 defer m.lock.Unlock()
160 m.testTxHook = hook
161 }
162
163 func (m *MockPersistentDataStore) startQuery() {
164 if m.queryStartedCh != nil {
165 m.queryStartedCh <- struct{}{}
166 }
167 if m.queryDelay > 0 {
168 <-time.After(m.queryDelay)
169 }
170 }
171
172
173 func (m *MockPersistentDataStore) Init(allData []ldstoretypes.SerializedCollection) error {
174 m.lock.Lock()
175 defer m.lock.Unlock()
176 if m.fakeError != nil {
177 return m.fakeError
178 }
179 for _, mm := range m.data {
180 maps.Clear(mm)
181 }
182 for _, coll := range allData {
183 AssertNotNil(coll.Kind)
184 itemsMap := make(map[string]ldstoretypes.SerializedItemDescriptor)
185 for _, item := range coll.Items {
186 itemsMap[item.Key] = m.storableItem(item.Item)
187 }
188 m.data[coll.Kind] = itemsMap
189 }
190 *m.inited = true
191 return nil
192 }
193
194
195 func (m *MockPersistentDataStore) Get(
196 kind ldstoretypes.DataKind,
197 key string,
198 ) (ldstoretypes.SerializedItemDescriptor, error) {
199 AssertNotNil(kind)
200 m.lock.Lock()
201 defer m.lock.Unlock()
202 if m.fakeError != nil {
203 return ldstoretypes.SerializedItemDescriptor{}.NotFound(), m.fakeError
204 }
205 m.startQuery()
206 if item, ok := m.data[kind][key]; ok {
207 return m.retrievedItem(item), nil
208 }
209 return ldstoretypes.SerializedItemDescriptor{}.NotFound(), nil
210 }
211
212
213 func (m *MockPersistentDataStore) GetAll(
214 kind ldstoretypes.DataKind,
215 ) ([]ldstoretypes.KeyedSerializedItemDescriptor, error) {
216 AssertNotNil(kind)
217 m.lock.Lock()
218 defer m.lock.Unlock()
219 if m.fakeError != nil {
220 return nil, m.fakeError
221 }
222 m.startQuery()
223 ret := []ldstoretypes.KeyedSerializedItemDescriptor{}
224 for k, v := range m.data[kind] {
225 ret = append(ret, ldstoretypes.KeyedSerializedItemDescriptor{Key: k, Item: m.retrievedItem(v)})
226 }
227 return ret, nil
228 }
229
230
231 func (m *MockPersistentDataStore) Upsert(
232 kind ldstoretypes.DataKind,
233 key string,
234 newItem ldstoretypes.SerializedItemDescriptor,
235 ) (bool, error) {
236 AssertNotNil(kind)
237 m.lock.Lock()
238 defer m.lock.Unlock()
239 if m.fakeError != nil {
240 return false, m.fakeError
241 }
242 if m.testTxHook != nil {
243 m.testTxHook()
244 }
245 if oldItem, ok := m.data[kind][key]; ok {
246 oldVersion := oldItem.Version
247 if m.persistOnlyAsString {
248
249
250 oldDeserializedItem, _ := kind.Deserialize(oldItem.SerializedItem)
251 oldVersion = oldDeserializedItem.Version
252 }
253 if oldVersion >= newItem.Version {
254 return false, nil
255 }
256 }
257 m.data[kind][key] = m.storableItem(newItem)
258 return true, nil
259 }
260
261
262 func (m *MockPersistentDataStore) IsInitialized() bool {
263 m.lock.Lock()
264 defer m.lock.Unlock()
265 m.InitQueriedCount++
266 return *m.inited
267 }
268
269
270 func (m *MockPersistentDataStore) IsStoreAvailable() bool {
271 m.lock.Lock()
272 defer m.lock.Unlock()
273 return m.available
274 }
275
276
277 func (m *MockPersistentDataStore) Close() error {
278 m.lock.Lock()
279 defer m.lock.Unlock()
280 m.closed = true
281 return nil
282 }
283
284 func (m *MockPersistentDataStore) retrievedItem(
285 item ldstoretypes.SerializedItemDescriptor,
286 ) ldstoretypes.SerializedItemDescriptor {
287 if m.persistOnlyAsString {
288
289 return ldstoretypes.SerializedItemDescriptor{Version: 0, SerializedItem: item.SerializedItem}
290 }
291 return item
292 }
293
294 func (m *MockPersistentDataStore) storableItem(
295 item ldstoretypes.SerializedItemDescriptor,
296 ) ldstoretypes.SerializedItemDescriptor {
297 if item.Deleted && !m.persistOnlyAsString {
298
299
300 return ldstoretypes.SerializedItemDescriptor{Version: item.Version, Deleted: true}
301 }
302 return item
303 }
304
View as plain text