1
2
3
4
5
6
7
8
9
10
11
12
13 package memorydb
14
15 import (
16 "context"
17 "encoding/json"
18 "net/http"
19 "strings"
20 "testing"
21
22 "gitlab.com/flimzy/testy"
23
24 "github.com/go-kivik/kivik/v4"
25 "github.com/go-kivik/kivik/v4/driver"
26 internal "github.com/go-kivik/kivik/v4/int/errors"
27 )
28
29 func TestStats(t *testing.T) {
30 type statTest struct {
31 Name string
32 DBName string
33 Setup func(driver.Client)
34 Expected *driver.DBStats
35 Error string
36 }
37 tests := []statTest{
38 {
39 Name: "NoDBs",
40 DBName: "foo",
41 Setup: func(c driver.Client) {
42 if e := c.CreateDB(context.Background(), "foo", nil); e != nil {
43 panic(e)
44 }
45 },
46 Expected: &driver.DBStats{Name: "foo"},
47 },
48 }
49 for _, test := range tests {
50 func(test statTest) {
51 t.Run(test.Name, func(t *testing.T) {
52 c := setup(t, test.Setup)
53 db, err := c.DB(test.DBName, nil)
54 if err != nil {
55 t.Fatal(err)
56 }
57 result, err := db.Stats(context.Background())
58 var msg string
59 if err != nil {
60 msg = err.Error()
61 }
62 if msg != test.Error {
63 t.Errorf("Unexpected error: %s", msg)
64 }
65 if err != nil {
66 return
67 }
68 if d := testy.DiffInterface(test.Expected, result); d != nil {
69 t.Error(d)
70 }
71 })
72 }(test)
73 }
74 }
75
76 func setupDB(t *testing.T) *db {
77 t.Helper()
78 c := setup(t, nil)
79 if err := c.CreateDB(context.Background(), "foo", nil); err != nil {
80 t.Fatal(err)
81 }
82 d, err := c.DB("foo", nil)
83 if err != nil {
84 t.Fatal(err)
85 }
86 return d.(*db)
87 }
88
89 func TestPut(t *testing.T) {
90 type putTest struct {
91 Name string
92 DocID string
93 Doc interface{}
94 Setup func() *db
95 Expected interface{}
96 Status int
97 Error string
98 }
99 tests := []putTest{
100 {
101 Name: "LeadingUnderscoreInID",
102 DocID: "_badid",
103 Doc: map[string]string{"_id": "_badid", "foo": "bar"},
104 Status: 400,
105 Error: "only reserved document ids may start with underscore",
106 },
107 {
108 Name: "MismatchedIDs",
109 DocID: "foo",
110 Doc: map[string]string{"_id": "bar"},
111 Expected: map[string]string{"_id": "foo", "_rev": "1-xxx"},
112 },
113 {
114 Name: "Success",
115 DocID: "foo",
116 Doc: map[string]string{"_id": "foo", "foo": "bar"},
117 Expected: map[string]string{"_id": "foo", "foo": "bar", "_rev": "1-xxx"},
118 },
119 {
120 Name: "Conflict",
121 DocID: "foo",
122 Doc: map[string]string{"_id": "foo", "_rev": "bar"},
123 Setup: func() *db {
124 db := setupDB(t)
125 if _, err := db.Put(context.Background(), "foo", map[string]string{"_id": "foo"}, nil); err != nil {
126 t.Fatal(err)
127 }
128 return db
129 },
130 Status: 409,
131 Error: "document update conflict",
132 },
133 {
134 Name: "Unmarshalable",
135 DocID: "foo",
136 Doc: func() interface{} {
137 return map[string]interface{}{
138 "channel": make(chan int),
139 }
140 }(),
141 Status: 400,
142 Error: "json: unsupported type: chan int",
143 },
144 {
145 Name: "InitialRev",
146 DocID: "foo",
147 Doc: map[string]string{"_id": "foo", "_rev": "bar"},
148 Status: 409,
149 Error: "document update conflict",
150 },
151 func() putTest {
152 dbv := setupDB(t)
153 rev, err := dbv.Put(context.Background(), "foo", map[string]string{"_id": "foo", "foo": "bar"}, nil)
154 if err != nil {
155 panic(err)
156 }
157 return putTest{
158 Name: "Update",
159 DocID: "foo",
160 Setup: func() *db { return dbv },
161 Doc: map[string]string{"_id": "foo", "_rev": rev},
162 Expected: map[string]string{"_id": "foo", "_rev": "2-xxx"},
163 }
164 }(),
165 {
166 Name: "DesignDoc",
167 DocID: "_design/foo",
168 Doc: map[string]string{"foo": "bar"},
169 Expected: map[string]string{"_id": "_design/foo", "foo": "bar", "_rev": "1-xxx"},
170 },
171 {
172 Name: "LocalDoc",
173 DocID: "_local/foo",
174 Doc: map[string]string{"foo": "bar"},
175 Expected: map[string]string{"_id": "_local/foo", "foo": "bar", "_rev": "1-0"},
176 },
177 {
178 Name: "RecreateDeleted",
179 DocID: "foo",
180 Doc: map[string]string{"foo": "bar"},
181 Expected: map[string]string{"_id": "foo", "foo": "bar", "_rev": "3-xxx"},
182 Setup: func() *db {
183 db := setupDB(t)
184 rev, err := db.Put(context.Background(), "foo", map[string]string{"_id": "foo"}, nil)
185 if err != nil {
186 t.Fatal(err)
187 }
188 if _, e := db.Delete(context.Background(), "foo", kivik.Rev(rev)); e != nil {
189 t.Fatal(e)
190 }
191 return db
192 },
193 },
194 {
195 Name: "LocalDoc",
196 DocID: "_local/foo",
197 Doc: map[string]string{"foo": "baz"},
198 Expected: map[string]string{"_id": "_local/foo", "foo": "baz", "_rev": "1-0"},
199 Setup: func() *db {
200 db := setupDB(t)
201 _, err := db.Put(context.Background(), "_local/foo", map[string]string{"foo": "bar"}, nil)
202 if err != nil {
203 t.Fatal(err)
204 }
205 return db
206 },
207 },
208 {
209 Name: "WithAttachments",
210 DocID: "duck",
211 Doc: map[string]interface{}{
212 "_id": "duck",
213 "value": "quack",
214 "_attachments": []map[string]interface{}{
215 {"foo.css": map[string]string{
216 "content_type": "text/css",
217 "data": "LyogYW4gZW1wdHkgQ1NTIGZpbGUgKi8=",
218 }},
219 },
220 },
221 Expected: map[string]string{
222 "_id": "duck",
223 "_rev": "1-xxx",
224 "value": "quack",
225 },
226 },
227 {
228 Name: "Deleted DB",
229 Setup: func() *db {
230 c := setup(t, nil)
231 if err := c.CreateDB(context.Background(), "deleted0", nil); err != nil {
232 t.Fatal(err)
233 }
234 dbv, err := c.DB("deleted0", nil)
235 if err != nil {
236 t.Fatal(err)
237 }
238 if e := c.DestroyDB(context.Background(), "deleted0", nil); e != nil {
239 t.Fatal(e)
240 }
241 return dbv.(*db)
242 },
243 Status: http.StatusPreconditionFailed,
244 Error: "database does not exist",
245 },
246 }
247 for _, test := range tests {
248 func(test putTest) {
249 t.Run(test.Name, func(t *testing.T) {
250 t.Parallel()
251 var db *db
252 if test.Setup != nil {
253 db = test.Setup()
254 } else {
255 db = setupDB(t)
256 }
257 _, err := db.Put(context.Background(), test.DocID, test.Doc, nil)
258 if d := internal.StatusErrorDiff(test.Error, test.Status, err); d != "" {
259 t.Error(d)
260 }
261 if err != nil {
262 return
263 }
264 rows, err := db.Get(context.Background(), test.DocID, kivik.Params(nil))
265 if err != nil {
266 t.Fatal(err)
267 }
268 var result map[string]interface{}
269 if e := json.NewDecoder(rows.Body).Decode(&result); e != nil {
270 t.Fatal(e)
271 }
272 if !strings.HasPrefix(test.DocID, "_local/") {
273 if rev, ok := result["_rev"].(string); ok {
274 parts := strings.SplitN(rev, "-", 2)
275 result["_rev"] = parts[0] + "-xxx"
276 }
277 }
278 if d := testy.DiffAsJSON(test.Expected, result); d != nil {
279 t.Error(d)
280 }
281 })
282 }(test)
283 }
284 }
285
286 func TestGet(t *testing.T) {
287 type getTest struct {
288 Name string
289 ID string
290 options kivik.Option
291 DB *db
292 Status int
293 Error string
294 Expected interface{}
295 }
296 tests := []getTest{
297 {
298 Name: "NotFound",
299 ID: "foo",
300 Status: 404,
301 Error: "missing",
302 },
303 func() getTest {
304 db := setupDB(t)
305 _, err := db.Put(context.Background(), "foo", map[string]string{"_id": "foo", "foo": "bar"}, nil)
306 if err != nil {
307 panic(err)
308 }
309 return getTest{
310 Name: "ExistingDoc",
311 ID: "foo",
312 DB: db,
313 Expected: map[string]string{"_id": "foo", "foo": "bar"},
314 }
315 }(),
316 func() getTest {
317 db := setupDB(t)
318 rev, err := db.Put(context.Background(), "foo", map[string]string{"_id": "foo", "foo": "Bar"}, nil)
319 if err != nil {
320 panic(err)
321 }
322 return getTest{
323 Name: "SpecificRev",
324 ID: "foo",
325 DB: db,
326 options: kivik.Rev(rev),
327 Expected: map[string]string{"_id": "foo", "foo": "Bar"},
328 }
329 }(),
330 func() getTest {
331 db := setupDB(t)
332 rev, err := db.Put(context.Background(), "foo", map[string]string{"_id": "foo", "foo": "Bar"}, nil)
333 if err != nil {
334 panic(err)
335 }
336 _, err = db.Put(context.Background(), "foo", map[string]string{"_id": "foo", "foo": "baz", "_rev": rev}, nil)
337 if err != nil {
338 panic(err)
339 }
340 return getTest{
341 Name: "OldRev",
342 ID: "foo",
343 DB: db,
344 options: kivik.Rev(rev),
345 Expected: map[string]string{"_id": "foo", "foo": "Bar"},
346 }
347 }(),
348 {
349 Name: "MissingRev",
350 ID: "foo",
351 options: kivik.Rev("1-4c6114c65e295552ab1019e2b046b10e"),
352 DB: func() *db {
353 db := setupDB(t)
354 _, err := db.Put(context.Background(), "foo", map[string]string{"_id": "foo", "foo": "Bar"}, nil)
355 if err != nil {
356 panic(err)
357 }
358 return db
359 }(),
360 Status: 404,
361 Error: "missing",
362 },
363 func() getTest {
364 db := setupDB(t)
365 rev, err := db.Put(context.Background(), "foo", map[string]string{"_id": "foo"}, nil)
366 if err != nil {
367 panic(err)
368 }
369 if _, e := db.Delete(context.Background(), "foo", kivik.Rev(rev)); e != nil {
370 panic(e)
371 }
372 return getTest{
373 Name: "DeletedDoc",
374 ID: "foo",
375 DB: db,
376 Status: 404,
377 Error: "missing",
378 }
379 }(),
380 {
381 Name: "Deleted DB",
382 DB: func() *db {
383 c := setup(t, nil)
384 if err := c.CreateDB(context.Background(), "deleted0", nil); err != nil {
385 t.Fatal(err)
386 }
387 dbv, err := c.DB("deleted0", nil)
388 if err != nil {
389 t.Fatal(err)
390 }
391 if e := c.DestroyDB(context.Background(), "deleted0", nil); e != nil {
392 t.Fatal(e)
393 }
394 return dbv.(*db)
395 }(),
396 Error: "database does not exist",
397 Status: http.StatusPreconditionFailed,
398 },
399 }
400 for _, test := range tests {
401 func(test getTest) {
402 t.Run(test.Name, func(t *testing.T) {
403 t.Parallel()
404 db := test.DB
405 if db == nil {
406 db = setupDB(t)
407 }
408 opts := test.options
409 if opts == nil {
410 opts = kivik.Params(nil)
411 }
412 rows, err := db.Get(context.Background(), test.ID, opts)
413 if d := internal.StatusErrorDiff(test.Error, test.Status, err); d != "" {
414 t.Error(d)
415 }
416 if err != nil {
417 return
418 }
419
420 var result map[string]interface{}
421 if err := json.NewDecoder(rows.Body).Decode(&result); err != nil {
422 t.Fatal(err)
423 }
424
425 if result != nil {
426 delete(result, "_rev")
427 }
428 if d := testy.DiffAsJSON(test.Expected, result); d != nil {
429 t.Error(d)
430 }
431 })
432 }(test)
433 }
434 }
435
436 func TestDeleteDoc(t *testing.T) {
437 type delTest struct {
438 Name string
439 ID string
440 Rev string
441 DB *db
442 Status int
443 Error string
444 }
445 tests := []delTest{
446 {
447 Name: "NonExistingDoc",
448 ID: "foo",
449 Rev: "1-4c6114c65e295552ab1019e2b046b10e",
450 Status: 404,
451 Error: "missing",
452 },
453 func() delTest {
454 db := setupDB(t)
455 rev, err := db.Put(context.Background(), "foo", map[string]string{"_id": "foo"}, nil)
456 if err != nil {
457 panic(err)
458 }
459 return delTest{
460 Name: "Success",
461 ID: "foo",
462 DB: db,
463 Rev: rev,
464 }
465 }(),
466 {
467 Name: "InvalidRevFormat",
468 ID: "foo",
469 Rev: "invalid rev format",
470 Status: 400,
471 Error: "invalid rev format",
472 },
473 {
474 Name: "LocalNoRev",
475 ID: "_local/foo",
476 Rev: "",
477 DB: func() *db {
478 db := setupDB(t)
479 if _, err := db.Put(context.Background(), "_local/foo", map[string]string{"foo": "bar"}, nil); err != nil {
480 panic(err)
481 }
482 return db
483 }(),
484 },
485 {
486 Name: "LocalWithRev",
487 ID: "_local/foo",
488 Rev: "0-1",
489 DB: func() *db {
490 db := setupDB(t)
491 if _, err := db.Put(context.Background(), "_local/foo", map[string]string{"foo": "bar"}, nil); err != nil {
492 panic(err)
493 }
494 return db
495 }(),
496 },
497 {
498 Name: "DB deleted",
499 ID: "foo",
500 DB: func() *db {
501 c := setup(t, nil)
502 if err := c.CreateDB(context.Background(), "deleted0", nil); err != nil {
503 t.Fatal(err)
504 }
505 dbv, err := c.DB("deleted0", nil)
506 if err != nil {
507 t.Fatal(err)
508 }
509 if e := c.DestroyDB(context.Background(), "deleted0", nil); e != nil {
510 t.Fatal(e)
511 }
512 return dbv.(*db)
513 }(),
514 Status: http.StatusPreconditionFailed,
515 Error: "database does not exist",
516 },
517 }
518 for _, test := range tests {
519 func(test delTest) {
520 t.Run(test.Name, func(t *testing.T) {
521 t.Parallel()
522 db := test.DB
523 if db == nil {
524 db = setupDB(t)
525 }
526 rev, err := db.Delete(context.Background(), test.ID, kivik.Rev(test.Rev))
527 var msg string
528 var status int
529 if err != nil {
530 msg = err.Error()
531 status = kivik.HTTPStatus(err)
532 }
533 if msg != test.Error {
534 t.Errorf("Unexpected error: %s", msg)
535 }
536 if status != test.Status {
537 t.Errorf("Unexpected status: %d", status)
538 }
539 if err != nil {
540 return
541 }
542 result, err := db.Get(context.Background(), test.ID, kivik.Rev(rev))
543 if err != nil {
544 t.Fatal(err)
545 }
546 var doc interface{}
547 if e := json.NewDecoder(result.Body).Decode(&doc); e != nil {
548 t.Fatal(e)
549 }
550 expected := map[string]interface{}{
551 "_id": test.ID,
552 "_rev": rev,
553 "_deleted": true,
554 }
555 if d := testy.DiffAsJSON(expected, doc); d != nil {
556 t.Error(d)
557 }
558 })
559 }(test)
560 }
561 }
562
View as plain text