storage_test.gno
7.36 Kb ยท 375 lines
1package datastore
2
3import (
4 "strings"
5 "testing"
6
7 "gno.land/p/demo/uassert"
8 "gno.land/p/demo/urequire"
9)
10
11func TestStorageDefaults(t *testing.T) {
12 name := "foo"
13 storage := NewStorage(name)
14
15 uassert.Equal(t, name, storage.Name())
16 uassert.NotEqual(t, nil, storage.Collection())
17 uassert.Equal(t, 0, storage.Size())
18
19 s := storage.Schema()
20 uassert.NotEqual(t, nil, s)
21 uassert.Equal(t, strings.Title(name), s.Name())
22}
23
24func TestStorageNewRecord(t *testing.T) {
25 field := "status"
26 defaultValue := "testing"
27 s := NewSchema("Foo", WithDefaultField(field, defaultValue))
28 storage := NewStorage("foo", WithSchema(s))
29
30 r := storage.NewRecord()
31 urequire.NotEqual(t, nil, r, "new record is not nil")
32 uassert.Equal(t, uint64(0), r.ID())
33 uassert.Equal(t, storage.Schema().Name(), r.Type())
34
35 v, found := r.Get(field)
36 urequire.True(t, found, "default value found")
37
38 got, ok := v.(string)
39 urequire.True(t, ok, "default value type")
40 uassert.Equal(t, defaultValue, got)
41}
42
43func TestStorageQuery(t *testing.T) {
44 index := NewIndex("tag", func(r Record) string {
45 if v, found := r.Get("tag"); found {
46 return v.(string)
47 }
48 return ""
49 })
50
51 cases := []struct {
52 name string
53 options []QueryOption
54 results []uint64
55 setup func() *Storage
56 errMsg string
57 }{
58 {
59 name: "default query",
60 results: []uint64{1, 2},
61 setup: func() *Storage {
62 s := NewStorage("foo")
63 s.NewRecord().Save()
64 s.NewRecord().Save()
65 return &s
66 },
67 },
68 {
69 name: "with size",
70 results: []uint64{1},
71 options: []QueryOption{WithSize(1)},
72 setup: func() *Storage {
73 s := NewStorage("foo")
74 s.NewRecord().Save()
75 s.NewRecord().Save()
76 return &s
77 },
78 },
79 {
80 name: "with offset",
81 results: []uint64{2},
82 options: []QueryOption{WithOffset(1)},
83 setup: func() *Storage {
84 s := NewStorage("foo")
85 s.NewRecord().Save()
86 s.NewRecord().Save()
87 return &s
88 },
89 },
90 {
91 name: "with offset overflow",
92 options: []QueryOption{WithOffset(4)},
93 setup: func() *Storage {
94 s := NewStorage("foo")
95 s.NewRecord().Save()
96 return &s
97 },
98 },
99 {
100 name: "with size and offset",
101 results: []uint64{2, 3},
102 options: []QueryOption{WithSize(2), WithOffset(1)},
103 setup: func() *Storage {
104 s := NewStorage("foo")
105 s.NewRecord().Save()
106 s.NewRecord().Save()
107 s.NewRecord().Save()
108 s.NewRecord().Save()
109 return &s
110 },
111 },
112 {
113 name: "custom index",
114 options: []QueryOption{UseIndex("tag", "A")},
115 results: []uint64{1, 3},
116 setup: func() *Storage {
117 s := NewStorage("foo", WithIndex(index))
118
119 r := s.NewRecord()
120 r.Set("tag", "A")
121 r.Save()
122
123 r = s.NewRecord()
124 r.Set("tag", "B")
125 r.Save()
126
127 r = s.NewRecord()
128 r.Set("tag", "A")
129 r.Save()
130
131 return &s
132 },
133 },
134 {
135 name: "custom index with offset",
136 options: []QueryOption{UseIndex("tag", "B"), WithOffset(1)},
137 results: []uint64{3, 4},
138 setup: func() *Storage {
139 s := NewStorage("foo", WithIndex(index))
140
141 r := s.NewRecord()
142 r.Set("tag", "B")
143 r.Save()
144
145 r = s.NewRecord()
146 r.Set("tag", "A")
147 r.Save()
148
149 r = s.NewRecord()
150 r.Set("tag", "B")
151 r.Save()
152
153 r = s.NewRecord()
154 r.Set("tag", "B")
155 r.Save()
156
157 return &s
158 },
159 },
160 {
161 name: "custom index with offset and size",
162 options: []QueryOption{UseIndex("tag", "B"), WithOffset(1), WithSize(1)},
163 results: []uint64{3},
164 setup: func() *Storage {
165 s := NewStorage("foo", WithIndex(index))
166
167 r := s.NewRecord()
168 r.Set("tag", "B")
169 r.Save()
170
171 r = s.NewRecord()
172 r.Set("tag", "A")
173 r.Save()
174
175 r = s.NewRecord()
176 r.Set("tag", "B")
177 r.Save()
178
179 r = s.NewRecord()
180 r.Set("tag", "B")
181 r.Save()
182
183 return &s
184 },
185 },
186 {
187 name: "custom index not found",
188 options: []QueryOption{UseIndex("foo", "B")},
189 setup: func() *Storage {
190 s := NewStorage("foo")
191 return &s
192 },
193 errMsg: "storage index for query not found: foo",
194 },
195 }
196
197 for _, tc := range cases {
198 t.Run(tc.name, func(t *testing.T) {
199 // Arrange
200 storage := tc.setup()
201
202 // Act
203 rs, err := storage.Query(tc.options...)
204
205 // Assert
206 if tc.errMsg != "" {
207 uassert.ErrorContains(t, err, tc.errMsg, "expect error")
208 return
209 }
210
211 urequire.NoError(t, err, "expect no error")
212 urequire.NotEqual(t, nil, rs, "new record is not nil")
213 urequire.Equal(t, len(tc.results), rs.Size(), "expect query results count to match")
214
215 var i int
216 rs.Iterate(func(r Record) bool {
217 urequire.Equal(t, tc.results[i], r.ID(), "expect result IDs to match")
218 i++
219 return false
220 })
221 })
222 }
223}
224
225func TestStorageGet(t *testing.T) {
226 index := NewIndex("name", func(r Record) string {
227 if v, found := r.Get("name"); found {
228 return v.(string)
229 }
230 return ""
231 })
232
233 cases := []struct {
234 name string
235 key string
236 recordID uint64
237 setup func(*Storage)
238 }{
239 {
240 name: "single record",
241 key: "foobar",
242 recordID: 1,
243 setup: func(s *Storage) {
244 r := s.NewRecord()
245 r.Set("name", "foobar")
246 r.Save()
247 },
248 },
249 {
250 name: "two records",
251 key: "foobar",
252 recordID: 1,
253 setup: func(s *Storage) {
254 r := s.NewRecord()
255 r.Set("name", "foobar")
256 r.Save()
257
258 r = s.NewRecord()
259 r.Set("name", "foobar")
260 r.Save()
261
262 r = s.NewRecord()
263 r.Set("name", "extra")
264 r.Save()
265 },
266 },
267 {
268 name: "record not found",
269 key: "unknown",
270 setup: func(s *Storage) {
271 r := s.NewRecord()
272 r.Set("name", "foobar")
273 r.Save()
274 },
275 },
276 {
277 name: "empty storage",
278 key: "foobar",
279 },
280 }
281
282 for _, tc := range cases {
283 t.Run(tc.name, func(t *testing.T) {
284 storage := NewStorage("foo", WithIndex(index))
285 if tc.setup != nil {
286 tc.setup(&storage)
287 }
288
289 r, found := storage.Get(index.Name(), tc.key)
290
291 if tc.recordID == 0 {
292 uassert.Equal(t, nil, r, "expect no record")
293 uassert.False(t, found, "expect record not found")
294 return
295 }
296
297 uassert.True(t, found, "expect record found")
298 urequire.NotEqual(t, nil, r, "expect record to be found")
299 uassert.Equal(t, tc.recordID, r.ID(), "expect ID to match")
300 })
301 }
302}
303
304func TestStorageGetByID(t *testing.T) {
305 cases := []struct {
306 name string
307 recordID uint64
308 found bool
309 setup func(*Storage)
310 }{
311 {
312 name: "single record",
313 recordID: 1,
314 found: true,
315 setup: func(s *Storage) {
316 s.NewRecord().Save()
317 },
318 },
319 {
320 name: "multiple records",
321 recordID: 2,
322 found: true,
323 setup: func(s *Storage) {
324 s.NewRecord().Save()
325 s.NewRecord().Save()
326 s.NewRecord().Save()
327 },
328 },
329 {
330 name: "record not found",
331 recordID: 3,
332 setup: func(s *Storage) {
333 s.NewRecord().Save()
334 s.NewRecord().Save()
335 },
336 },
337 {
338 name: "empty storage",
339 recordID: 1,
340 },
341 }
342
343 for _, tc := range cases {
344 t.Run(tc.name, func(t *testing.T) {
345 storage := NewStorage("foo")
346 if tc.setup != nil {
347 tc.setup(&storage)
348 }
349
350 r, found := storage.GetByID(tc.recordID)
351
352 if !tc.found {
353 uassert.Equal(t, nil, r, "expect no record")
354 uassert.False(t, found, "expect record not found")
355 return
356 }
357
358 uassert.True(t, found, "expect record found")
359 urequire.NotEqual(t, nil, r, "expect record to be found")
360 uassert.Equal(t, tc.recordID, r.ID(), "expect ID to match")
361 })
362 }
363}
364
365func TestStorageDelete(t *testing.T) {
366 storage := NewStorage("foo")
367 r := storage.NewRecord()
368 r.Save()
369
370 deleted := storage.Delete(r.ID())
371 uassert.True(t, deleted)
372
373 deleted = storage.Delete(r.ID())
374 uassert.False(t, deleted)
375}