github.com/go-kivik/kivik/v4@v4.3.2/replicate_live_test.go (about) 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 }