...

Source file src/github.com/go-kivik/kivik/v4/replication_test.go

Documentation: github.com/go-kivik/kivik/v4

     1  // Licensed under the Apache License, Version 2.0 (the "License"); you may not
     2  // use this file except in compliance with the License. You may obtain a copy of
     3  // the License at
     4  //
     5  //  http://www.apache.org/licenses/LICENSE-2.0
     6  //
     7  // Unless required by applicable law or agreed to in writing, software
     8  // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
     9  // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
    10  // License for the specific language governing permissions and limitations under
    11  // the License.
    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