1
2
3
4
5
6
7
8
9
10
11
12
13 package kivik
14
15 import (
16 "context"
17 "encoding/json"
18 "errors"
19 "fmt"
20 "net/http"
21 "testing"
22
23 "gitlab.com/flimzy/testy"
24
25 "github.com/go-kivik/kivik/v4/driver"
26 internal "github.com/go-kivik/kivik/v4/int/errors"
27 "github.com/go-kivik/kivik/v4/int/mock"
28 )
29
30 func TestFind(t *testing.T) {
31 tests := []struct {
32 name string
33 db *DB
34 query interface{}
35 options []Option
36 expected *ResultSet
37 status int
38 err string
39 }{
40 {
41 name: "non-finder",
42 db: &DB{
43 client: &Client{},
44 driverDB: &mock.DB{},
45 },
46 status: http.StatusNotImplemented,
47 err: "kivik: driver does not support Find interface",
48 },
49 {
50 name: "db error",
51 db: &DB{
52 client: &Client{},
53 driverDB: &mock.Finder{
54 FindFunc: func(context.Context, interface{}, driver.Options) (driver.Rows, error) {
55 return nil, errors.New("db error")
56 },
57 },
58 },
59 status: http.StatusInternalServerError,
60 err: "db error",
61 },
62 {
63 name: "success",
64 db: &DB{
65 client: &Client{},
66 driverDB: &mock.Finder{
67 FindFunc: func(_ context.Context, query interface{}, _ driver.Options) (driver.Rows, error) {
68 expectedQuery := json.RawMessage(`{"limit":3,"selector":{"foo":"bar"},"skip":10}`)
69 if d := testy.DiffInterface(expectedQuery, query); d != nil {
70 return nil, fmt.Errorf("Unexpected query:\n%s", d)
71 }
72 return &mock.Rows{ID: "a"}, nil
73 },
74 },
75 },
76 query: map[string]interface{}{"selector": map[string]interface{}{"foo": "bar"}},
77 options: []Option{
78 Param("limit", 3),
79 Param("skip", 10),
80 },
81 expected: &ResultSet{
82 iter: &iter{
83 feed: &rowsIterator{
84 Rows: &mock.Rows{ID: "a"},
85 },
86 curVal: &driver.Row{},
87 },
88 rowsi: &mock.Rows{ID: "a"},
89 },
90 },
91 {
92 name: "db error",
93 db: &DB{
94 err: errors.New("db error"),
95 },
96 status: http.StatusInternalServerError,
97 err: "db error",
98 },
99 {
100 name: "client closed",
101 db: &DB{
102 client: &Client{
103 closed: true,
104 },
105 driverDB: &mock.Finder{},
106 },
107 status: http.StatusServiceUnavailable,
108 err: "kivik: client closed",
109 },
110 }
111
112 for _, test := range tests {
113 t.Run(test.name, func(t *testing.T) {
114 rs := test.db.Find(context.Background(), test.query, test.options...)
115 err := rs.Err()
116 if d := internal.StatusErrorDiff(test.err, test.status, err); d != "" {
117 t.Error(d)
118 }
119 if err != nil {
120 return
121 }
122 rs.cancel = nil
123 rs.onClose = nil
124 if d := testy.DiffInterface(test.expected, rs); d != nil {
125 t.Error(d)
126 }
127 })
128 }
129 t.Run("standalone", func(t *testing.T) {
130 t.Run("after err, close doesn't block", func(t *testing.T) {
131 db := &DB{
132 client: &Client{},
133 driverDB: &mock.Finder{
134 FindFunc: func(context.Context, interface{}, driver.Options) (driver.Rows, error) {
135 return nil, errors.New("sdfsdf")
136 },
137 },
138 }
139 rows := db.Find(context.Background(), nil)
140 if err := rows.Err(); err == nil {
141 t.Fatal("expected an error, got none")
142 }
143 _ = db.Close()
144 })
145 t.Run("not finder, close doesn't block", func(t *testing.T) {
146 db := &DB{
147 client: &Client{},
148 driverDB: &mock.DB{},
149 }
150 rows := db.Find(context.Background(), nil)
151 if err := rows.Err(); err == nil {
152 t.Fatal("expected an error, got none")
153 }
154 _ = db.Close()
155 })
156 })
157 }
158
159 func TestCreateIndex(t *testing.T) {
160 tests := []struct {
161 testName string
162 db *DB
163 ddoc, name string
164 index interface{}
165 status int
166 err string
167 }{
168 {
169 testName: "non-finder",
170 db: &DB{
171 client: &Client{},
172 driverDB: &mock.DB{},
173 },
174 status: http.StatusNotImplemented,
175 err: "kivik: driver does not support Find interface",
176 },
177 {
178 testName: "db error",
179 db: &DB{
180 client: &Client{},
181 driverDB: &mock.Finder{
182 CreateIndexFunc: func(context.Context, string, string, interface{}, driver.Options) error {
183 return errors.New("db error")
184 },
185 },
186 },
187 status: http.StatusInternalServerError,
188 err: "db error",
189 },
190 {
191 testName: "success",
192 db: &DB{
193 client: &Client{},
194 driverDB: &mock.Finder{
195 CreateIndexFunc: func(_ context.Context, ddoc, name string, index interface{}, _ driver.Options) error {
196 expectedDdoc := "foo"
197 expectedName := "bar"
198 expectedIndex := int(3)
199 if expectedDdoc != ddoc {
200 return fmt.Errorf("Unexpected ddoc: %s", ddoc)
201 }
202 if expectedName != name {
203 return fmt.Errorf("Unexpected name: %s", name)
204 }
205 if d := testy.DiffInterface(expectedIndex, index); d != nil {
206 return fmt.Errorf("Unexpected index:\n%s", d)
207 }
208 return nil
209 },
210 },
211 },
212 ddoc: "foo",
213 name: "bar",
214 index: int(3),
215 },
216 {
217 name: "closed",
218 db: &DB{
219 client: &Client{
220 closed: true,
221 },
222 },
223 status: http.StatusServiceUnavailable,
224 err: "kivik: client closed",
225 },
226 {
227 name: "db error",
228 db: &DB{
229 client: &Client{},
230 err: errors.New("db error"),
231 },
232 status: http.StatusInternalServerError,
233 err: "db error",
234 },
235 }
236
237 for _, test := range tests {
238 t.Run(test.testName, func(t *testing.T) {
239 err := test.db.CreateIndex(context.Background(), test.ddoc, test.name, test.index)
240 if d := internal.StatusErrorDiff(test.err, test.status, err); d != "" {
241 t.Error(d)
242 }
243 })
244 }
245 }
246
247 func TestDeleteIndex(t *testing.T) {
248 tests := []struct {
249 testName string
250 db *DB
251 ddoc, name string
252 status int
253 err string
254 }{
255 {
256 testName: "non-finder",
257 db: &DB{
258 client: &Client{},
259 driverDB: &mock.DB{},
260 },
261 status: http.StatusNotImplemented,
262 err: "kivik: driver does not support Find interface",
263 },
264 {
265 testName: "db error",
266 db: &DB{
267 client: &Client{},
268 driverDB: &mock.Finder{
269 DeleteIndexFunc: func(context.Context, string, string, driver.Options) error {
270 return errors.New("db error")
271 },
272 },
273 },
274 status: http.StatusInternalServerError,
275 err: "db error",
276 },
277 {
278 testName: "success",
279 db: &DB{
280 client: &Client{},
281 driverDB: &mock.Finder{
282 DeleteIndexFunc: func(_ context.Context, ddoc, name string, _ driver.Options) error {
283 expectedDdoc := "foo"
284 expectedName := "bar"
285 if expectedDdoc != ddoc {
286 return fmt.Errorf("Unexpected ddoc: %s", ddoc)
287 }
288 if expectedName != name {
289 return fmt.Errorf("Unexpected name: %s", name)
290 }
291 return nil
292 },
293 },
294 },
295 ddoc: "foo",
296 name: "bar",
297 },
298 {
299 testName: "client closed",
300 db: &DB{
301 client: &Client{
302 closed: true,
303 },
304 },
305 status: http.StatusServiceUnavailable,
306 err: "kivik: client closed",
307 },
308 {
309 testName: "db error",
310 db: &DB{
311 err: errors.New("db error"),
312 },
313 status: http.StatusInternalServerError,
314 err: "db error",
315 },
316 }
317
318 for _, test := range tests {
319 t.Run(test.testName, func(t *testing.T) {
320 err := test.db.DeleteIndex(context.Background(), test.ddoc, test.name)
321 if d := internal.StatusErrorDiff(test.err, test.status, err); d != "" {
322 t.Error(d)
323 }
324 })
325 }
326 }
327
328 func TestGetIndexes(t *testing.T) {
329 tests := []struct {
330 testName string
331 db *DB
332 expected []Index
333 status int
334 err string
335 }{
336 {
337 testName: "non-finder",
338 db: &DB{
339 client: &Client{},
340 driverDB: &mock.DB{},
341 },
342 status: http.StatusNotImplemented,
343 err: "kivik: driver does not support Find interface",
344 },
345 {
346 testName: "db error",
347 db: &DB{
348 client: &Client{},
349 driverDB: &mock.Finder{
350 GetIndexesFunc: func(context.Context, driver.Options) ([]driver.Index, error) {
351 return nil, errors.New("db error")
352 },
353 },
354 },
355 status: http.StatusInternalServerError,
356 err: "db error",
357 },
358 {
359 testName: "success",
360 db: &DB{
361 client: &Client{},
362 driverDB: &mock.Finder{
363 GetIndexesFunc: func(context.Context, driver.Options) ([]driver.Index, error) {
364 return []driver.Index{
365 {Name: "a"},
366 {Name: "b"},
367 }, nil
368 },
369 },
370 },
371 expected: []Index{
372 {
373 Name: "a",
374 },
375 {
376 Name: "b",
377 },
378 },
379 },
380 {
381 testName: "client closed",
382 db: &DB{
383 client: &Client{
384 closed: true,
385 },
386 },
387 status: http.StatusServiceUnavailable,
388 err: "kivik: client closed",
389 },
390 {
391 testName: "db error",
392 db: &DB{
393 err: errors.New("db error"),
394 },
395 status: http.StatusInternalServerError,
396 err: "db error",
397 },
398 }
399
400 for _, test := range tests {
401 t.Run(test.testName, func(t *testing.T) {
402 result, err := test.db.GetIndexes(context.Background())
403 if d := internal.StatusErrorDiff(test.err, test.status, err); d != "" {
404 t.Error(d)
405 }
406 if err != nil {
407 return
408 }
409 if d := testy.DiffInterface(test.expected, result); d != nil {
410 t.Error(d)
411 }
412 })
413 }
414 }
415
416 func TestExplain(t *testing.T) {
417 type tt struct {
418 db *DB
419 query interface{}
420 expected *QueryPlan
421 status int
422 err string
423 }
424
425 tests := testy.NewTable()
426 tests.Add("non-finder", tt{
427 db: &DB{
428 client: &Client{},
429 driverDB: &mock.DB{},
430 },
431 status: http.StatusNotImplemented,
432 err: "kivik: driver does not support Find interface",
433 })
434 tests.Add("explain error", tt{
435 db: &DB{
436 client: &Client{},
437 driverDB: &mock.Finder{
438 ExplainFunc: func(context.Context, interface{}, driver.Options) (*driver.QueryPlan, error) {
439 return nil, errors.New("explain error")
440 },
441 },
442 },
443 status: http.StatusInternalServerError,
444 err: "explain error",
445 })
446 tests.Add("success", tt{
447 db: &DB{
448 client: &Client{},
449 driverDB: &mock.Finder{
450 ExplainFunc: func(_ context.Context, query interface{}, _ driver.Options) (*driver.QueryPlan, error) {
451 expectedQuery := int(3)
452 if d := testy.DiffInterface(expectedQuery, query); d != nil {
453 return nil, fmt.Errorf("Unexpected query:\n%s", d)
454 }
455 return &driver.QueryPlan{DBName: "foo"}, nil
456 },
457 },
458 },
459 query: int(3),
460 expected: &QueryPlan{DBName: "foo"},
461 })
462 tests.Add("client closed", tt{
463 db: &DB{
464 driverDB: &mock.Finder{},
465 client: &Client{
466 closed: true,
467 },
468 },
469 status: http.StatusServiceUnavailable,
470 err: "kivik: client closed",
471 })
472 tests.Add("db error", tt{
473 db: &DB{
474 err: errors.New("db error"),
475 },
476 status: http.StatusInternalServerError,
477 err: "db error",
478 })
479
480 tests.Run(t, func(t *testing.T, tt tt) {
481 result, err := tt.db.Explain(context.Background(), tt.query)
482 if d := internal.StatusErrorDiff(tt.err, tt.status, err); d != "" {
483 t.Error(d)
484 }
485 if d := testy.DiffInterface(tt.expected, result); d != nil {
486 t.Error(d)
487 }
488 })
489 }
490
View as plain text