...

Source file src/github.com/go-kivik/kivik/v4/replicate_live_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  // TODO: Try to re-enable these tests when we're on a newer version of GopherJS.
    14  
    15  //go:build !js
    16  
    17  package kivik_test
    18  
    19  import (
    20  	"context"
    21  	"net/http"
    22  	"os"
    23  	"testing"
    24  	"time"
    25  
    26  	"gitlab.com/flimzy/testy"
    27  
    28  	"github.com/go-kivik/kivik/v4"
    29  	_ "github.com/go-kivik/kivik/v4/couchdb" // CouchDB driver
    30  	internal "github.com/go-kivik/kivik/v4/int/errors"
    31  	"github.com/go-kivik/kivik/v4/kiviktest/kt"
    32  	_ "github.com/go-kivik/kivik/v4/x/fsdb" // Filesystem driver
    33  )
    34  
    35  func TestReplicate_live(t *testing.T) { //nolint:gocyclo // allowed for subtests
    36  	if isGopherJS117 {
    37  		t.Skip("Replication doesn't work in GopherJS 1.17")
    38  	}
    39  	type tt struct {
    40  		source, target *kivik.DB
    41  		options        kivik.Option
    42  		status         int
    43  		err            string
    44  		result         *kivik.ReplicationResult
    45  	}
    46  	tests := testy.NewTable()
    47  	tests.Add("couch to couch", func(t *testing.T) interface{} {
    48  		dsn := kt.DSN3(t)
    49  		client, err := kivik.New("couch", dsn)
    50  		if err != nil {
    51  			t.Fatal(err)
    52  		}
    53  		sourceName := kt.TestDBName(t)
    54  		targetName := kt.TestDBName(t)
    55  		ctx := context.Background()
    56  		if err := client.CreateDB(ctx, sourceName); err != nil {
    57  			t.Fatal(err)
    58  		}
    59  		tests.Cleanup(func() {
    60  			_ = client.DestroyDB(ctx, sourceName)
    61  		})
    62  		if err := client.CreateDB(ctx, targetName); err != nil {
    63  			t.Fatal(err)
    64  		}
    65  		tests.Cleanup(func() {
    66  			_ = client.DestroyDB(ctx, targetName)
    67  		})
    68  		source := client.DB(sourceName)
    69  		target := client.DB(targetName)
    70  		doc := map[string]string{"foo": "bar"}
    71  		if _, err := source.Put(ctx, "foo", doc); err != nil {
    72  			t.Fatal(err)
    73  		}
    74  
    75  		return tt{
    76  			source: source,
    77  			target: target,
    78  			result: &kivik.ReplicationResult{
    79  				DocsRead:       1,
    80  				DocsWritten:    1,
    81  				MissingChecked: 1,
    82  				MissingFound:   1,
    83  			},
    84  		}
    85  	})
    86  	tests.Add("fs to couch", func(t *testing.T) interface{} {
    87  		fsclient, err := kivik.New("fs", "testdata/")
    88  		if err != nil {
    89  			t.Fatal(err)
    90  		}
    91  		dsn := kt.DSN3(t)
    92  		client, err := kivik.New("couch", dsn)
    93  		if err != nil {
    94  			t.Fatal(err)
    95  		}
    96  		ctx := context.Background()
    97  		source := fsclient.DB("db1")
    98  		targetName := kt.TestDBName(t)
    99  		if err := client.CreateDB(ctx, targetName); err != nil {
   100  			t.Fatal(err)
   101  		}
   102  		tests.Cleanup(func() {
   103  			_ = client.DestroyDB(ctx, targetName)
   104  		})
   105  		target := client.DB(targetName)
   106  
   107  		return tt{
   108  			source: source,
   109  			target: target,
   110  			result: &kivik.ReplicationResult{
   111  				DocsRead:       1,
   112  				DocsWritten:    1,
   113  				MissingChecked: 1,
   114  				MissingFound:   1,
   115  			},
   116  		}
   117  	})
   118  	tests.Add("fs to couch, no shared history", func(t *testing.T) interface{} {
   119  		fsclient, err := kivik.New("fs", "testdata/")
   120  		if err != nil {
   121  			t.Fatal(err)
   122  		}
   123  		dsn := kt.DSN3(t)
   124  		client, err := kivik.New("couch", dsn)
   125  		if err != nil {
   126  			t.Fatal(err)
   127  		}
   128  		ctx := context.Background()
   129  		source := fsclient.DB("db1")
   130  		targetName := kt.TestDBName(t)
   131  		if err := client.CreateDB(ctx, targetName); err != nil {
   132  			t.Fatal(err)
   133  		}
   134  		tests.Cleanup(func() {
   135  			_ = client.DestroyDB(ctx, targetName)
   136  		})
   137  		target := client.DB(targetName)
   138  
   139  		if _, err := kivik.Replicate(ctx, target, source); err != nil {
   140  			t.Fatalf("setup replication failed: %s", err)
   141  		}
   142  
   143  		return tt{
   144  			source: fsclient.DB("db2"),
   145  			target: target,
   146  			result: &kivik.ReplicationResult{
   147  				DocsRead:       1,
   148  				DocsWritten:    1,
   149  				MissingChecked: 1,
   150  				MissingFound:   1,
   151  			},
   152  		}
   153  	})
   154  	tests.Add("couch to couch with sec", func(t *testing.T) interface{} {
   155  		dsn := kt.DSN3(t)
   156  		client, err := kivik.New("couch", dsn)
   157  		if err != nil {
   158  			t.Fatal(err)
   159  		}
   160  		sourceName := kt.TestDBName(t)
   161  		targetName := kt.TestDBName(t)
   162  		ctx := context.Background()
   163  		if err := client.CreateDB(ctx, sourceName); err != nil {
   164  			t.Fatal(err)
   165  		}
   166  		tests.Cleanup(func() {
   167  			_ = client.DestroyDB(ctx, sourceName)
   168  		})
   169  		if err := client.CreateDB(ctx, targetName); err != nil {
   170  			t.Fatal(err)
   171  		}
   172  		tests.Cleanup(func() {
   173  			_ = client.DestroyDB(ctx, targetName)
   174  		})
   175  		source := client.DB(sourceName)
   176  		target := client.DB(targetName)
   177  		doc := map[string]string{"foo": "bar"}
   178  		if _, err := source.Put(ctx, "foo", doc); err != nil {
   179  			t.Fatal(err)
   180  		}
   181  		err = source.SetSecurity(ctx, &kivik.Security{
   182  			Members: kivik.Members{
   183  				Names: []string{"bob"},
   184  			},
   185  		})
   186  		if err != nil {
   187  			t.Fatal(err)
   188  		}
   189  
   190  		return tt{
   191  			source:  source,
   192  			target:  target,
   193  			options: kivik.ReplicateCopySecurity(),
   194  			result: &kivik.ReplicationResult{
   195  				DocsRead:       1,
   196  				DocsWritten:    1,
   197  				MissingChecked: 1,
   198  				MissingFound:   1,
   199  			},
   200  		}
   201  	})
   202  	tests.Add("fs to couch, bad put", func(t *testing.T) interface{} {
   203  		fsclient, err := kivik.New("fs", "testdata/")
   204  		if err != nil {
   205  			t.Fatal(err)
   206  		}
   207  		dsn := kt.DSN3(t)
   208  		client, err := kivik.New("couch", dsn)
   209  		if err != nil {
   210  			t.Fatal(err)
   211  		}
   212  		ctx := context.Background()
   213  		targetName := kt.TestDBName(t)
   214  		if err := client.CreateDB(ctx, targetName); err != nil {
   215  			t.Fatal(err)
   216  		}
   217  		tests.Cleanup(func() {
   218  			_ = client.DestroyDB(ctx, targetName)
   219  		})
   220  		target := client.DB(targetName)
   221  
   222  		return tt{
   223  			source: fsclient.DB("db3"),
   224  			target: target,
   225  			result: &kivik.ReplicationResult{
   226  				DocsRead:       1,
   227  				DocsWritten:    1,
   228  				MissingChecked: 1,
   229  				MissingFound:   1,
   230  			},
   231  			status: http.StatusBadRequest,
   232  			err:    "store doc note--XkWjFv13acvjJTt-CGJJ8hXlWE: Bad Request: Bad special document member: _invalid",
   233  		}
   234  	})
   235  	tests.Add("fs to couch with attachment", func(t *testing.T) interface{} {
   236  		fsclient, err := kivik.New("fs", "testdata/")
   237  		if err != nil {
   238  			t.Fatal(err)
   239  		}
   240  		dsn := kt.DSN3(t)
   241  		client, err := kivik.New("couch", dsn)
   242  		if err != nil {
   243  			t.Fatal(err)
   244  		}
   245  		ctx := context.Background()
   246  		source := fsclient.DB("db4")
   247  		targetName := kt.TestDBName(t)
   248  		if err := client.CreateDB(ctx, targetName); err != nil {
   249  			t.Fatal(err)
   250  		}
   251  		tests.Cleanup(func() {
   252  			_ = client.DestroyDB(ctx, targetName)
   253  		})
   254  		target := client.DB(targetName)
   255  
   256  		return tt{
   257  			source: source,
   258  			target: target,
   259  			result: &kivik.ReplicationResult{
   260  				DocsRead:       1,
   261  				DocsWritten:    1,
   262  				MissingChecked: 1,
   263  				MissingFound:   1,
   264  			},
   265  		}
   266  	})
   267  	tests.Add("couch to fs", func(t *testing.T) interface{} {
   268  		tempDir, err := os.MkdirTemp("", "kivik.test.")
   269  		if err != nil {
   270  			t.Fatal(err)
   271  		}
   272  		tests.Cleanup(func() error {
   273  			return os.RemoveAll(tempDir)
   274  		})
   275  		tClient, err := kivik.New("fs", tempDir)
   276  		if err != nil {
   277  			t.Fatal(err)
   278  		}
   279  
   280  		dsn := kt.DSN3(t)
   281  		client, err := kivik.New("couch", dsn)
   282  		if err != nil {
   283  			t.Fatal(err)
   284  		}
   285  		dbName := kt.TestDBName(t)
   286  		ctx := context.Background()
   287  		if err := client.CreateDB(ctx, dbName); err != nil {
   288  			t.Fatal(err)
   289  		}
   290  		tests.Cleanup(func() {
   291  			_ = client.DestroyDB(ctx, dbName)
   292  		})
   293  		if err := tClient.CreateDB(ctx, dbName); err != nil {
   294  			t.Fatal(err)
   295  		}
   296  		source := client.DB(dbName)
   297  		target := tClient.DB(dbName)
   298  		doc := map[string]interface{}{
   299  			"foo": "bar",
   300  			"_attachments": map[string]interface{}{
   301  				"foo.txt": map[string]interface{}{
   302  					"content_type": "application/octet-stream",
   303  					"data":         []byte("Test content"),
   304  				},
   305  			},
   306  		}
   307  		if _, err := source.Put(ctx, "foo", doc); err != nil {
   308  			t.Fatal(err)
   309  		}
   310  
   311  		return tt{
   312  			source: source,
   313  			target: target,
   314  			result: &kivik.ReplicationResult{
   315  				DocsRead:       1,
   316  				DocsWritten:    1,
   317  				MissingChecked: 1,
   318  				MissingFound:   1,
   319  			},
   320  		}
   321  	})
   322  	tests.Add("fs to couch with deleted document", func(t *testing.T) interface{} {
   323  		fsclient, err := kivik.New("fs", "testdata/")
   324  		if err != nil {
   325  			t.Fatal(err)
   326  		}
   327  		dsn := kt.DSN3(t)
   328  		client, err := kivik.New("couch", dsn)
   329  		if err != nil {
   330  			t.Fatal(err)
   331  		}
   332  		ctx := context.Background()
   333  		source := fsclient.DB("dbdelete")
   334  		targetName := kt.TestDBName(t)
   335  		if err := client.CreateDB(ctx, targetName); err != nil {
   336  			t.Fatal(err)
   337  		}
   338  		tests.Cleanup(func() {
   339  			_ = client.DestroyDB(ctx, targetName)
   340  		})
   341  		target := client.DB(targetName)
   342  		if _, err := target.Put(ctx, "foo", map[string]string{"still": "here"}); err != nil {
   343  			t.Fatal(err)
   344  		}
   345  
   346  		return tt{
   347  			source: source,
   348  			target: target,
   349  			result: &kivik.ReplicationResult{
   350  				DocsRead:       1,
   351  				DocsWritten:    1,
   352  				MissingChecked: 1,
   353  				MissingFound:   1,
   354  			},
   355  		}
   356  	})
   357  	tests.Run(t, func(t *testing.T, tt tt) {
   358  		ctx := context.TODO()
   359  		result, err := kivik.Replicate(ctx, tt.target, tt.source, tt.options)
   360  		if d := internal.StatusErrorDiff(tt.err, tt.status, err); d != "" {
   361  			t.Error(d)
   362  		}
   363  		if err != nil {
   364  			return
   365  		}
   366  
   367  		verifyDoc(ctx, t, tt.target, tt.source, "foo")
   368  		verifySec(ctx, t, tt.target)
   369  		result.StartTime = time.Time{}
   370  		result.EndTime = time.Time{}
   371  		if d := testy.DiffAsJSON(tt.result, result); d != nil {
   372  			t.Error(d)
   373  		}
   374  	})
   375  }
   376  
   377  func verifyDoc(ctx context.Context, t *testing.T, target, source *kivik.DB, docID string) {
   378  	t.Helper()
   379  	var targetDoc, sourceDoc interface{}
   380  	notFound := false
   381  	if err := source.Get(ctx, docID).ScanDoc(&sourceDoc); err != nil {
   382  		if kivik.HTTPStatus(err) == http.StatusNotFound {
   383  			notFound = true
   384  		} else {
   385  			t.Fatalf("get %s from source failed: %s", docID, err)
   386  		}
   387  	}
   388  	if err := target.Get(ctx, docID).ScanDoc(&targetDoc); err != nil {
   389  		if notFound && kivik.HTTPStatus(err) == http.StatusNotFound {
   390  			return
   391  		}
   392  		t.Fatalf("get %s from target failed: %s", docID, err)
   393  	}
   394  	if d := testy.DiffAsJSON(sourceDoc, targetDoc); d != nil {
   395  		t.Error(d)
   396  	}
   397  }
   398  
   399  func verifySec(ctx context.Context, t *testing.T, target *kivik.DB) {
   400  	t.Helper()
   401  	sec, err := target.Security(ctx)
   402  	if err != nil {
   403  		t.Fatal(err)
   404  	}
   405  	if d := testy.DiffAsJSON(&testy.File{Path: "testdata/" + testy.Stub(t) + ".security"}, sec); d != nil {
   406  		t.Errorf("Security object:\n%s", d)
   407  	}
   408  }
   409  

View as plain text