1
2
3
4
5
6
7
8
9
10
11
12
13 package kivik
14
15 import (
16 "context"
17 "errors"
18 "fmt"
19 "net/http"
20 "testing"
21 "time"
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 TestReplicationDocsWritten(t *testing.T) {
31 t.Run("No Info", func(t *testing.T) {
32 r := &Replication{}
33 result := r.DocsWritten()
34 if result != 0 {
35 t.Errorf("Unexpected doc count: %d", result)
36 }
37 })
38 t.Run("With Info", func(t *testing.T) {
39 r := &Replication{
40 info: &driver.ReplicationInfo{
41 DocsWritten: 123,
42 },
43 }
44 result := r.DocsWritten()
45 if result != 123 {
46 t.Errorf("Unexpected doc count: %d", result)
47 }
48 })
49 t.Run("Nil", func(t *testing.T) {
50 var r *Replication
51 result := r.DocsWritten()
52 if result != 0 {
53 t.Errorf("Unexpected doc count: %d", result)
54 }
55 })
56 }
57
58 func TestDocsRead(t *testing.T) {
59 t.Run("No Info", func(t *testing.T) {
60 r := &Replication{}
61 result := r.DocsRead()
62 if result != 0 {
63 t.Errorf("Unexpected doc count: %d", result)
64 }
65 })
66 t.Run("With Info", func(t *testing.T) {
67 r := &Replication{
68 info: &driver.ReplicationInfo{
69 DocsRead: 123,
70 },
71 }
72 result := r.DocsRead()
73 if result != 123 {
74 t.Errorf("Unexpected doc count: %d", result)
75 }
76 })
77 t.Run("Nil", func(t *testing.T) {
78 var r *Replication
79 result := r.DocsRead()
80 if result != 0 {
81 t.Errorf("Unexpected doc count: %d", result)
82 }
83 })
84 }
85
86 func TestDocWriteFailures(t *testing.T) {
87 t.Run("No Info", func(t *testing.T) {
88 r := &Replication{}
89 result := r.DocWriteFailures()
90 if result != 0 {
91 t.Errorf("Unexpected doc count: %d", result)
92 }
93 })
94 t.Run("With Info", func(t *testing.T) {
95 r := &Replication{
96 info: &driver.ReplicationInfo{
97 DocWriteFailures: 123,
98 },
99 }
100 result := r.DocWriteFailures()
101 if result != 123 {
102 t.Errorf("Unexpected doc count: %d", result)
103 }
104 })
105 t.Run("Nil", func(t *testing.T) {
106 var r *Replication
107 result := r.DocWriteFailures()
108 if result != 0 {
109 t.Errorf("Unexpected doc count: %d", result)
110 }
111 })
112 }
113
114 func TestProgress(t *testing.T) {
115 t.Run("No Info", func(t *testing.T) {
116 r := &Replication{}
117 result := r.Progress()
118 if result != 0 {
119 t.Errorf("Unexpected doc count: %v", result)
120 }
121 })
122 t.Run("With Info", func(t *testing.T) {
123 r := &Replication{
124 info: &driver.ReplicationInfo{
125 Progress: 123,
126 },
127 }
128 result := r.Progress()
129 if result != 123 {
130 t.Errorf("Unexpected doc count: %v", result)
131 }
132 })
133 t.Run("Nil", func(t *testing.T) {
134 var r *Replication
135 result := r.Progress()
136 if result != 0 {
137 t.Errorf("Unexpected doc count: %v", result)
138 }
139 })
140 }
141
142 func TestNewReplication(t *testing.T) {
143 source := "foo"
144 target := "bar"
145 rep := &mock.Replication{
146 SourceFunc: func() string { return source },
147 TargetFunc: func() string { return target },
148 }
149 expected := &Replication{
150 Source: source,
151 Target: target,
152 irep: rep,
153 }
154 result := newReplication(rep)
155 if d := testy.DiffInterface(expected, result); d != nil {
156 t.Error(d)
157 }
158 }
159
160 func TestReplicationGetters(t *testing.T) {
161 repID := "repID"
162 start := parseTime(t, "2018-01-01T00:00:00Z")
163 end := parseTime(t, "2019-01-01T00:00:00Z")
164 state := "confusion"
165 r := &Replication{
166 irep: &mock.Replication{
167 ReplicationIDFunc: func() string { return repID },
168 StartTimeFunc: func() time.Time { return start },
169 EndTimeFunc: func() time.Time { return end },
170 StateFunc: func() string { return state },
171 },
172 }
173
174 t.Run("ReplicationID", func(t *testing.T) {
175 result := r.ReplicationID()
176 if result != repID {
177 t.Errorf("Unexpected result: %v", result)
178 }
179 })
180
181 t.Run("StartTime", func(t *testing.T) {
182 result := r.StartTime()
183 if !result.Equal(start) {
184 t.Errorf("Unexpected result: %v", result)
185 }
186 })
187
188 t.Run("EndTime", func(t *testing.T) {
189 result := r.EndTime()
190 if !result.Equal(end) {
191 t.Errorf("Unexpected result: %v", result)
192 }
193 })
194
195 t.Run("State", func(t *testing.T) {
196 result := r.State()
197 if result != ReplicationState(state) {
198 t.Errorf("Unexpected result: %v", result)
199 }
200 })
201 }
202
203 func TestReplicationErr(t *testing.T) {
204 t.Run("No error", func(t *testing.T) {
205 r := &Replication{
206 irep: &mock.Replication{
207 ErrFunc: func() error { return nil },
208 },
209 }
210 if err := r.Err(); err != nil {
211 t.Errorf("Unexpected error: %s", err)
212 }
213 })
214 t.Run("Error", func(t *testing.T) {
215 r := &Replication{
216 irep: &mock.Replication{
217 ErrFunc: func() error {
218 return errors.New("rep error")
219 },
220 },
221 }
222 if err := r.Err(); err == nil || err.Error() != "rep error" {
223 t.Errorf("Unexpected error: %s", err)
224 }
225 })
226 t.Run("Nil", func(t *testing.T) {
227 var r *Replication
228 if err := r.Err(); err != nil {
229 t.Errorf("Unexpected error: %s", err)
230 }
231 })
232 }
233
234 func TestReplicationIsActive(t *testing.T) {
235 t.Run("Active", func(t *testing.T) {
236 r := &Replication{
237 irep: &mock.Replication{
238 StateFunc: func() string {
239 return "active"
240 },
241 },
242 }
243 if !r.IsActive() {
244 t.Errorf("Expected active")
245 }
246 })
247 t.Run("Complete", func(t *testing.T) {
248 r := &Replication{
249 irep: &mock.Replication{
250 StateFunc: func() string {
251 return string(ReplicationComplete)
252 },
253 },
254 }
255 if r.IsActive() {
256 t.Errorf("Expected not active")
257 }
258 })
259 t.Run("Nil", func(t *testing.T) {
260 var r *Replication
261 if r.IsActive() {
262 t.Errorf("Expected not active")
263 }
264 })
265 }
266
267 func TestReplicationDelete(t *testing.T) {
268 expected := "delete error"
269 r := &Replication{
270 irep: &mock.Replication{
271 DeleteFunc: func(context.Context) error { return errors.New(expected) },
272 },
273 }
274 err := r.Delete(context.Background())
275 if !testy.ErrorMatches(expected, err) {
276 t.Errorf("Unexpected error: %s", err)
277 }
278 }
279
280 func TestReplicationUpdate(t *testing.T) {
281 t.Run("update error", func(t *testing.T) {
282 expected := "rep error"
283 r := &Replication{
284 irep: &mock.Replication{
285 UpdateFunc: func(context.Context, *driver.ReplicationInfo) error {
286 return errors.New(expected)
287 },
288 },
289 }
290 err := r.Update(context.Background())
291 if !testy.ErrorMatches(expected, err) {
292 t.Errorf("Unexpected error: %s", err)
293 }
294 })
295
296 t.Run("success", func(t *testing.T) {
297 expected := driver.ReplicationInfo{
298 DocsRead: 123,
299 }
300 r := &Replication{
301 irep: &mock.Replication{
302 UpdateFunc: func(_ context.Context, i *driver.ReplicationInfo) error {
303 *i = driver.ReplicationInfo{
304 DocsRead: 123,
305 }
306 return nil
307 },
308 },
309 }
310 err := r.Update(context.Background())
311 if !testy.ErrorMatches("", err) {
312 t.Errorf("Unexpected error: %s", err)
313 }
314 if d := testy.DiffInterface(&expected, r.info); d != nil {
315 t.Error(d)
316 }
317 })
318 }
319
320 func TestGetReplications(t *testing.T) {
321 tests := []struct {
322 name string
323 client *Client
324 options Option
325 expected []*Replication
326 status int
327 err string
328 }{
329 {
330 name: "non-replicator",
331 client: &Client{
332 driverClient: &mock.Client{},
333 },
334 status: http.StatusNotImplemented,
335 err: "kivik: driver does not support replication",
336 },
337 {
338 name: "db error",
339 client: &Client{
340 driverClient: &mock.ClientReplicator{
341 GetReplicationsFunc: func(context.Context, driver.Options) ([]driver.Replication, error) {
342 return nil, errors.New("db error")
343 },
344 },
345 },
346 status: http.StatusInternalServerError,
347 err: "db error",
348 },
349 {
350 name: "success",
351 client: &Client{
352 driverClient: &mock.ClientReplicator{
353 GetReplicationsFunc: func(_ context.Context, options driver.Options) ([]driver.Replication, error) {
354 gotOpts := map[string]interface{}{}
355 options.Apply(gotOpts)
356 wantOpts := map[string]interface{}{"foo": 123}
357 if d := testy.DiffInterface(wantOpts, gotOpts); d != nil {
358 return nil, fmt.Errorf("Unexpected options:\n%v", d)
359 }
360 return []driver.Replication{
361 &mock.Replication{ID: "1"},
362 &mock.Replication{ID: "2"},
363 }, nil
364 },
365 },
366 },
367 options: Param("foo", 123),
368 expected: []*Replication{
369 {
370 Source: "1-source",
371 Target: "1-target",
372 irep: &mock.Replication{ID: "1"},
373 },
374 {
375 Source: "2-source",
376 Target: "2-target",
377 irep: &mock.Replication{ID: "2"},
378 },
379 },
380 },
381 {
382 name: "closed",
383 client: &Client{
384 closed: true,
385 },
386 status: http.StatusServiceUnavailable,
387 err: "kivik: client closed",
388 },
389 }
390 for _, test := range tests {
391 t.Run(test.name, func(t *testing.T) {
392 result, err := test.client.GetReplications(context.Background(), test.options)
393 if d := internal.StatusErrorDiff(test.err, test.status, err); d != "" {
394 t.Error(d)
395 }
396 if d := testy.DiffInterface(test.expected, result); d != nil {
397 t.Error(d)
398 }
399 })
400 }
401 }
402
403 func TestReplicate(t *testing.T) {
404 tests := []struct {
405 name string
406 client *Client
407 target, source string
408 options Option
409 expected *Replication
410 status int
411 err string
412 }{
413 {
414 name: "non-replicator",
415 client: &Client{
416 driverClient: &mock.Client{},
417 },
418 status: http.StatusNotImplemented,
419 err: "kivik: driver does not support replication",
420 },
421 {
422 name: "db error",
423 client: &Client{
424 driverClient: &mock.ClientReplicator{
425 ReplicateFunc: func(context.Context, string, string, driver.Options) (driver.Replication, error) {
426 return nil, errors.New("db error")
427 },
428 },
429 },
430 status: http.StatusInternalServerError,
431 err: "db error",
432 },
433 {
434 name: "success",
435 client: &Client{
436 driverClient: &mock.ClientReplicator{
437 ReplicateFunc: func(_ context.Context, target, source string, options driver.Options) (driver.Replication, error) {
438 expectedTarget := "foo"
439 expectedSource := "bar"
440 gotOpts := map[string]interface{}{}
441 options.Apply(gotOpts)
442 wantOpts := map[string]interface{}{"foo": 123}
443 if target != expectedTarget {
444 return nil, fmt.Errorf("Unexpected target: %s", target)
445 }
446 if source != expectedSource {
447 return nil, fmt.Errorf("Unexpected source: %s", source)
448 }
449 if d := testy.DiffInterface(wantOpts, gotOpts); d != nil {
450 return nil, fmt.Errorf("Unexpected options:\n%v", d)
451 }
452 return &mock.Replication{ID: "a"}, nil
453 },
454 },
455 },
456 target: "foo",
457 source: "bar",
458 options: Param("foo", 123),
459 expected: &Replication{
460 Source: "a-source",
461 Target: "a-target",
462 irep: &mock.Replication{ID: "a"},
463 },
464 },
465 {
466 name: "closed",
467 client: &Client{
468 closed: true,
469 },
470 status: http.StatusServiceUnavailable,
471 err: "kivik: client closed",
472 },
473 }
474 for _, test := range tests {
475 t.Run(test.name, func(t *testing.T) {
476 result, err := test.client.Replicate(context.Background(), test.target, test.source, test.options)
477 if d := internal.StatusErrorDiff(test.err, test.status, err); d != "" {
478 t.Error(d)
479 }
480 if d := testy.DiffInterface(test.expected, result); d != nil {
481 t.Error(d)
482 }
483 })
484 }
485 }
486
View as plain text