github.com/go-kivik/kivik/v4@v4.3.2/x/memorydb/db_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 package memorydb 14 15 import ( 16 "context" 17 "encoding/json" 18 "net/http" 19 "strings" 20 "testing" 21 22 "gitlab.com/flimzy/testy" 23 24 "github.com/go-kivik/kivik/v4" 25 "github.com/go-kivik/kivik/v4/driver" 26 internal "github.com/go-kivik/kivik/v4/int/errors" 27 ) 28 29 func TestStats(t *testing.T) { 30 type statTest struct { 31 Name string 32 DBName string 33 Setup func(driver.Client) 34 Expected *driver.DBStats 35 Error string 36 } 37 tests := []statTest{ 38 { 39 Name: "NoDBs", 40 DBName: "foo", 41 Setup: func(c driver.Client) { 42 if e := c.CreateDB(context.Background(), "foo", nil); e != nil { 43 panic(e) 44 } 45 }, 46 Expected: &driver.DBStats{Name: "foo"}, 47 }, 48 } 49 for _, test := range tests { 50 func(test statTest) { 51 t.Run(test.Name, func(t *testing.T) { 52 c := setup(t, test.Setup) 53 db, err := c.DB(test.DBName, nil) 54 if err != nil { 55 t.Fatal(err) 56 } 57 result, err := db.Stats(context.Background()) 58 var msg string 59 if err != nil { 60 msg = err.Error() 61 } 62 if msg != test.Error { 63 t.Errorf("Unexpected error: %s", msg) 64 } 65 if err != nil { 66 return 67 } 68 if d := testy.DiffInterface(test.Expected, result); d != nil { 69 t.Error(d) 70 } 71 }) 72 }(test) 73 } 74 } 75 76 func setupDB(t *testing.T) *db { 77 t.Helper() 78 c := setup(t, nil) 79 if err := c.CreateDB(context.Background(), "foo", nil); err != nil { 80 t.Fatal(err) 81 } 82 d, err := c.DB("foo", nil) 83 if err != nil { 84 t.Fatal(err) 85 } 86 return d.(*db) 87 } 88 89 func TestPut(t *testing.T) { 90 type putTest struct { 91 Name string 92 DocID string 93 Doc interface{} 94 Setup func() *db 95 Expected interface{} 96 Status int 97 Error string 98 } 99 tests := []putTest{ 100 { 101 Name: "LeadingUnderscoreInID", 102 DocID: "_badid", 103 Doc: map[string]string{"_id": "_badid", "foo": "bar"}, 104 Status: 400, 105 Error: "only reserved document ids may start with underscore", 106 }, 107 { 108 Name: "MismatchedIDs", 109 DocID: "foo", 110 Doc: map[string]string{"_id": "bar"}, 111 Expected: map[string]string{"_id": "foo", "_rev": "1-xxx"}, 112 }, 113 { 114 Name: "Success", 115 DocID: "foo", 116 Doc: map[string]string{"_id": "foo", "foo": "bar"}, 117 Expected: map[string]string{"_id": "foo", "foo": "bar", "_rev": "1-xxx"}, 118 }, 119 { 120 Name: "Conflict", 121 DocID: "foo", 122 Doc: map[string]string{"_id": "foo", "_rev": "bar"}, 123 Setup: func() *db { 124 db := setupDB(t) 125 if _, err := db.Put(context.Background(), "foo", map[string]string{"_id": "foo"}, nil); err != nil { 126 t.Fatal(err) 127 } 128 return db 129 }, 130 Status: 409, 131 Error: "document update conflict", 132 }, 133 { 134 Name: "Unmarshalable", 135 DocID: "foo", 136 Doc: func() interface{} { 137 return map[string]interface{}{ 138 "channel": make(chan int), 139 } 140 }(), 141 Status: 400, 142 Error: "json: unsupported type: chan int", 143 }, 144 { 145 Name: "InitialRev", 146 DocID: "foo", 147 Doc: map[string]string{"_id": "foo", "_rev": "bar"}, 148 Status: 409, 149 Error: "document update conflict", 150 }, 151 func() putTest { 152 dbv := setupDB(t) 153 rev, err := dbv.Put(context.Background(), "foo", map[string]string{"_id": "foo", "foo": "bar"}, nil) 154 if err != nil { 155 panic(err) 156 } 157 return putTest{ 158 Name: "Update", 159 DocID: "foo", 160 Setup: func() *db { return dbv }, 161 Doc: map[string]string{"_id": "foo", "_rev": rev}, 162 Expected: map[string]string{"_id": "foo", "_rev": "2-xxx"}, 163 } 164 }(), 165 { 166 Name: "DesignDoc", 167 DocID: "_design/foo", 168 Doc: map[string]string{"foo": "bar"}, 169 Expected: map[string]string{"_id": "_design/foo", "foo": "bar", "_rev": "1-xxx"}, 170 }, 171 { 172 Name: "LocalDoc", 173 DocID: "_local/foo", 174 Doc: map[string]string{"foo": "bar"}, 175 Expected: map[string]string{"_id": "_local/foo", "foo": "bar", "_rev": "1-0"}, 176 }, 177 { 178 Name: "RecreateDeleted", 179 DocID: "foo", 180 Doc: map[string]string{"foo": "bar"}, 181 Expected: map[string]string{"_id": "foo", "foo": "bar", "_rev": "3-xxx"}, 182 Setup: func() *db { 183 db := setupDB(t) 184 rev, err := db.Put(context.Background(), "foo", map[string]string{"_id": "foo"}, nil) 185 if err != nil { 186 t.Fatal(err) 187 } 188 if _, e := db.Delete(context.Background(), "foo", kivik.Rev(rev)); e != nil { 189 t.Fatal(e) 190 } 191 return db 192 }, 193 }, 194 { 195 Name: "LocalDoc", 196 DocID: "_local/foo", 197 Doc: map[string]string{"foo": "baz"}, 198 Expected: map[string]string{"_id": "_local/foo", "foo": "baz", "_rev": "1-0"}, 199 Setup: func() *db { 200 db := setupDB(t) 201 _, err := db.Put(context.Background(), "_local/foo", map[string]string{"foo": "bar"}, nil) 202 if err != nil { 203 t.Fatal(err) 204 } 205 return db 206 }, 207 }, 208 { 209 Name: "WithAttachments", 210 DocID: "duck", 211 Doc: map[string]interface{}{ 212 "_id": "duck", 213 "value": "quack", 214 "_attachments": []map[string]interface{}{ 215 {"foo.css": map[string]string{ 216 "content_type": "text/css", 217 "data": "LyogYW4gZW1wdHkgQ1NTIGZpbGUgKi8=", 218 }}, 219 }, 220 }, 221 Expected: map[string]string{ 222 "_id": "duck", 223 "_rev": "1-xxx", 224 "value": "quack", 225 }, 226 }, 227 { 228 Name: "Deleted DB", 229 Setup: func() *db { 230 c := setup(t, nil) 231 if err := c.CreateDB(context.Background(), "deleted0", nil); err != nil { 232 t.Fatal(err) 233 } 234 dbv, err := c.DB("deleted0", nil) 235 if err != nil { 236 t.Fatal(err) 237 } 238 if e := c.DestroyDB(context.Background(), "deleted0", nil); e != nil { 239 t.Fatal(e) 240 } 241 return dbv.(*db) 242 }, 243 Status: http.StatusPreconditionFailed, 244 Error: "database does not exist", 245 }, 246 } 247 for _, test := range tests { 248 func(test putTest) { 249 t.Run(test.Name, func(t *testing.T) { 250 t.Parallel() 251 var db *db 252 if test.Setup != nil { 253 db = test.Setup() 254 } else { 255 db = setupDB(t) 256 } 257 _, err := db.Put(context.Background(), test.DocID, test.Doc, nil) 258 if d := internal.StatusErrorDiff(test.Error, test.Status, err); d != "" { 259 t.Error(d) 260 } 261 if err != nil { 262 return 263 } 264 rows, err := db.Get(context.Background(), test.DocID, kivik.Params(nil)) 265 if err != nil { 266 t.Fatal(err) 267 } 268 var result map[string]interface{} 269 if e := json.NewDecoder(rows.Body).Decode(&result); e != nil { 270 t.Fatal(e) 271 } 272 if !strings.HasPrefix(test.DocID, "_local/") { 273 if rev, ok := result["_rev"].(string); ok { 274 parts := strings.SplitN(rev, "-", 2) 275 result["_rev"] = parts[0] + "-xxx" 276 } 277 } 278 if d := testy.DiffAsJSON(test.Expected, result); d != nil { 279 t.Error(d) 280 } 281 }) 282 }(test) 283 } 284 } 285 286 func TestGet(t *testing.T) { 287 type getTest struct { 288 Name string 289 ID string 290 options kivik.Option 291 DB *db 292 Status int 293 Error string 294 Expected interface{} 295 } 296 tests := []getTest{ 297 { 298 Name: "NotFound", 299 ID: "foo", 300 Status: 404, 301 Error: "missing", 302 }, 303 func() getTest { 304 db := setupDB(t) 305 _, err := db.Put(context.Background(), "foo", map[string]string{"_id": "foo", "foo": "bar"}, nil) 306 if err != nil { 307 panic(err) 308 } 309 return getTest{ 310 Name: "ExistingDoc", 311 ID: "foo", 312 DB: db, 313 Expected: map[string]string{"_id": "foo", "foo": "bar"}, 314 } 315 }(), 316 func() getTest { 317 db := setupDB(t) 318 rev, err := db.Put(context.Background(), "foo", map[string]string{"_id": "foo", "foo": "Bar"}, nil) 319 if err != nil { 320 panic(err) 321 } 322 return getTest{ 323 Name: "SpecificRev", 324 ID: "foo", 325 DB: db, 326 options: kivik.Rev(rev), 327 Expected: map[string]string{"_id": "foo", "foo": "Bar"}, 328 } 329 }(), 330 func() getTest { 331 db := setupDB(t) 332 rev, err := db.Put(context.Background(), "foo", map[string]string{"_id": "foo", "foo": "Bar"}, nil) 333 if err != nil { 334 panic(err) 335 } 336 _, err = db.Put(context.Background(), "foo", map[string]string{"_id": "foo", "foo": "baz", "_rev": rev}, nil) 337 if err != nil { 338 panic(err) 339 } 340 return getTest{ 341 Name: "OldRev", 342 ID: "foo", 343 DB: db, 344 options: kivik.Rev(rev), 345 Expected: map[string]string{"_id": "foo", "foo": "Bar"}, 346 } 347 }(), 348 { 349 Name: "MissingRev", 350 ID: "foo", 351 options: kivik.Rev("1-4c6114c65e295552ab1019e2b046b10e"), 352 DB: func() *db { 353 db := setupDB(t) 354 _, err := db.Put(context.Background(), "foo", map[string]string{"_id": "foo", "foo": "Bar"}, nil) 355 if err != nil { 356 panic(err) 357 } 358 return db 359 }(), 360 Status: 404, 361 Error: "missing", 362 }, 363 func() getTest { 364 db := setupDB(t) 365 rev, err := db.Put(context.Background(), "foo", map[string]string{"_id": "foo"}, nil) 366 if err != nil { 367 panic(err) 368 } 369 if _, e := db.Delete(context.Background(), "foo", kivik.Rev(rev)); e != nil { 370 panic(e) 371 } 372 return getTest{ 373 Name: "DeletedDoc", 374 ID: "foo", 375 DB: db, 376 Status: 404, 377 Error: "missing", 378 } 379 }(), 380 { 381 Name: "Deleted DB", 382 DB: func() *db { 383 c := setup(t, nil) 384 if err := c.CreateDB(context.Background(), "deleted0", nil); err != nil { 385 t.Fatal(err) 386 } 387 dbv, err := c.DB("deleted0", nil) 388 if err != nil { 389 t.Fatal(err) 390 } 391 if e := c.DestroyDB(context.Background(), "deleted0", nil); e != nil { 392 t.Fatal(e) 393 } 394 return dbv.(*db) 395 }(), 396 Error: "database does not exist", 397 Status: http.StatusPreconditionFailed, 398 }, 399 } 400 for _, test := range tests { 401 func(test getTest) { 402 t.Run(test.Name, func(t *testing.T) { 403 t.Parallel() 404 db := test.DB 405 if db == nil { 406 db = setupDB(t) 407 } 408 opts := test.options 409 if opts == nil { 410 opts = kivik.Params(nil) 411 } 412 rows, err := db.Get(context.Background(), test.ID, opts) 413 if d := internal.StatusErrorDiff(test.Error, test.Status, err); d != "" { 414 t.Error(d) 415 } 416 if err != nil { 417 return 418 } 419 420 var result map[string]interface{} 421 if err := json.NewDecoder(rows.Body).Decode(&result); err != nil { 422 t.Fatal(err) 423 } 424 425 if result != nil { 426 delete(result, "_rev") 427 } 428 if d := testy.DiffAsJSON(test.Expected, result); d != nil { 429 t.Error(d) 430 } 431 }) 432 }(test) 433 } 434 } 435 436 func TestDeleteDoc(t *testing.T) { 437 type delTest struct { 438 Name string 439 ID string 440 Rev string 441 DB *db 442 Status int 443 Error string 444 } 445 tests := []delTest{ 446 { 447 Name: "NonExistingDoc", 448 ID: "foo", 449 Rev: "1-4c6114c65e295552ab1019e2b046b10e", 450 Status: 404, 451 Error: "missing", 452 }, 453 func() delTest { 454 db := setupDB(t) 455 rev, err := db.Put(context.Background(), "foo", map[string]string{"_id": "foo"}, nil) 456 if err != nil { 457 panic(err) 458 } 459 return delTest{ 460 Name: "Success", 461 ID: "foo", 462 DB: db, 463 Rev: rev, 464 } 465 }(), 466 { 467 Name: "InvalidRevFormat", 468 ID: "foo", 469 Rev: "invalid rev format", 470 Status: 400, 471 Error: "invalid rev format", 472 }, 473 { 474 Name: "LocalNoRev", 475 ID: "_local/foo", 476 Rev: "", 477 DB: func() *db { 478 db := setupDB(t) 479 if _, err := db.Put(context.Background(), "_local/foo", map[string]string{"foo": "bar"}, nil); err != nil { 480 panic(err) 481 } 482 return db 483 }(), 484 }, 485 { 486 Name: "LocalWithRev", 487 ID: "_local/foo", 488 Rev: "0-1", 489 DB: func() *db { 490 db := setupDB(t) 491 if _, err := db.Put(context.Background(), "_local/foo", map[string]string{"foo": "bar"}, nil); err != nil { 492 panic(err) 493 } 494 return db 495 }(), 496 }, 497 { 498 Name: "DB deleted", 499 ID: "foo", 500 DB: func() *db { 501 c := setup(t, nil) 502 if err := c.CreateDB(context.Background(), "deleted0", nil); err != nil { 503 t.Fatal(err) 504 } 505 dbv, err := c.DB("deleted0", nil) 506 if err != nil { 507 t.Fatal(err) 508 } 509 if e := c.DestroyDB(context.Background(), "deleted0", nil); e != nil { 510 t.Fatal(e) 511 } 512 return dbv.(*db) 513 }(), 514 Status: http.StatusPreconditionFailed, 515 Error: "database does not exist", 516 }, 517 } 518 for _, test := range tests { 519 func(test delTest) { 520 t.Run(test.Name, func(t *testing.T) { 521 t.Parallel() 522 db := test.DB 523 if db == nil { 524 db = setupDB(t) 525 } 526 rev, err := db.Delete(context.Background(), test.ID, kivik.Rev(test.Rev)) 527 var msg string 528 var status int 529 if err != nil { 530 msg = err.Error() 531 status = kivik.HTTPStatus(err) 532 } 533 if msg != test.Error { 534 t.Errorf("Unexpected error: %s", msg) 535 } 536 if status != test.Status { 537 t.Errorf("Unexpected status: %d", status) 538 } 539 if err != nil { 540 return 541 } 542 result, err := db.Get(context.Background(), test.ID, kivik.Rev(rev)) 543 if err != nil { 544 t.Fatal(err) 545 } 546 var doc interface{} 547 if e := json.NewDecoder(result.Body).Decode(&doc); e != nil { 548 t.Fatal(e) 549 } 550 expected := map[string]interface{}{ 551 "_id": test.ID, 552 "_rev": rev, 553 "_deleted": true, 554 } 555 if d := testy.DiffAsJSON(expected, doc); d != nil { 556 t.Error(d) 557 } 558 }) 559 }(test) 560 } 561 }