diff --git a/x/sqlite/delete_test.go b/x/sqlite/delete_test.go index a554a1052..0716846ed 100644 --- a/x/sqlite/delete_test.go +++ b/x/sqlite/delete_test.go @@ -30,7 +30,7 @@ import ( func TestDBDelete(t *testing.T) { t.Parallel() type test struct { - setup func(*testing.T, driver.DB) + db driver.DB id string options driver.Options wantRev string @@ -45,96 +45,110 @@ func TestDBDelete(t *testing.T) { wantStatus: http.StatusNotFound, wantErr: "document not found", }) - tests.Add("success", test{ - setup: func(t *testing.T, d driver.DB) { - _, err := d.Put(context.Background(), "foo", map[string]string{"foo": "bar"}, mock.NilOption) - if err != nil { - t.Fatal(err) - } - }, - id: "foo", - options: kivik.Rev("1-9bb58f26192e4ba00f01e2e7b136bbd8"), - wantRev: "2-df2a4fe30cde39c357c8d1105748d1b9", - check: func(t *testing.T, d driver.DB) { - var deleted bool - err := d.(*db).db.QueryRow(` + tests.Add("success", func(t *testing.T) interface{} { + dbc := newDB(t) + rev, err := dbc.Put(context.Background(), "foo", map[string]string{"foo": "bar"}, mock.NilOption) + if err != nil { + t.Fatal(err) + } + + return test{ + db: dbc, + id: "foo", + options: kivik.Rev(rev), + wantRev: "2-df2a4fe30cde39c357c8d1105748d1b9", + check: func(t *testing.T, d driver.DB) { + var deleted bool + err := d.(*db).db.QueryRow(` SELECT deleted FROM test WHERE id='foo' ORDER BY rev DESC, rev_id DESC LIMIT 1 `).Scan(&deleted) - if err != nil { - t.Fatal(err) - } - if !deleted { - t.Errorf("Document not marked deleted") - } - }, + if err != nil { + t.Fatal(err) + } + if !deleted { + t.Errorf("Document not marked deleted") + } + }, + } }) - tests.Add("replay delete should conflict", test{ - setup: func(t *testing.T, d driver.DB) { - rev, err := d.Put(context.Background(), "foo", map[string]string{"foo": "bar"}, mock.NilOption) - if err != nil { - t.Fatal(err) - } - _, err = d.Delete(context.Background(), "foo", kivik.Rev(rev)) - if err != nil { - t.Fatal(err) - } - }, - id: "foo", - options: kivik.Rev("1-9bb58f26192e4ba00f01e2e7b136bbd8"), - wantStatus: http.StatusConflict, - wantErr: "conflict", + tests.Add("replay delete should conflict", func(t *testing.T) interface{} { + db := newDB(t) + rev, err := db.Put(context.Background(), "foo", map[string]string{"foo": "bar"}, mock.NilOption) + if err != nil { + t.Fatal(err) + } + _, err = db.Delete(context.Background(), "foo", kivik.Rev(rev)) + if err != nil { + t.Fatal(err) + } + + return test{ + db: db, + id: "foo", + options: kivik.Rev(rev), + wantStatus: http.StatusConflict, + wantErr: "conflict", + } }) - tests.Add("delete deleted doc should succeed", test{ - setup: func(t *testing.T, d driver.DB) { - rev, err := d.Put(context.Background(), "foo", map[string]string{"foo": "bar"}, mock.NilOption) - if err != nil { - t.Fatal(err) - } - _, err = d.Delete(context.Background(), "foo", kivik.Rev(rev)) - if err != nil { - t.Fatal(err) - } - }, - id: "foo", - options: kivik.Rev("2-df2a4fe30cde39c357c8d1105748d1b9"), - wantRev: "3-df2a4fe30cde39c357c8d1105748d1b9", + tests.Add("delete deleted doc should succeed", func(t *testing.T) interface{} { + db := newDB(t) + rev, err := db.Put(context.Background(), "foo", map[string]string{"foo": "bar"}, mock.NilOption) + if err != nil { + t.Fatal(err) + } + rev2, err := db.Delete(context.Background(), "foo", kivik.Rev(rev)) + if err != nil { + t.Fatal(err) + } + + return test{ + db: db, + id: "foo", + options: kivik.Rev(rev2), + wantRev: "3-df2a4fe30cde39c357c8d1105748d1b9", + } }) - tests.Add("delete without rev", test{ - setup: func(t *testing.T, d driver.DB) { - _, err := d.Put(context.Background(), "foo", map[string]string{"foo": "bar"}, mock.NilOption) - if err != nil { - t.Fatal(err) - } - }, - id: "foo", - wantStatus: http.StatusConflict, - wantErr: "conflict", + tests.Add("delete without rev", func(t *testing.T) interface{} { + db := newDB(t) + _, err := db.Put(context.Background(), "foo", map[string]string{"foo": "bar"}, mock.NilOption) + if err != nil { + t.Fatal(err) + } + + return test{ + db: db, + id: "foo", + wantStatus: http.StatusConflict, + wantErr: "conflict", + } }) + tests.Add("delete losing rev for conflict should succeed", func(t *testing.T) interface{} { + db := newDB(t) + _, err := db.Put(context.Background(), "foo", map[string]string{ + "cat": "meow", + "_rev": "1-xxx", + }, kivik.Param("new_edits", false)) + if err != nil { + t.Fatal(err) + } + _, err = db.Put(context.Background(), "foo", map[string]string{ + "cat": "purr", + "_rev": "1-aaa", + }, kivik.Param("new_edits", false)) + if err != nil { + t.Fatal(err) + } - tests.Add("delete losing rev for conflict should succeed", test{ - setup: func(t *testing.T, db driver.DB) { - _, err := db.Put(context.Background(), "foo", map[string]string{ - "cat": "meow", - "_rev": "1-xxx", - }, kivik.Param("new_edits", false)) - if err != nil { - t.Fatal(err) - } - _, err = db.Put(context.Background(), "foo", map[string]string{ - "cat": "purr", - "_rev": "1-aaa", - }, kivik.Param("new_edits", false)) - if err != nil { - t.Fatal(err) - } - }, - id: "foo", - options: kivik.Rev("1-aaa"), - wantRev: "2-df2a4fe30cde39c357c8d1105748d1b9", + return test{ + db: db, + id: "foo", + options: kivik.Rev("1-aaa"), + wantRev: "2-df2a4fe30cde39c357c8d1105748d1b9", + } }) tests.Add("invalid rev format", test{ id: "foo", @@ -149,9 +163,9 @@ func TestDBDelete(t *testing.T) { tests.Run(t, func(t *testing.T, tt test) { t.Parallel() - db := newDB(t) - if tt.setup != nil { - tt.setup(t, db) + db := tt.db + if db == nil { + db = newDB(t) } opts := tt.options if opts == nil { diff --git a/x/sqlite/deleteattachment_test.go b/x/sqlite/deleteattachment_test.go index 5710286cc..0673dcc17 100644 --- a/x/sqlite/deleteattachment_test.go +++ b/x/sqlite/deleteattachment_test.go @@ -31,7 +31,7 @@ import ( func TestDBDeleteAttachment(t *testing.T) { t.Parallel() type test struct { - setup func(*testing.T, driver.DB) + db driver.DB docID string filename string options driver.Options @@ -51,30 +51,36 @@ func TestDBDeleteAttachment(t *testing.T) { wantErr: "document not found", wantStatus: http.StatusNotFound, }) - tests.Add("doc exists, but no rev provided", test{ - setup: func(t *testing.T, d driver.DB) { - _, err := d.Put(context.Background(), "foo", map[string]string{"foo": "bar"}, mock.NilOption) - if err != nil { - t.Fatal(err) - } - }, - docID: "foo", - filename: "foo.txt", - wantErr: "conflict", - wantStatus: http.StatusConflict, + tests.Add("doc exists, but no rev provided", func(t *testing.T) interface{} { + db := newDB(t) + _, err := db.Put(context.Background(), "foo", map[string]string{"foo": "bar"}, mock.NilOption) + if err != nil { + t.Fatal(err) + } + + return test{ + db: db, + docID: "foo", + filename: "foo.txt", + wantErr: "conflict", + wantStatus: http.StatusConflict, + } }) - tests.Add("doc exists, but wrong rev provided", test{ - setup: func(t *testing.T, d driver.DB) { - _, err := d.Put(context.Background(), "foo", map[string]string{"foo": "bar"}, mock.NilOption) - if err != nil { - t.Fatal(err) - } - }, - docID: "foo", - filename: "foo.txt", - options: kivik.Rev("1-wrong"), - wantErr: "document not found", - wantStatus: http.StatusNotFound, + tests.Add("doc exists, but wrong rev provided", func(t *testing.T) interface{} { + db := newDB(t) + _, err := db.Put(context.Background(), "foo", map[string]string{"foo": "bar"}, mock.NilOption) + if err != nil { + t.Fatal(err) + } + + return test{ + db: db, + docID: "foo", + filename: "foo.txt", + options: kivik.Rev("1-wrong"), + wantErr: "document not found", + wantStatus: http.StatusNotFound, + } }) /* @@ -85,9 +91,9 @@ func TestDBDeleteAttachment(t *testing.T) { tests.Run(t, func(t *testing.T, tt test) { t.Parallel() - dbc := newDB(t) - if tt.setup != nil { - tt.setup(t, dbc) + dbc := tt.db + if dbc == nil { + dbc = newDB(t) } opts := tt.options if opts == nil { diff --git a/x/sqlite/get_test.go b/x/sqlite/get_test.go index 0ae418623..c72fd6843 100644 --- a/x/sqlite/get_test.go +++ b/x/sqlite/get_test.go @@ -31,9 +31,8 @@ import ( ) func TestDBGet(t *testing.T) { - t.Parallel() type test struct { - setup func(*testing.T, driver.DB) + db driver.DB id string options driver.Options wantDoc interface{} @@ -46,38 +45,44 @@ func TestDBGet(t *testing.T) { wantStatus: http.StatusNotFound, wantErr: "not found", }) - tests.Add("success", test{ - setup: func(t *testing.T, d driver.DB) { - _, err := d.Put(context.Background(), "foo", map[string]string{"foo": "bar"}, mock.NilOption) - if err != nil { - t.Fatal(err) - } - }, - id: "foo", - wantDoc: map[string]string{ - "_id": "foo", - "_rev": "1-9bb58f26192e4ba00f01e2e7b136bbd8", - "foo": "bar", - }, + tests.Add("success", func(t *testing.T) interface{} { + db := newDB(t) + rev, err := db.Put(context.Background(), "foo", map[string]string{"foo": "bar"}, mock.NilOption) + if err != nil { + t.Fatal(err) + } + + return test{ + db: db, + id: "foo", + wantDoc: map[string]string{ + "_id": "foo", + "_rev": rev, + "foo": "bar", + }, + } }) - tests.Add("get specific rev", test{ - setup: func(t *testing.T, d driver.DB) { - rev, err := d.Put(context.Background(), "foo", map[string]string{"foo": "bar"}, mock.NilOption) - if err != nil { - t.Fatal(err) - } - _, err = d.Put(context.Background(), "foo", map[string]string{"foo": "baz"}, kivik.Rev(rev)) - if err != nil { - t.Fatal(err) - } - }, - id: "foo", - options: kivik.Rev("1-9bb58f26192e4ba00f01e2e7b136bbd8"), - wantDoc: map[string]string{ - "_id": "foo", - "_rev": "1-9bb58f26192e4ba00f01e2e7b136bbd8", - "foo": "bar", - }, + tests.Add("get specific rev", func(t *testing.T) interface{} { + db := newDB(t) + rev, err := db.Put(context.Background(), "foo", map[string]string{"foo": "bar"}, mock.NilOption) + if err != nil { + t.Fatal(err) + } + _, err = db.Put(context.Background(), "foo", map[string]string{"foo": "baz"}, kivik.Rev(rev)) + if err != nil { + t.Fatal(err) + } + + return test{ + db: db, + id: "foo", + options: kivik.Rev(rev), + wantDoc: map[string]string{ + "_id": "foo", + "_rev": rev, + "foo": "bar", + }, + } }) tests.Add("specific rev not found", test{ id: "foo", @@ -85,979 +90,1063 @@ func TestDBGet(t *testing.T) { wantStatus: http.StatusNotFound, wantErr: "not found", }) - tests.Add("include conflicts", test{ - setup: func(t *testing.T, d driver.DB) { - _, err := d.Put(context.Background(), "foo", map[string]string{"foo": "bar"}, kivik.Params(map[string]interface{}{ - "new_edits": false, - "rev": "1-abc", - })) - if err != nil { - t.Fatal(err) - } - _, err = d.Put(context.Background(), "foo", map[string]string{"foo": "baz"}, kivik.Params(map[string]interface{}{ - "new_edits": false, - "rev": "1-xyz", - })) - if err != nil { - t.Fatal(err) - } - }, - id: "foo", - options: kivik.Param("conflicts", true), - wantDoc: map[string]interface{}{ - "_id": "foo", - "_rev": "1-xyz", - "foo": "baz", - "_conflicts": []string{"1-abc"}, - }, - }) - tests.Add("include only leaf conflicts", test{ - setup: func(t *testing.T, d driver.DB) { - _, err := d.Put(context.Background(), "foo", map[string]string{"foo": "bar"}, kivik.Params(map[string]interface{}{ - "new_edits": false, - "rev": "1-abc", - })) - if err != nil { - t.Fatal(err) - } - _, err = d.Put(context.Background(), "foo", map[string]string{"foo": "baz"}, kivik.Params(map[string]interface{}{ - "new_edits": false, - "rev": "1-xyz", - })) - if err != nil { - t.Fatal(err) - } - _, err = d.Put(context.Background(), "foo", map[string]string{"foo": "qux"}, kivik.Rev("1-xyz")) - if err != nil { - t.Fatal(err) - } - }, - id: "foo", - options: kivik.Param("conflicts", true), - wantDoc: map[string]interface{}{ - "_id": "foo", - "_rev": "2-8ecd3d54a4d763ebc0b6e6666d9af066", - "foo": "qux", - "_conflicts": []string{"1-abc"}, - }, - }) - tests.Add("deleted document", test{ - setup: func(t *testing.T, d driver.DB) { - rev, err := d.Put(context.Background(), "foo", map[string]string{"foo": "bar"}, mock.NilOption) - if err != nil { - t.Fatal(err) - } - _, err = d.Delete(context.Background(), "foo", kivik.Rev(rev)) - if err != nil { - t.Fatal(err) - } - }, - id: "foo", - wantStatus: http.StatusNotFound, - wantErr: "not found", + tests.Add("include conflicts", func(t *testing.T) interface{} { + db := newDB(t) + _, err := db.Put(context.Background(), "foo", map[string]string{"foo": "bar"}, kivik.Params(map[string]interface{}{ + "new_edits": false, + "rev": "1-abc", + })) + if err != nil { + t.Fatal(err) + } + _, err = db.Put(context.Background(), "foo", map[string]string{"foo": "baz"}, kivik.Params(map[string]interface{}{ + "new_edits": false, + "rev": "1-xyz", + })) + if err != nil { + t.Fatal(err) + } + + return test{ + db: db, + id: "foo", + options: kivik.Param("conflicts", true), + wantDoc: map[string]interface{}{ + "_id": "foo", + "_rev": "1-xyz", + "foo": "baz", + "_conflicts": []string{"1-abc"}, + }, + } }) - tests.Add("deleted document by rev", test{ - setup: func(t *testing.T, d driver.DB) { - rev, err := d.Put(context.Background(), "foo", map[string]string{"foo": "bar"}, mock.NilOption) - if err != nil { - t.Fatal(err) - } - _, err = d.Delete(context.Background(), "foo", kivik.Rev(rev)) - if err != nil { - t.Fatal(err) - } - }, - id: "foo", - options: kivik.Rev("2-df2a4fe30cde39c357c8d1105748d1b9"), - wantDoc: map[string]interface{}{ - "_id": "foo", - "_rev": "2-df2a4fe30cde39c357c8d1105748d1b9", - "_deleted": true, - }, + tests.Add("include only leaf conflicts", func(t *testing.T) interface{} { + db := newDB(t) + _, err := db.Put(context.Background(), "foo", map[string]string{"foo": "bar"}, kivik.Params(map[string]interface{}{ + "new_edits": false, + "rev": "1-abc", + })) + if err != nil { + t.Fatal(err) + } + _, err = db.Put(context.Background(), "foo", map[string]string{"foo": "baz"}, kivik.Params(map[string]interface{}{ + "new_edits": false, + "rev": "1-xyz", + })) + if err != nil { + t.Fatal(err) + } + rev, err := db.Put(context.Background(), "foo", map[string]string{"foo": "qux"}, kivik.Rev("1-xyz")) + if err != nil { + t.Fatal(err) + } + + return test{ + db: db, + id: "foo", + options: kivik.Param("conflicts", true), + wantDoc: map[string]interface{}{ + "_id": "foo", + "_rev": rev, + "foo": "qux", + "_conflicts": []string{"1-abc"}, + }, + } }) - tests.Add("deleted document with data by rev", test{ - setup: func(t *testing.T, d driver.DB) { - _, err := d.Put(context.Background(), "foo", map[string]interface{}{"_deleted": true, "foo": "bar"}, mock.NilOption) - if err != nil { - t.Fatal(err) - } - }, - id: "foo", - options: kivik.Rev("1-6872a0fc474ada5c46ce054b92897063"), - wantDoc: map[string]interface{}{ - "_id": "foo", - "_rev": "1-6872a0fc474ada5c46ce054b92897063", - "_deleted": true, - "foo": "bar", - }, + tests.Add("deleted document", func(t *testing.T) interface{} { + db := newDB(t) + rev, err := db.Put(context.Background(), "foo", map[string]string{"foo": "bar"}, mock.NilOption) + if err != nil { + t.Fatal(err) + } + _, err = db.Delete(context.Background(), "foo", kivik.Rev(rev)) + if err != nil { + t.Fatal(err) + } + + return test{ + db: db, + id: "foo", + wantStatus: http.StatusNotFound, + wantErr: "not found", + } }) - tests.Add("include conflicts, skip deleted conflicts", test{ - setup: func(t *testing.T, d driver.DB) { - _, err := d.Put(context.Background(), "foo", map[string]interface{}{"foo": "moo", "_deleted": true}, kivik.Params(map[string]interface{}{ - "new_edits": false, - "rev": "1-qwe", - })) - if err != nil { - t.Fatal(err) - } - _, err = d.Put(context.Background(), "foo", map[string]string{"foo": "bar"}, kivik.Params(map[string]interface{}{ - "new_edits": false, - "rev": "1-abc", - })) - if err != nil { - t.Fatal(err) - } - _, err = d.Put(context.Background(), "foo", map[string]string{"foo": "baz"}, kivik.Params(map[string]interface{}{ - "new_edits": false, - "rev": "1-xyz", - })) - if err != nil { - t.Fatal(err) - } - _, err = d.Put(context.Background(), "foo", map[string]string{"foo": "qux"}, kivik.Rev("1-xyz")) - if err != nil { - t.Fatal(err) - } - }, - id: "foo", - options: kivik.Param("conflicts", true), - wantDoc: map[string]interface{}{ - "_id": "foo", - "_rev": "2-8ecd3d54a4d763ebc0b6e6666d9af066", - "foo": "qux", - "_conflicts": []string{"1-abc"}, - }, + tests.Add("deleted document by rev", func(t *testing.T) interface{} { + db := newDB(t) + rev, err := db.Put(context.Background(), "foo", map[string]string{"foo": "bar"}, mock.NilOption) + if err != nil { + t.Fatal(err) + } + rev, err = db.Delete(context.Background(), "foo", kivik.Rev(rev)) + if err != nil { + t.Fatal(err) + } + + return test{ + db: db, + id: "foo", + options: kivik.Rev("2-df2a4fe30cde39c357c8d1105748d1b9"), + wantDoc: map[string]interface{}{ + "_id": "foo", + "_rev": rev, + "_deleted": true, + }, + } }) - tests.Add("include deleted conflicts", test{ - setup: func(t *testing.T, d driver.DB) { - _, err := d.Put(context.Background(), "foo", map[string]interface{}{"foo": "moo", "_deleted": true}, kivik.Params(map[string]interface{}{ - "new_edits": false, - "rev": "1-qwe", - })) - if err != nil { - t.Fatal(err) - } - _, err = d.Put(context.Background(), "foo", map[string]string{"foo": "bar"}, kivik.Params(map[string]interface{}{ - "new_edits": false, - "rev": "1-abc", - })) - if err != nil { - t.Fatal(err) - } - _, err = d.Put(context.Background(), "foo", map[string]string{"foo": "baz"}, kivik.Params(map[string]interface{}{ - "new_edits": false, - "rev": "1-xyz", - })) - if err != nil { - t.Fatal(err) - } - _, err = d.Put(context.Background(), "foo", map[string]string{"foo": "qux"}, kivik.Rev("1-xyz")) - if err != nil { - t.Fatal(err) - } - }, - id: "foo", - options: kivik.Param("deleted_conflicts", true), - wantDoc: map[string]interface{}{ - "_id": "foo", - "_rev": "2-8ecd3d54a4d763ebc0b6e6666d9af066", - "foo": "qux", - "_deleted_conflicts": []string{"1-qwe"}, - }, + tests.Add("deleted document with data by rev", func(t *testing.T) interface{} { + db := newDB(t) + rev, err := db.Put(context.Background(), "foo", map[string]interface{}{"_deleted": true, "foo": "bar"}, mock.NilOption) + if err != nil { + t.Fatal(err) + } + + return test{ + db: db, + id: "foo", + options: kivik.Rev("1-6872a0fc474ada5c46ce054b92897063"), + wantDoc: map[string]interface{}{ + "_id": "foo", + "_rev": rev, + "_deleted": true, + "foo": "bar", + }, + } }) - tests.Add("include all conflicts", test{ - setup: func(t *testing.T, d driver.DB) { - _, err := d.Put(context.Background(), "foo", map[string]interface{}{"foo": "moo", "_deleted": true}, kivik.Params(map[string]interface{}{ - "new_edits": false, - "rev": "1-qwe", - })) - if err != nil { - t.Fatal(err) - } - _, err = d.Put(context.Background(), "foo", map[string]string{"foo": "bar"}, kivik.Params(map[string]interface{}{ - "new_edits": false, - "rev": "1-abc", - })) - if err != nil { - t.Fatal(err) - } - _, err = d.Put(context.Background(), "foo", map[string]string{"foo": "baz"}, kivik.Params(map[string]interface{}{ - "new_edits": false, - "rev": "1-xyz", - })) - if err != nil { - t.Fatal(err) - } - _, err = d.Put(context.Background(), "foo", map[string]string{"foo": "qux"}, kivik.Rev("1-xyz")) - if err != nil { - t.Fatal(err) - } - }, - id: "foo", - options: kivik.Params(map[string]interface{}{ - "conflicts": true, - "deleted_conflicts": true, - }), - wantDoc: map[string]interface{}{ - "_id": "foo", - "_rev": "2-8ecd3d54a4d763ebc0b6e6666d9af066", - "foo": "qux", - "_deleted_conflicts": []string{"1-qwe"}, - "_conflicts": []string{"1-abc"}, - }, + tests.Add("include conflicts, skip deleted conflicts", func(t *testing.T) interface{} { + db := newDB(t) + _, err := db.Put(context.Background(), "foo", map[string]interface{}{"foo": "moo", "_deleted": true}, kivik.Params(map[string]interface{}{ + "new_edits": false, + "rev": "1-qwe", + })) + if err != nil { + t.Fatal(err) + } + _, err = db.Put(context.Background(), "foo", map[string]string{"foo": "bar"}, kivik.Params(map[string]interface{}{ + "new_edits": false, + "rev": "1-abc", + })) + if err != nil { + t.Fatal(err) + } + _, err = db.Put(context.Background(), "foo", map[string]string{"foo": "baz"}, kivik.Params(map[string]interface{}{ + "new_edits": false, + "rev": "1-xyz", + })) + if err != nil { + t.Fatal(err) + } + rev, err := db.Put(context.Background(), "foo", map[string]string{"foo": "qux"}, kivik.Rev("1-xyz")) + if err != nil { + t.Fatal(err) + } + + return test{ + db: db, + id: "foo", + options: kivik.Param("conflicts", true), + wantDoc: map[string]interface{}{ + "_id": "foo", + "_rev": rev, + "foo": "qux", + "_conflicts": []string{"1-abc"}, + }, + } }) - tests.Add("include revs_info", test{ - setup: func(t *testing.T, d driver.DB) { - _, err := d.Put(context.Background(), "foo", map[string]interface{}{"foo": "moo", "_deleted": true}, kivik.Params(map[string]interface{}{ - "new_edits": false, - "rev": "1-qwe", - })) - if err != nil { - t.Fatal(err) - } - _, err = d.Put(context.Background(), "foo", map[string]string{"foo": "bar"}, kivik.Params(map[string]interface{}{ - "new_edits": false, - "rev": "1-abc", - })) - if err != nil { - t.Fatal(err) - } - _, err = d.Put(context.Background(), "foo", map[string]string{"foo": "baz"}, kivik.Params(map[string]interface{}{ - "new_edits": false, - "rev": "1-xyz", - })) - if err != nil { - t.Fatal(err) - } - _, err = d.Put(context.Background(), "foo", map[string]string{"foo": "qux"}, kivik.Rev("1-xyz")) - if err != nil { - t.Fatal(err) - } - }, - id: "foo", - options: kivik.Params(map[string]interface{}{ - "revs_info": true, - }), - wantDoc: map[string]interface{}{ - "_id": "foo", - "_rev": "2-8ecd3d54a4d763ebc0b6e6666d9af066", - "foo": "qux", - "_revs_info": []map[string]string{ - {"rev": "2-8ecd3d54a4d763ebc0b6e6666d9af066", "status": "available"}, - {"rev": "1-xyz", "status": "available"}, + tests.Add("include deleted conflicts", func(t *testing.T) interface{} { + db := newDB(t) + _, err := db.Put(context.Background(), "foo", map[string]interface{}{"foo": "moo", "_deleted": true}, kivik.Params(map[string]interface{}{ + "new_edits": false, + "rev": "1-qwe", + })) + if err != nil { + t.Fatal(err) + } + _, err = db.Put(context.Background(), "foo", map[string]string{"foo": "bar"}, kivik.Params(map[string]interface{}{ + "new_edits": false, + "rev": "1-abc", + })) + if err != nil { + t.Fatal(err) + } + _, err = db.Put(context.Background(), "foo", map[string]string{"foo": "baz"}, kivik.Params(map[string]interface{}{ + "new_edits": false, + "rev": "1-xyz", + })) + if err != nil { + t.Fatal(err) + } + rev, err := db.Put(context.Background(), "foo", map[string]string{"foo": "qux"}, kivik.Rev("1-xyz")) + if err != nil { + t.Fatal(err) + } + + return test{ + db: db, + id: "foo", + options: kivik.Param("deleted_conflicts", true), + wantDoc: map[string]interface{}{ + "_id": "foo", + "_rev": rev, + "foo": "qux", + "_deleted_conflicts": []string{"1-qwe"}, }, - }, + } }) - tests.Add("include meta", test{ - setup: func(t *testing.T, d driver.DB) { - _, err := d.Put(context.Background(), "foo", map[string]interface{}{"foo": "moo", "_deleted": true}, kivik.Params(map[string]interface{}{ - "new_edits": false, - "rev": "1-qwe", - })) - if err != nil { - t.Fatal(err) - } - _, err = d.Put(context.Background(), "foo", map[string]string{"foo": "bar"}, kivik.Params(map[string]interface{}{ - "new_edits": false, - "rev": "1-abc", - })) - if err != nil { - t.Fatal(err) - } - _, err = d.Put(context.Background(), "foo", map[string]string{"foo": "baz"}, kivik.Params(map[string]interface{}{ - "new_edits": false, - "rev": "1-xyz", - })) - if err != nil { - t.Fatal(err) - } - _, err = d.Put(context.Background(), "foo", map[string]string{"foo": "qux"}, kivik.Rev("1-xyz")) - if err != nil { - t.Fatal(err) - } - }, - id: "foo", - options: kivik.Param("meta", true), - wantDoc: map[string]interface{}{ - "_id": "foo", - "_rev": "2-8ecd3d54a4d763ebc0b6e6666d9af066", - "foo": "qux", - "_revs_info": []map[string]string{ - {"rev": "2-8ecd3d54a4d763ebc0b6e6666d9af066", "status": "available"}, - {"rev": "1-xyz", "status": "available"}, + tests.Add("include all conflicts", func(t *testing.T) interface{} { + db := newDB(t) + _, err := db.Put(context.Background(), "foo", map[string]interface{}{"foo": "moo", "_deleted": true}, kivik.Params(map[string]interface{}{ + "new_edits": false, + "rev": "1-qwe", + })) + if err != nil { + t.Fatal(err) + } + _, err = db.Put(context.Background(), "foo", map[string]string{"foo": "bar"}, kivik.Params(map[string]interface{}{ + "new_edits": false, + "rev": "1-abc", + })) + if err != nil { + t.Fatal(err) + } + _, err = db.Put(context.Background(), "foo", map[string]string{"foo": "baz"}, kivik.Params(map[string]interface{}{ + "new_edits": false, + "rev": "1-xyz", + })) + if err != nil { + t.Fatal(err) + } + rev, err := db.Put(context.Background(), "foo", map[string]string{"foo": "qux"}, kivik.Rev("1-xyz")) + if err != nil { + t.Fatal(err) + } + + return test{ + db: db, + id: "foo", + options: kivik.Params(map[string]interface{}{ + "conflicts": true, + "deleted_conflicts": true, + }), + wantDoc: map[string]interface{}{ + "_id": "foo", + "_rev": rev, + "foo": "qux", + "_deleted_conflicts": []string{"1-qwe"}, + "_conflicts": []string{"1-abc"}, }, - "_conflicts": []string{"1-abc"}, - "_deleted_conflicts": []string{"1-qwe"}, - }, + } }) - tests.Add("get latest winning leaf", test{ - setup: func(t *testing.T, d driver.DB) { - _, err := d.Put(context.Background(), "foo", map[string]interface{}{"foo": "aaa", "_rev": "1-aaa"}, kivik.Params(map[string]interface{}{ - "new_edits": false, - })) - if err != nil { - t.Fatal(err) - } - _, err = d.Put(context.Background(), "foo", map[string]interface{}{ - "foo": "bbb", - "_revisions": map[string]interface{}{ - "ids": []string{"bbb", "aaa"}, - "start": 2, - }, - }, kivik.Params(map[string]interface{}{ - "new_edits": false, - })) - if err != nil { - t.Fatal(err) - } - _, err = d.Put(context.Background(), "foo", map[string]interface{}{ - "foo": "ddd", - "_revisions": map[string]interface{}{ - "ids": []string{"yyy", "aaa"}, - "start": 2, + tests.Add("include revs_info", func(t *testing.T) interface{} { + db := newDB(t) + _, err := db.Put(context.Background(), "foo", map[string]interface{}{"foo": "moo", "_deleted": true}, kivik.Params(map[string]interface{}{ + "new_edits": false, + "rev": "1-qwe", + })) + if err != nil { + t.Fatal(err) + } + _, err = db.Put(context.Background(), "foo", map[string]string{"foo": "bar"}, kivik.Params(map[string]interface{}{ + "new_edits": false, + "rev": "1-abc", + })) + if err != nil { + t.Fatal(err) + } + _, err = db.Put(context.Background(), "foo", map[string]string{"foo": "baz"}, kivik.Params(map[string]interface{}{ + "new_edits": false, + "rev": "1-xyz", + })) + if err != nil { + t.Fatal(err) + } + rev, err := db.Put(context.Background(), "foo", map[string]string{"foo": "qux"}, kivik.Rev("1-xyz")) + if err != nil { + t.Fatal(err) + } + + return test{ + db: db, + id: "foo", + options: kivik.Params(map[string]interface{}{ + "revs_info": true, + }), + wantDoc: map[string]interface{}{ + "_id": "foo", + "_rev": rev, + "foo": "qux", + "_revs_info": []map[string]string{ + {"rev": rev, "status": "available"}, + {"rev": "1-xyz", "status": "available"}, }, - }, kivik.Params(map[string]interface{}{ - "new_edits": false, - })) - if err != nil { - t.Fatal(err) - } - }, - id: "foo", - options: kivik.Params(map[string]interface{}{ - "latest": true, - "rev": "1-aaa", - }), - wantDoc: map[string]interface{}{ - "_id": "foo", - "_rev": "2-yyy", - "foo": "ddd", - }, + }, + } }) - tests.Add("get latest non-winning leaf", test{ - setup: func(t *testing.T, d driver.DB) { - // common root doc - _, err := d.Put(context.Background(), "foo", map[string]interface{}{"foo": "aaa", "_rev": "1-aaa"}, kivik.Params(map[string]interface{}{ - "new_edits": false, - })) - if err != nil { - t.Fatal(err) - } - // losing branch - _, err = d.Put(context.Background(), "foo", map[string]interface{}{ - "foo": "bbb", - "_revisions": map[string]interface{}{ - "ids": []string{"ccc", "bbb", "aaa"}, - "start": 3, - }, - }, kivik.Params(map[string]interface{}{ - "new_edits": false, - })) - if err != nil { - t.Fatal(err) - } + tests.Add("include meta", func(t *testing.T) interface{} { + db := newDB(t) + _, err := db.Put(context.Background(), "foo", map[string]interface{}{"foo": "moo", "_deleted": true}, kivik.Params(map[string]interface{}{ + "new_edits": false, + "rev": "1-qwe", + })) + if err != nil { + t.Fatal(err) + } + _, err = db.Put(context.Background(), "foo", map[string]string{"foo": "bar"}, kivik.Params(map[string]interface{}{ + "new_edits": false, + "rev": "1-abc", + })) + if err != nil { + t.Fatal(err) + } + _, err = db.Put(context.Background(), "foo", map[string]string{"foo": "baz"}, kivik.Params(map[string]interface{}{ + "new_edits": false, + "rev": "1-xyz", + })) + if err != nil { + t.Fatal(err) + } + rev, err := db.Put(context.Background(), "foo", map[string]string{"foo": "qux"}, kivik.Rev("1-xyz")) + if err != nil { + t.Fatal(err) + } - // winning branch - _, err = d.Put(context.Background(), "foo", map[string]interface{}{ - "foo": "ddd", - "_revisions": map[string]interface{}{ - "ids": []string{"xxx", "yyy", "aaa"}, - "start": 3, + return test{ + db: db, + id: "foo", + options: kivik.Param("meta", true), + wantDoc: map[string]interface{}{ + "_id": "foo", + "_rev": rev, + "foo": "qux", + "_revs_info": []map[string]string{ + {"rev": rev, "status": "available"}, + {"rev": "1-xyz", "status": "available"}, }, - }, kivik.Params(map[string]interface{}{ - "new_edits": false, - })) - if err != nil { - t.Fatal(err) - } - }, - id: "foo", - options: kivik.Params(map[string]interface{}{ - "latest": true, - "rev": "2-bbb", - }), - wantDoc: map[string]interface{}{ - "_id": "foo", - "_rev": "3-ccc", - "foo": "bbb", - }, + "_conflicts": []string{"1-abc"}, + "_deleted_conflicts": []string{"1-qwe"}, + }, + } }) - tests.Add("get latest rev with deleted leaf, reverts to the winning branch", test{ - setup: func(t *testing.T, d driver.DB) { - // common root doc - _, err := d.Put(context.Background(), "foo", map[string]interface{}{"foo": "aaa", "_rev": "1-aaa"}, kivik.Params(map[string]interface{}{ - "new_edits": false, - })) - if err != nil { - t.Fatal(err) - } - // losing branch - _, err = d.Put(context.Background(), "foo", map[string]interface{}{ - "foo": "bbb", - "_revisions": map[string]interface{}{ - "ids": []string{"ccc", "bbb", "aaa"}, - "start": 3, - }, - }, kivik.Params(map[string]interface{}{ - "new_edits": false, - })) - if err != nil { - t.Fatal(err) - } - // now delete the losing leaf - _, err = d.Delete(context.Background(), "foo", kivik.Rev("3-ccc")) - if err != nil { - t.Fatal(err) - } + tests.Add("get latest winning leaf", func(t *testing.T) interface{} { + db := newDB(t) + _, err := db.Put(context.Background(), "foo", map[string]interface{}{"foo": "aaa", "_rev": "1-aaa"}, kivik.Params(map[string]interface{}{ + "new_edits": false, + })) + if err != nil { + t.Fatal(err) + } + _, err = db.Put(context.Background(), "foo", map[string]interface{}{ + "foo": "bbb", + "_revisions": map[string]interface{}{ + "ids": []string{"bbb", "aaa"}, + "start": 2, + }, + }, kivik.Params(map[string]interface{}{ + "new_edits": false, + })) + if err != nil { + t.Fatal(err) + } + _, err = db.Put(context.Background(), "foo", map[string]interface{}{ + "foo": "ddd", + "_revisions": map[string]interface{}{ + "ids": []string{"yyy", "aaa"}, + "start": 2, + }, + }, kivik.Params(map[string]interface{}{ + "new_edits": false, + })) + if err != nil { + t.Fatal(err) + } - // winning branch - _, err = d.Put(context.Background(), "foo", map[string]interface{}{ - "foo": "ddd", - "_revisions": map[string]interface{}{ - "ids": []string{"xxx", "yyy", "aaa"}, - "start": 3, - }, - }, kivik.Params(map[string]interface{}{ - "new_edits": false, - })) - if err != nil { - t.Fatal(err) - } - }, - id: "foo", - options: kivik.Params(map[string]interface{}{ - "latest": true, - "rev": "2-bbb", - }), - wantDoc: map[string]interface{}{ - "_id": "foo", - "_rev": "3-xxx", - "foo": "ddd", - }, + return test{ + db: db, + id: "foo", + options: kivik.Params(map[string]interface{}{ + "latest": true, + "rev": "1-aaa", + }), + wantDoc: map[string]interface{}{ + "_id": "foo", + "_rev": "2-yyy", + "foo": "ddd", + }, + } }) - tests.Add("revs=true, losing leaf", test{ - setup: func(t *testing.T, d driver.DB) { - _, err := d.Put(context.Background(), "foo", map[string]interface{}{"foo": "aaa", "_rev": "1-aaa"}, kivik.Params(map[string]interface{}{ - "new_edits": false, - })) - if err != nil { - t.Fatal(err) - } - _, err = d.Put(context.Background(), "foo", map[string]interface{}{ - "foo": "bbb", - "_revisions": map[string]interface{}{ - "ids": []string{"bbb", "aaa"}, - "start": 2, - }, - }, kivik.Params(map[string]interface{}{ - "new_edits": false, - })) - if err != nil { - t.Fatal(err) - } - _, err = d.Put(context.Background(), "foo", map[string]interface{}{ - "foo": "ddd", + tests.Add("get latest non-winning leaf", func(t *testing.T) interface{} { + db := newDB(t) + // common root doc + _, err := db.Put(context.Background(), "foo", map[string]interface{}{"foo": "aaa", "_rev": "1-aaa"}, kivik.Params(map[string]interface{}{ + "new_edits": false, + })) + if err != nil { + t.Fatal(err) + } + // losing branch + _, err = db.Put(context.Background(), "foo", map[string]interface{}{ + "foo": "bbb", + "_revisions": map[string]interface{}{ + "ids": []string{"ccc", "bbb", "aaa"}, + "start": 3, + }, + }, kivik.Params(map[string]interface{}{ + "new_edits": false, + })) + if err != nil { + t.Fatal(err) + } + + // winning branch + _, err = db.Put(context.Background(), "foo", map[string]interface{}{ + "foo": "ddd", + "_revisions": map[string]interface{}{ + "ids": []string{"xxx", "yyy", "aaa"}, + "start": 3, + }, + }, kivik.Params(map[string]interface{}{ + "new_edits": false, + })) + if err != nil { + t.Fatal(err) + } + + return test{ + db: db, + id: "foo", + options: kivik.Params(map[string]interface{}{ + "latest": true, + "rev": "2-bbb", + }), + wantDoc: map[string]interface{}{ + "_id": "foo", + "_rev": "3-ccc", + "foo": "bbb", + }, + } + }) + tests.Add("get latest rev with deleted leaf, reverts to the winning branch", func(t *testing.T) interface{} { + db := newDB(t) + // common root doc + _, err := db.Put(context.Background(), "foo", map[string]interface{}{"foo": "aaa", "_rev": "1-aaa"}, kivik.Params(map[string]interface{}{ + "new_edits": false, + })) + if err != nil { + t.Fatal(err) + } + // losing branch + _, err = db.Put(context.Background(), "foo", map[string]interface{}{ + "foo": "bbb", + "_revisions": map[string]interface{}{ + "ids": []string{"ccc", "bbb", "aaa"}, + "start": 3, + }, + }, kivik.Params(map[string]interface{}{ + "new_edits": false, + })) + if err != nil { + t.Fatal(err) + } + // now delete the losing leaf + _, err = db.Delete(context.Background(), "foo", kivik.Rev("3-ccc")) + if err != nil { + t.Fatal(err) + } + + // winning branch + _, err = db.Put(context.Background(), "foo", map[string]interface{}{ + "foo": "ddd", + "_revisions": map[string]interface{}{ + "ids": []string{"xxx", "yyy", "aaa"}, + "start": 3, + }, + }, kivik.Params(map[string]interface{}{ + "new_edits": false, + })) + if err != nil { + t.Fatal(err) + } + + return test{ + db: db, + id: "foo", + options: kivik.Params(map[string]interface{}{ + "latest": true, + "rev": "2-bbb", + }), + wantDoc: map[string]interface{}{ + "_id": "foo", + "_rev": "3-xxx", + "foo": "ddd", + }, + } + }) + tests.Add("revs=true, losing leaf", func(t *testing.T) interface{} { + db := newDB(t) + _, err := db.Put(context.Background(), "foo", map[string]interface{}{"foo": "aaa", "_rev": "1-aaa"}, kivik.Params(map[string]interface{}{ + "new_edits": false, + })) + if err != nil { + t.Fatal(err) + } + _, err = db.Put(context.Background(), "foo", map[string]interface{}{ + "foo": "bbb", + "_revisions": map[string]interface{}{ + "ids": []string{"bbb", "aaa"}, + "start": 2, + }, + }, kivik.Params(map[string]interface{}{ + "new_edits": false, + })) + if err != nil { + t.Fatal(err) + } + _, err = db.Put(context.Background(), "foo", map[string]interface{}{ + "foo": "ddd", + "_revisions": map[string]interface{}{ + "ids": []string{"yyy", "aaa"}, + "start": 2, + }, + }, kivik.Params(map[string]interface{}{ + "new_edits": false, + })) + if err != nil { + t.Fatal(err) + } + + return test{ + db: db, + id: "foo", + options: kivik.Params(map[string]interface{}{ + "revs": true, + "rev": "2-bbb", + }), + wantDoc: map[string]interface{}{ + "_id": "foo", + "_rev": "2-bbb", + "foo": "bbb", "_revisions": map[string]interface{}{ - "ids": []string{"yyy", "aaa"}, "start": 2, + "ids": []string{"bbb", "aaa"}, }, - }, kivik.Params(map[string]interface{}{ - "new_edits": false, - })) - if err != nil { - t.Fatal(err) - } - }, - id: "foo", - options: kivik.Params(map[string]interface{}{ - "revs": true, - "rev": "2-bbb", - }), - wantDoc: map[string]interface{}{ - "_id": "foo", - "_rev": "2-bbb", - "foo": "bbb", - "_revisions": map[string]interface{}{ - "start": 2, - "ids": []string{"bbb", "aaa"}, }, - }, + } }) - tests.Add("local_seq=true", test{ - setup: func(t *testing.T, d driver.DB) { - _, err := d.Put(context.Background(), "foo", map[string]interface{}{"foo": "aaa", "_rev": "1-aaa"}, kivik.Params(map[string]interface{}{ - "new_edits": false, - })) - if err != nil { - t.Fatal(err) - } - }, - id: "foo", - options: kivik.Param("local_seq", true), - wantDoc: map[string]interface{}{ - "_id": "foo", - "_rev": "1-aaa", - "foo": "aaa", - "_local_seq": float64(1), - }, + tests.Add("local_seq=true", func(t *testing.T) interface{} { + db := newDB(t) + _, err := db.Put(context.Background(), "foo", map[string]interface{}{"foo": "aaa", "_rev": "1-aaa"}, kivik.Params(map[string]interface{}{ + "new_edits": false, + })) + if err != nil { + t.Fatal(err) + } + + return test{ + db: db, + id: "foo", + options: kivik.Param("local_seq", true), + wantDoc: map[string]interface{}{ + "_id": "foo", + "_rev": "1-aaa", + "foo": "aaa", + "_local_seq": float64(1), + }, + } }) - tests.Add("local_seq=true & specified rev", test{ - setup: func(t *testing.T, d driver.DB) { - _, err := d.Put(context.Background(), "foo", map[string]interface{}{"foo": "aaa", "_rev": "1-aaa"}, kivik.Params(map[string]interface{}{ - "new_edits": false, - })) - if err != nil { - t.Fatal(err) - } - }, - id: "foo", - options: kivik.Params(map[string]interface{}{"local_seq": true, "rev": "1-aaa"}), - wantDoc: map[string]interface{}{ - "_id": "foo", - "_rev": "1-aaa", - "foo": "aaa", - "_local_seq": float64(1), - }, + tests.Add("local_seq=true & specified rev", func(t *testing.T) interface{} { + db := newDB(t) + _, err := db.Put(context.Background(), "foo", map[string]interface{}{"foo": "aaa", "_rev": "1-aaa"}, kivik.Params(map[string]interface{}{ + "new_edits": false, + })) + if err != nil { + t.Fatal(err) + } + + return test{ + db: db, + id: "foo", + options: kivik.Params(map[string]interface{}{"local_seq": true, "rev": "1-aaa"}), + wantDoc: map[string]interface{}{ + "_id": "foo", + "_rev": "1-aaa", + "foo": "aaa", + "_local_seq": float64(1), + }, + } }) - tests.Add("local_seq=true & specified rev & latest=true", test{ - setup: func(t *testing.T, d driver.DB) { - _, err := d.Put(context.Background(), "foo", map[string]interface{}{"foo": "aaa", "_rev": "1-aaa"}, kivik.Params(map[string]interface{}{ - "new_edits": false, - })) - if err != nil { - t.Fatal(err) - } - }, - id: "foo", - options: kivik.Params(map[string]interface{}{ - "local_seq": true, - "rev": "1-aaa", - "latest": true, - }), - wantDoc: map[string]interface{}{ - "_id": "foo", - "_rev": "1-aaa", - "foo": "aaa", - "_local_seq": float64(1), - }, + tests.Add("local_seq=true & specified rev & latest=true", func(t *testing.T) interface{} { + db := newDB(t) + _, err := db.Put(context.Background(), "foo", map[string]interface{}{"foo": "aaa", "_rev": "1-aaa"}, kivik.Params(map[string]interface{}{ + "new_edits": false, + })) + if err != nil { + t.Fatal(err) + } + + return test{ + db: db, + id: "foo", + options: kivik.Params(map[string]interface{}{ + "local_seq": true, + "rev": "1-aaa", + "latest": true, + }), + wantDoc: map[string]interface{}{ + "_id": "foo", + "_rev": "1-aaa", + "foo": "aaa", + "_local_seq": float64(1), + }, + } }) - tests.Add("attachments=false, doc with attachments", test{ - setup: func(t *testing.T, d driver.DB) { - _, err := d.Put(context.Background(), "foo", map[string]interface{}{ - "foo": "aaa", + tests.Add("attachments=false, doc with attachments", func(t *testing.T) interface{} { + db := newDB(t) + rev, err := db.Put(context.Background(), "foo", map[string]interface{}{ + "foo": "aaa", + "_attachments": map[string]interface{}{ + "att.txt": map[string]interface{}{ + "content_type": "text/plain", + "data": "YXR0LnR4dA==", + }, + }, + }, mock.NilOption) + if err != nil { + t.Fatal(err) + } + + return test{ + db: db, + id: "foo", + options: kivik.Param("attachments", false), + wantDoc: map[string]interface{}{ + "_id": "foo", + "_rev": rev, + "foo": "aaa", "_attachments": map[string]interface{}{ "att.txt": map[string]interface{}{ "content_type": "text/plain", - "data": "YXR0LnR4dA==", + "digest": "md5-a4NyknGw7YOh+a5ezPdZ4A==", + "length": float64(7), + "revpos": float64(1), + "stub": true, }, }, - }, mock.NilOption) - if err != nil { - t.Fatal(err) - } - }, - id: "foo", - options: kivik.Param("attachments", false), - wantDoc: map[string]interface{}{ - "_id": "foo", - "_rev": "1-5f3e7150f872a1dd295f44b1e4a9fa41", - "foo": "aaa", + }, + } + }) + tests.Add("attachments=true, doc with attachments", func(t *testing.T) interface{} { + db := newDB(t) + rev, err := db.Put(context.Background(), "foo", map[string]interface{}{ + "foo": "aaa", "_attachments": map[string]interface{}{ "att.txt": map[string]interface{}{ "content_type": "text/plain", - "digest": "md5-a4NyknGw7YOh+a5ezPdZ4A==", - "length": float64(7), - "revpos": float64(1), - "stub": true, + "data": "YXR0LnR4dA==", }, }, - }, - }) - tests.Add("attachments=true, doc with attachments", test{ - setup: func(t *testing.T, d driver.DB) { - _, err := d.Put(context.Background(), "foo", map[string]interface{}{ - "foo": "aaa", + }, mock.NilOption) + if err != nil { + t.Fatal(err) + } + + return test{ + db: db, + id: "foo", + options: kivik.Param("attachments", true), + wantDoc: map[string]interface{}{ + "_id": "foo", + "_rev": rev, + "foo": "aaa", "_attachments": map[string]interface{}{ "att.txt": map[string]interface{}{ "content_type": "text/plain", + "digest": "md5-a4NyknGw7YOh+a5ezPdZ4A==", + "length": float64(7), + "revpos": float64(1), "data": "YXR0LnR4dA==", }, }, - }, mock.NilOption) - if err != nil { - t.Fatal(err) - } - }, - id: "foo", - options: kivik.Param("attachments", true), - wantDoc: map[string]interface{}{ - "_id": "foo", - "_rev": "1-5f3e7150f872a1dd295f44b1e4a9fa41", - "foo": "aaa", + }, + } + }) + tests.Add("attachments=true, doc without attachments", func(t *testing.T) interface{} { + db := newDB(t) + rev, err := db.Put(context.Background(), "foo", map[string]interface{}{ + "foo": "aaa", + }, mock.NilOption) + if err != nil { + t.Fatal(err) + } + + return test{ + db: db, + id: "foo", + options: kivik.Param("attachments", true), + wantDoc: map[string]interface{}{ + "_id": "foo", + "_rev": rev, + "foo": "aaa", + }, + } + }) + tests.Add("attachments=false, do not return deleted attachments", func(t *testing.T) interface{} { + db := newDB(t) + rev1, err := db.Put(context.Background(), "foo", map[string]interface{}{ + "foo": "aaa", "_attachments": map[string]interface{}{ "att.txt": map[string]interface{}{ "content_type": "text/plain", - "digest": "md5-a4NyknGw7YOh+a5ezPdZ4A==", - "length": float64(7), - "revpos": float64(1), + "data": "YXR0LnR4dA==", + }, + "att2.txt": map[string]interface{}{ + "content_type": "text/plain", "data": "YXR0LnR4dA==", }, }, - }, - }) - tests.Add("attachments=true, doc without attachments", test{ - setup: func(t *testing.T, d driver.DB) { - _, err := d.Put(context.Background(), "foo", map[string]interface{}{ - "foo": "aaa", - }, mock.NilOption) - if err != nil { - t.Fatal(err) - } - }, - id: "foo", - options: kivik.Param("attachments", true), - wantDoc: map[string]interface{}{ - "_id": "foo", - "_rev": "1-8655eafbc9513d4857258c6d48f40399", - "foo": "aaa", - }, - }) - tests.Add("attachments=false, do not return deleted attachments", test{ - setup: func(t *testing.T, d driver.DB) { - _, err := d.Put(context.Background(), "foo", map[string]interface{}{ - "foo": "aaa", + }, mock.NilOption) + if err != nil { + t.Fatal(err) + } + rev2, err := db.Put(context.Background(), "foo", map[string]interface{}{ + "foo": "aaa", + "_attachments": map[string]interface{}{ + "att.txt": map[string]interface{}{ + "stub": true, + }, + }, + }, kivik.Rev(rev1)) + if err != nil { + t.Fatal(err) + } + + return test{ + db: db, + id: "foo", + options: kivik.Param("attachments", false), + wantDoc: map[string]interface{}{ + "_id": "foo", + "_rev": rev2, + "foo": "aaa", "_attachments": map[string]interface{}{ "att.txt": map[string]interface{}{ "content_type": "text/plain", - "data": "YXR0LnR4dA==", - }, - "att2.txt": map[string]interface{}{ - "content_type": "text/plain", - "data": "YXR0LnR4dA==", + "digest": "md5-a4NyknGw7YOh+a5ezPdZ4A==", + "revpos": float64(1), + "length": float64(7), + "stub": true, }, }, - }, mock.NilOption) - if err != nil { - t.Fatal(err) - } - _, err = d.Put(context.Background(), "foo", map[string]interface{}{ - "foo": "aaa", - "_attachments": map[string]interface{}{ - "att.txt": map[string]interface{}{ - "stub": true, - }, + }, + } + }) + tests.Add("attachments=false, fetch atts added at different revs", func(t *testing.T) interface{} { + db := newDB(t) + rev1, err := db.Put(context.Background(), "foo", map[string]interface{}{ + "foo": "aaa", + "_attachments": map[string]interface{}{ + "att.txt": map[string]interface{}{ + "content_type": "text/plain", + "data": "YXR0LnR4dA==", }, - }, kivik.Rev("1-a4791acb8fcc7d205b4c582e0c9e3dc0")) - if err != nil { - t.Fatal(err) - } - }, - id: "foo", - options: kivik.Param("attachments", false), - wantDoc: map[string]interface{}{ - "_id": "foo", - "_rev": "2-683854351c2ead3ccc353da6980070c4", - "foo": "aaa", + }, + }, mock.NilOption) + if err != nil { + t.Fatal(err) + } + rev2, err := db.Put(context.Background(), "foo", map[string]interface{}{ + "foo": "aaa", "_attachments": map[string]interface{}{ "att.txt": map[string]interface{}{ + "stub": true, + }, + "att2.txt": map[string]interface{}{ "content_type": "text/plain", - "digest": "md5-a4NyknGw7YOh+a5ezPdZ4A==", - "revpos": float64(1), - "length": float64(7), - "stub": true, + "data": "YXR0LnR4dA==", }, }, - }, - }) - tests.Add("attachments=false, fetch atts added at different revs", test{ - setup: func(t *testing.T, d driver.DB) { - _, err := d.Put(context.Background(), "foo", map[string]interface{}{ - "foo": "aaa", + }, kivik.Rev(rev1)) + if err != nil { + t.Fatal(err) + } + + return test{ + db: db, + id: "foo", + options: kivik.Param("attachments", false), + wantDoc: map[string]interface{}{ + "_id": "foo", + "_rev": rev2, + "foo": "aaa", "_attachments": map[string]interface{}{ "att.txt": map[string]interface{}{ "content_type": "text/plain", - "data": "YXR0LnR4dA==", - }, - }, - }, mock.NilOption) - if err != nil { - t.Fatal(err) - } - _, err = d.Put(context.Background(), "foo", map[string]interface{}{ - "foo": "aaa", - "_attachments": map[string]interface{}{ - "att.txt": map[string]interface{}{ - "stub": true, + "digest": "md5-a4NyknGw7YOh+a5ezPdZ4A==", + "revpos": float64(1), + "length": float64(7), + "stub": true, }, "att2.txt": map[string]interface{}{ "content_type": "text/plain", - "data": "YXR0LnR4dA==", + "digest": "md5-a4NyknGw7YOh+a5ezPdZ4A==", + "revpos": float64(2), + "length": float64(7), + "stub": true, }, }, - }, kivik.Rev("1-5f3e7150f872a1dd295f44b1e4a9fa41")) - if err != nil { - t.Fatal(err) - } - }, - id: "foo", - options: kivik.Param("attachments", false), - wantDoc: map[string]interface{}{ - "_id": "foo", - "_rev": "2-9dc3adaa7b08ac0ae246cded87669883", - "foo": "aaa", + }, + } + }) + tests.Add("attachments=false, fetch only atts that existed at time of specific rev", func(t *testing.T) interface{} { + db := newDB(t) + rev1, err := db.Put(context.Background(), "foo", map[string]interface{}{ + "foo": "aaa", "_attachments": map[string]interface{}{ "att.txt": map[string]interface{}{ "content_type": "text/plain", - "digest": "md5-a4NyknGw7YOh+a5ezPdZ4A==", - "revpos": float64(1), - "length": float64(7), - "stub": true, + "data": "YXR0LnR4dA==", + }, + }, + }, mock.NilOption) + if err != nil { + t.Fatal(err) + } + _, err = db.Put(context.Background(), "foo", map[string]interface{}{ + "foo": "aaa", + "_attachments": map[string]interface{}{ + "att.txt": map[string]interface{}{ + "stub": true, }, "att2.txt": map[string]interface{}{ "content_type": "text/plain", - "digest": "md5-a4NyknGw7YOh+a5ezPdZ4A==", - "revpos": float64(2), - "length": float64(7), - "stub": true, + "data": "YXR0LnR4dA==", }, }, - }, - }) - tests.Add("attachments=false, fetch only atts that existed at time of specific rev", test{ - setup: func(t *testing.T, d driver.DB) { - _, err := d.Put(context.Background(), "foo", map[string]interface{}{ - "foo": "aaa", - "_attachments": map[string]interface{}{ - "att.txt": map[string]interface{}{ - "content_type": "text/plain", - "data": "YXR0LnR4dA==", - }, - }, - }, mock.NilOption) - if err != nil { - t.Fatal(err) - } - _, err = d.Put(context.Background(), "foo", map[string]interface{}{ - "foo": "aaa", + }, kivik.Rev(rev1)) + if err != nil { + t.Fatal(err) + } + + return test{ + db: db, + id: "foo", + options: kivik.Params(map[string]interface{}{ + "attachments": false, + "rev": rev1, + }), + wantDoc: map[string]interface{}{ + "_id": "foo", + "_rev": rev1, + "foo": "aaa", "_attachments": map[string]interface{}{ "att.txt": map[string]interface{}{ - "stub": true, - }, - "att2.txt": map[string]interface{}{ "content_type": "text/plain", - "data": "YXR0LnR4dA==", + "digest": "md5-a4NyknGw7YOh+a5ezPdZ4A==", + "revpos": float64(1), + "length": float64(7), + "stub": true, }, }, - }, kivik.Rev("1-5f3e7150f872a1dd295f44b1e4a9fa41")) - if err != nil { - t.Fatal(err) - } - }, - id: "foo", - options: kivik.Params(map[string]interface{}{ - "attachments": false, - "rev": "1-5f3e7150f872a1dd295f44b1e4a9fa41", - }), - wantDoc: map[string]interface{}{ - "_id": "foo", - "_rev": "1-5f3e7150f872a1dd295f44b1e4a9fa41", - "foo": "aaa", + }, + } + }) + tests.Add("attachments=false, fetch updated attachment", func(t *testing.T) interface{} { + db := newDB(t) + rev1, err := db.Put(context.Background(), "foo", map[string]interface{}{ + "foo": "aaa", "_attachments": map[string]interface{}{ "att.txt": map[string]interface{}{ "content_type": "text/plain", - "digest": "md5-a4NyknGw7YOh+a5ezPdZ4A==", - "revpos": float64(1), - "length": float64(7), - "stub": true, + "data": "YXR0LnR4dA==", }, }, - }, - }) - tests.Add("attachments=false, fetch updated attachment", test{ - setup: func(t *testing.T, d driver.DB) { - _, err := d.Put(context.Background(), "foo", map[string]interface{}{ - "foo": "aaa", - "_attachments": map[string]interface{}{ - "att.txt": map[string]interface{}{ - "content_type": "text/plain", - "data": "YXR0LnR4dA==", - }, + }, mock.NilOption) + if err != nil { + t.Fatal(err) + } + rev2, err := db.Put(context.Background(), "foo", map[string]interface{}{ + "foo": "aaa", + "_attachments": map[string]interface{}{ + "att.txt": map[string]interface{}{ + "data": "dmVyc2lvbiAyCg==", }, - }, mock.NilOption) - if err != nil { - t.Fatal(err) - } - _, err = d.Put(context.Background(), "foo", map[string]interface{}{ - "foo": "aaa", + }, + }, kivik.Rev(rev1)) + if err != nil { + t.Fatal(err) + } + + return test{ + db: db, + id: "foo", + options: kivik.Params(map[string]interface{}{ + "attachments": false, + }), + wantDoc: map[string]interface{}{ + "_id": "foo", + "_rev": rev2, + "foo": "aaa", "_attachments": map[string]interface{}{ "att.txt": map[string]interface{}{ - "data": "dmVyc2lvbiAyCg==", + "content_type": "application/octet-stream", + "digest": "md5-sE0LKdS6wHgf6ETjKMXirA==", + "revpos": float64(2), + "length": float64(10), + "stub": true, }, }, - }, kivik.Rev("1-5f3e7150f872a1dd295f44b1e4a9fa41")) - if err != nil { - t.Fatal(err) - } - }, - id: "foo", - options: kivik.Params(map[string]interface{}{ - "attachments": false, - }), - wantDoc: map[string]interface{}{ - "_id": "foo", - "_rev": "2-f8b1930b7bdd8d48f4701188f999d326", - "foo": "aaa", + }, + } + }) + tests.Add("atts_since", func(t *testing.T) interface{} { + db := newDB(t) + rev1, err := db.Put(context.Background(), "foo", map[string]interface{}{ + "foo": "aaa", "_attachments": map[string]interface{}{ "att.txt": map[string]interface{}{ - "content_type": "application/octet-stream", - "digest": "md5-sE0LKdS6wHgf6ETjKMXirA==", - "revpos": float64(2), - "length": float64(10), - "stub": true, + "content_type": "text/plain", + "data": "YXR0LnR4dA==", }, }, - }, - }) - tests.Add("atts_since", test{ - setup: func(t *testing.T, d driver.DB) { - _, err := d.Put(context.Background(), "foo", map[string]interface{}{ - "foo": "aaa", - "_attachments": map[string]interface{}{ - "att.txt": map[string]interface{}{ - "content_type": "text/plain", - "data": "YXR0LnR4dA==", - }, + }, mock.NilOption) + if err != nil { + t.Fatal(err) + } + rev2, err := db.Put(context.Background(), "foo", map[string]interface{}{ + "foo": "aaa", + "_attachments": map[string]interface{}{ + "att.txt": map[string]interface{}{ + "stub": true, }, - }, mock.NilOption) - if err != nil { - t.Fatal(err) - } - _, err = d.Put(context.Background(), "foo", map[string]interface{}{ - "foo": "aaa", + "att2.txt": map[string]interface{}{ + "content_type": "text/plain", + "data": "YXR0LnR4dA==", + }, + }, + }, kivik.Rev(rev1)) + if err != nil { + t.Fatal(err) + } + + return test{ + db: db, + id: "foo", + options: kivik.Param("atts_since", []string{rev1}), + wantDoc: map[string]interface{}{ + "_id": "foo", + "_rev": rev2, + "foo": "aaa", "_attachments": map[string]interface{}{ "att.txt": map[string]interface{}{ - "stub": true, + "content_type": "text/plain", + "digest": "md5-a4NyknGw7YOh+a5ezPdZ4A==", + "revpos": float64(1), + "length": float64(7), + "stub": true, }, "att2.txt": map[string]interface{}{ "content_type": "text/plain", + "digest": "md5-a4NyknGw7YOh+a5ezPdZ4A==", + "revpos": float64(2), + "length": float64(7), "data": "YXR0LnR4dA==", }, }, - }, kivik.Rev("1-5f3e7150f872a1dd295f44b1e4a9fa41")) - if err != nil { - t.Fatal(err) - } - }, - id: "foo", - options: kivik.Param("atts_since", []string{"1-5f3e7150f872a1dd295f44b1e4a9fa41"}), - wantDoc: map[string]interface{}{ - "_id": "foo", - "_rev": "2-9dc3adaa7b08ac0ae246cded87669883", - "foo": "aaa", + }, + } + }) + tests.Add("atts_since with invalid rev format", func(t *testing.T) interface{} { + db := newDB(t) + _, err := db.Put(context.Background(), "foo", map[string]interface{}{ + "foo": "aaa", "_attachments": map[string]interface{}{ "att.txt": map[string]interface{}{ "content_type": "text/plain", - "digest": "md5-a4NyknGw7YOh+a5ezPdZ4A==", - "revpos": float64(1), - "length": float64(7), - "stub": true, + "data": "YXR0LnR4dA==", }, - "att2.txt": map[string]interface{}{ + }, + }, mock.NilOption) + if err != nil { + t.Fatal(err) + } + + return test{ + db: db, + id: "foo", + options: kivik.Param("atts_since", []string{"this is an invalid rev"}), + wantStatus: http.StatusBadRequest, + wantErr: `strconv.ParseInt: parsing "this is an invalid rev": invalid syntax`, + } + }) + tests.Add("atts_since with non-existent rev", func(t *testing.T) interface{} { + db := newDB(t) + rev, err := db.Put(context.Background(), "foo", map[string]interface{}{ + "foo": "aaa", + "_attachments": map[string]interface{}{ + "att.txt": map[string]interface{}{ "content_type": "text/plain", - "digest": "md5-a4NyknGw7YOh+a5ezPdZ4A==", - "revpos": float64(2), - "length": float64(7), "data": "YXR0LnR4dA==", }, }, - }, - }) - tests.Add("atts_since with invalid rev format", test{ - setup: func(t *testing.T, d driver.DB) { - _, err := d.Put(context.Background(), "foo", map[string]interface{}{ - "foo": "aaa", + }, mock.NilOption) + if err != nil { + t.Fatal(err) + } + + return test{ + db: db, + id: "foo", + options: kivik.Param("atts_since", []string{"1-asdfasdf"}), + wantDoc: map[string]interface{}{ + "_id": "foo", + "_rev": rev, + "foo": "aaa", "_attachments": map[string]interface{}{ "att.txt": map[string]interface{}{ "content_type": "text/plain", - "data": "YXR0LnR4dA==", + "digest": "md5-a4NyknGw7YOh+a5ezPdZ4A==", + "revpos": float64(1), + "length": float64(7), + "stub": true, }, }, - }, mock.NilOption) - if err != nil { - t.Fatal(err) - } - }, - id: "foo", - options: kivik.Param("atts_since", []string{"this is an invalid rev"}), - wantStatus: http.StatusBadRequest, - wantErr: `strconv.ParseInt: parsing "this is an invalid rev": invalid syntax`, + }, + } }) - tests.Add("atts_since with non-existent rev", test{ - setup: func(t *testing.T, d driver.DB) { - _, err := d.Put(context.Background(), "foo", map[string]interface{}{ - "foo": "aaa", + tests.Add("after PutAttachment", func(t *testing.T) interface{} { + db := newDB(t) + rev1, err := db.Put(context.Background(), "foo", map[string]string{ + "foo": "aaa", + }, mock.NilOption) + if err != nil { + t.Fatal(err) + } + att := driver.Attachment{ + ContentType: "text/plain", + Filename: "att.txt", + Content: io.NopCloser(strings.NewReader("test")), + } + + rev2, err := db.PutAttachment(context.Background(), "foo", &att, kivik.Rev(rev1)) + if err != nil { + t.Fatal(err) + } + + return test{ + db: db, + id: "foo", + wantDoc: map[string]interface{}{ + "_id": "foo", + "_rev": rev2, + "foo": "aaa", "_attachments": map[string]interface{}{ "att.txt": map[string]interface{}{ "content_type": "text/plain", - "data": "YXR0LnR4dA==", + "digest": "md5-CY9rzUYh03PK3k6DJie09g==", + "revpos": float64(2), + "length": float64(4), + "stub": true, }, }, - }, mock.NilOption) - if err != nil { - t.Fatal(err) - } - }, - id: "foo", - options: kivik.Param("atts_since", []string{"1-asdfasdf"}), - wantDoc: map[string]interface{}{ - "_id": "foo", - "_rev": "1-5f3e7150f872a1dd295f44b1e4a9fa41", - "foo": "aaa", - "_attachments": map[string]interface{}{ - "att.txt": map[string]interface{}{ - "content_type": "text/plain", - "digest": "md5-a4NyknGw7YOh+a5ezPdZ4A==", - "revpos": float64(1), - "length": float64(7), - "stub": true, - }, - }, - }, - }) - tests.Add("after PutAttachment", test{ - setup: func(t *testing.T, d driver.DB) { - _, err := d.Put(context.Background(), "foo", map[string]string{ - "foo": "aaa", - }, mock.NilOption) - if err != nil { - t.Fatal(err) - } - att := driver.Attachment{ - ContentType: "text/plain", - Filename: "att.txt", - Content: io.NopCloser(strings.NewReader("test")), - } - - _, err = d.PutAttachment(context.Background(), "foo", &att, kivik.Rev("1-8655eafbc9513d4857258c6d48f40399")) - if err != nil { - t.Fatal(err) - } - }, - id: "foo", - wantDoc: map[string]interface{}{ - "_id": "foo", - "_rev": "2-8655eafbc9513d4857258c6d48f40399", - "foo": "aaa", - "_attachments": map[string]interface{}{ - "att.txt": map[string]interface{}{ - "content_type": "text/plain", - "digest": "md5-CY9rzUYh03PK3k6DJie09g==", - "revpos": float64(2), - "length": float64(4), - "stub": true, - }, }, - }, + } }) /* @@ -1068,9 +1157,9 @@ func TestDBGet(t *testing.T) { tests.Run(t, func(t *testing.T, tt test) { t.Parallel() - db := newDB(t) - if tt.setup != nil { - tt.setup(t, db) + db := tt.db + if db == nil { + db = newDB(t) } opts := tt.options if opts == nil { diff --git a/x/sqlite/getattachment_test.go b/x/sqlite/getattachment_test.go index 75ce36548..f12e3479b 100644 --- a/x/sqlite/getattachment_test.go +++ b/x/sqlite/getattachment_test.go @@ -37,7 +37,7 @@ func TestDBGetAttachment(t *testing.T) { RevPos int64 } type test struct { - setup func(t *testing.T, db driver.DB) + db driver.DB docID string filename string @@ -53,48 +53,55 @@ func TestDBGetAttachment(t *testing.T) { wantStatus: http.StatusNotFound, wantErr: "Not Found: missing", }) - tests.Add("when the attachment exists, return it", test{ - setup: func(t *testing.T, db driver.DB) { - _, err := db.Put(context.Background(), "foo", map[string]interface{}{ - "_id": "foo", - "_attachments": map[string]interface{}{ - "foo.txt": map[string]interface{}{ - "content_type": "text/plain", - "data": "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGluZw==", - }, + tests.Add("when the attachment exists, return it", func(t *testing.T) interface{} { + db := newDB(t) + _, err := db.Put(context.Background(), "foo", map[string]interface{}{ + "_id": "foo", + "_attachments": map[string]interface{}{ + "foo.txt": map[string]interface{}{ + "content_type": "text/plain", + "data": "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGluZw==", }, - }, mock.NilOption) - if err != nil { - t.Fatal(err) - } - }, - docID: "foo", - filename: "foo.txt", + }, + }, mock.NilOption) + if err != nil { + t.Fatal(err) + } + + return test{ + db: db, + docID: "foo", + filename: "foo.txt", + } }) - tests.Add("when an attachment is returned, it contains metadata...", test{ - setup: func(t *testing.T, db driver.DB) { - _, err := db.Put(context.Background(), "foo", map[string]interface{}{ - "_id": "foo", - "_attachments": map[string]interface{}{ - "foo.txt": map[string]interface{}{ - "content_type": "text/plain", - "data": "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGluZw==", - }, + tests.Add("when an attachment is returned, it contains metadata...", func(t *testing.T) interface{} { + db := newDB(t) + _, err := db.Put(context.Background(), "foo", map[string]interface{}{ + "_id": "foo", + "_attachments": map[string]interface{}{ + "foo.txt": map[string]interface{}{ + "content_type": "text/plain", + "data": "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGluZw==", }, - }, mock.NilOption) - if err != nil { - t.Fatal(err) - } - }, - docID: "foo", - filename: "foo.txt", - wantAttachment: &attachmentMetadata{ - Filename: "foo.txt", - ContentType: "text/plain", - Length: 25, - RevPos: 1, - }, + }, + }, mock.NilOption) + if err != nil { + t.Fatal(err) + } + + return test{ + db: db, + docID: "foo", + filename: "foo.txt", + wantAttachment: &attachmentMetadata{ + Filename: "foo.txt", + ContentType: "text/plain", + Length: 25, + RevPos: 1, + }, + } }) + // GetAttachment returns the latest revision by default // @@ -118,9 +125,9 @@ func TestDBGetAttachment(t *testing.T) { tests.Run(t, func(t *testing.T, tt test) { t.Parallel() - db := newDB(t) - if tt.setup != nil { - tt.setup(t, db) + db := tt.db + if db == nil { + db = newDB(t) } // opts := tt.options // if opts == nil { diff --git a/x/sqlite/put_test.go b/x/sqlite/put_test.go index 47fa3375e..f48d66905 100644 --- a/x/sqlite/put_test.go +++ b/x/sqlite/put_test.go @@ -46,7 +46,7 @@ type attachmentRow struct { func TestDBPut(t *testing.T) { t.Parallel() type test struct { - setup func(*testing.T, driver.DB) + db driver.DB docID string doc interface{} options driver.Options @@ -100,60 +100,69 @@ func TestDBPut(t *testing.T) { wantStatus: http.StatusConflict, wantErr: "conflict", }) - tests.Add("attempt to update doc without rev should conflict", test{ - setup: func(t *testing.T, d driver.DB) { - if _, err := d.Put(context.Background(), "foo", map[string]string{"foo": "bar"}, mock.NilOption); err != nil { - t.Fatal(err) - } - }, - docID: "foo", - doc: map[string]interface{}{ - "foo": "bar", - }, - wantStatus: http.StatusConflict, - wantErr: "conflict", + tests.Add("attempt to update doc without rev should conflict", func(t *testing.T) interface{} { + db := newDB(t) + if _, err := db.Put(context.Background(), "foo", map[string]string{"foo": "bar"}, mock.NilOption); err != nil { + t.Fatal(err) + } + + return test{ + db: db, + docID: "foo", + doc: map[string]interface{}{ + "foo": "bar", + }, + wantStatus: http.StatusConflict, + wantErr: "conflict", + } }) - tests.Add("attempt to update doc with wrong rev should conflict", test{ - setup: func(t *testing.T, d driver.DB) { - if _, err := d.Put(context.Background(), "foo", map[string]string{"foo": "bar"}, mock.NilOption); err != nil { - t.Fatal(err) - } - }, - docID: "foo", - doc: map[string]interface{}{ - "_rev": "2-1234567890abcdef1234567890abcdef", - "foo": "bar", - }, - wantStatus: http.StatusConflict, - wantErr: "conflict", + tests.Add("attempt to update doc with wrong rev should conflict", func(t *testing.T) interface{} { + db := newDB(t) + if _, err := db.Put(context.Background(), "foo", map[string]string{"foo": "bar"}, mock.NilOption); err != nil { + t.Fatal(err) + } + + return test{ + db: db, + docID: "foo", + doc: map[string]interface{}{ + "_rev": "2-1234567890abcdef1234567890abcdef", + "foo": "bar", + }, + wantStatus: http.StatusConflict, + wantErr: "conflict", + } }) - tests.Add("update doc with correct rev", test{ - setup: func(t *testing.T, d driver.DB) { - _, err := d.Put(context.Background(), "foo", map[string]string{"foo": "bar"}, mock.NilOption) - if err != nil { - t.Fatal(err) - } - }, - docID: "foo", - doc: map[string]interface{}{ - "_rev": "1-9bb58f26192e4ba00f01e2e7b136bbd8", - "foo": "baz", - }, - wantRev: "2-afa7ae8a1906f4bb061be63525974f92", - wantRevs: []leaf{ - { - ID: "foo", - Rev: 1, - RevID: "9bb58f26192e4ba00f01e2e7b136bbd8", + tests.Add("update doc with correct rev", func(t *testing.T) interface{} { + db := newDB(t) + _, err := db.Put(context.Background(), "foo", map[string]string{"foo": "bar"}, mock.NilOption) + if err != nil { + t.Fatal(err) + } + + return test{ + db: db, + docID: "foo", + doc: map[string]interface{}{ + "_rev": "1-9bb58f26192e4ba00f01e2e7b136bbd8", + "foo": "baz", }, - { - ID: "foo", - Rev: 2, - RevID: "afa7ae8a1906f4bb061be63525974f92", - ParentRev: &[]int{1}[0], - ParentRevID: &[]string{"9bb58f26192e4ba00f01e2e7b136bbd8"}[0], + wantRev: "2-afa7ae8a1906f4bb061be63525974f92", + wantRevs: []leaf{ + { + ID: "foo", + Rev: 1, + RevID: "9bb58f26192e4ba00f01e2e7b136bbd8", + }, + { + ID: "foo", + Rev: 2, + RevID: "afa7ae8a1906f4bb061be63525974f92", + ParentRev: &[]int{1}[0], + ParentRevID: &[]string{"9bb58f26192e4ba00f01e2e7b136bbd8"}[0], + }, }, - }, + } }) tests.Add("update doc with new_edits=false, no existing doc", test{ docID: "foo", @@ -180,69 +189,75 @@ func TestDBPut(t *testing.T) { wantStatus: http.StatusBadRequest, wantErr: "When `new_edits: false`, the document needs `_rev` or `_revisions` specified", }) - tests.Add("update doc with new_edits=false, existing doc", test{ - setup: func(t *testing.T, d driver.DB) { - _, err := d.Put(context.Background(), "foo", map[string]string{"foo": "bar"}, mock.NilOption) - if err != nil { - t.Fatal(err) - } - }, - docID: "foo", - doc: map[string]interface{}{ - "_rev": "1-asdf", - "foo": "baz", - }, - options: kivik.Param("new_edits", false), - wantRev: "1-asdf", - wantRevs: []leaf{ - { - ID: "foo", - Rev: 1, - RevID: "9bb58f26192e4ba00f01e2e7b136bbd8", + tests.Add("update doc with new_edits=false, existing doc", func(t *testing.T) interface{} { + db := newDB(t) + _, err := db.Put(context.Background(), "foo", map[string]string{"foo": "bar"}, mock.NilOption) + if err != nil { + t.Fatal(err) + } + + return test{ + db: db, + docID: "foo", + doc: map[string]interface{}{ + "_rev": "1-asdf", + "foo": "baz", }, - { - ID: "foo", - Rev: 1, - RevID: "asdf", + options: kivik.Param("new_edits", false), + wantRev: "1-asdf", + wantRevs: []leaf{ + { + ID: "foo", + Rev: 1, + RevID: "9bb58f26192e4ba00f01e2e7b136bbd8", + }, + { + ID: "foo", + Rev: 1, + RevID: "asdf", + }, }, - }, + } }) - tests.Add("update doc with new_edits=false, existing doc and rev", test{ - setup: func(t *testing.T, d driver.DB) { - _, err := d.Put(context.Background(), "foo", map[string]string{"foo": "bar"}, mock.NilOption) - if err != nil { - t.Fatal(err) - } - }, - docID: "foo", - doc: map[string]interface{}{ - "_rev": "1-9bb58f26192e4ba00f01e2e7b136bbd8", - "foo": "baz", - }, - options: kivik.Param("new_edits", false), - wantRev: "1-9bb58f26192e4ba00f01e2e7b136bbd8", - check: func(t *testing.T, d driver.DB) { - var doc string - err := d.(*db).db.QueryRow(` + tests.Add("update doc with new_edits=false, existing doc and rev", func(t *testing.T) interface{} { + dbc := newDB(t) + _, err := dbc.Put(context.Background(), "foo", map[string]string{"foo": "bar"}, mock.NilOption) + if err != nil { + t.Fatal(err) + } + + return test{ + db: dbc, + docID: "foo", + doc: map[string]interface{}{ + "_rev": "1-9bb58f26192e4ba00f01e2e7b136bbd8", + "foo": "baz", + }, + options: kivik.Param("new_edits", false), + wantRev: "1-9bb58f26192e4ba00f01e2e7b136bbd8", + check: func(t *testing.T, d driver.DB) { + var doc string + err := d.(*db).db.QueryRow(` SELECT doc FROM test WHERE id='foo' AND rev=1 AND rev_id='9bb58f26192e4ba00f01e2e7b136bbd8'`).Scan(&doc) - if err != nil { - t.Fatal(err) - } - if doc != `{"foo":"bar"}` { - t.Errorf("Unexpected doc: %s", doc) - } - }, - wantRevs: []leaf{ - { - ID: "foo", - Rev: 1, - RevID: "9bb58f26192e4ba00f01e2e7b136bbd8", + if err != nil { + t.Fatal(err) + } + if doc != `{"foo":"bar"}` { + t.Errorf("Unexpected doc: %s", doc) + } }, - }, + wantRevs: []leaf{ + { + ID: "foo", + Rev: 1, + RevID: "9bb58f26192e4ba00f01e2e7b136bbd8", + }, + }, + } }) tests.Add("doc id in url and doc differ", test{ docID: "foo", @@ -417,133 +432,142 @@ func TestDBPut(t *testing.T) { wantStatus: http.StatusBadRequest, wantErr: "Document rev and option have different values", }) - tests.Add("new_edits=false, with _revisions replayed", test{ - setup: func(t *testing.T, d driver.DB) { - _, err := d.Put(context.Background(), "foo", map[string]interface{}{ - "_revisions": map[string]interface{}{ - "ids": []string{"ghi", "def", "abc"}, - "start": 3, - }, - "foo": "bar", - }, kivik.Param("new_edits", false)) - if err != nil { - t.Fatal(err) - } - }, - docID: "foo", - doc: map[string]interface{}{ + tests.Add("new_edits=false, with _revisions replayed", func(t *testing.T) interface{} { + db := newDB(t) + _, err := db.Put(context.Background(), "foo", map[string]interface{}{ "_revisions": map[string]interface{}{ "ids": []string{"ghi", "def", "abc"}, "start": 3, }, "foo": "bar", - }, - options: kivik.Param("new_edits", false), - wantRev: "3-ghi", - wantRevs: []leaf{ - { - ID: "foo", - Rev: 1, - RevID: "abc", - }, - { - ID: "foo", - Rev: 2, - RevID: "def", - ParentRev: &[]int{1}[0], - ParentRevID: &[]string{"abc"}[0], + }, kivik.Param("new_edits", false)) + if err != nil { + t.Fatal(err) + } + + return test{ + db: db, + docID: "foo", + doc: map[string]interface{}{ + "_revisions": map[string]interface{}{ + "ids": []string{"ghi", "def", "abc"}, + "start": 3, + }, + "foo": "bar", }, - { - ID: "foo", - Rev: 3, - RevID: "ghi", - ParentRev: &[]int{2}[0], - ParentRevID: &[]string{"def"}[0], + options: kivik.Param("new_edits", false), + wantRev: "3-ghi", + wantRevs: []leaf{ + { + ID: "foo", + Rev: 1, + RevID: "abc", + }, + { + ID: "foo", + Rev: 2, + RevID: "def", + ParentRev: &[]int{1}[0], + ParentRevID: &[]string{"abc"}[0], + }, + { + ID: "foo", + Rev: 3, + RevID: "ghi", + ParentRev: &[]int{2}[0], + ParentRevID: &[]string{"def"}[0], + }, }, - }, + } }) - tests.Add("new_edits=false, with _revisions and some revs already exist without parents", test{ - setup: func(t *testing.T, d driver.DB) { - _, err := d.(*db).db.Exec(` - INSERT INTO test_revs (id, rev, rev_id) - VALUES ('foo', 1, 'abc'), ('foo', 2, 'def') - `) - if err != nil { - t.Fatal(err) - } - }, - docID: "foo", - doc: map[string]interface{}{ - "_revisions": map[string]interface{}{ - "ids": []string{"ghi", "def", "abc"}, - "start": 3, - }, - "foo": "bar", - }, - options: kivik.Param("new_edits", false), - wantRev: "3-ghi", - wantRevs: []leaf{ - { - ID: "foo", - Rev: 1, - RevID: "abc", - }, - { - ID: "foo", - Rev: 2, - RevID: "def", - ParentRev: &[]int{1}[0], - ParentRevID: &[]string{"abc"}[0], + tests.Add("new_edits=false, with _revisions and some revs already exist without parents", func(t *testing.T) interface{} { + dbc := newDB(t) + _, err := dbc.(*db).db.Exec(` + INSERT INTO test_revs (id, rev, rev_id) + VALUES ('foo', 1, 'abc'), ('foo', 2, 'def') + `) + if err != nil { + t.Fatal(err) + } + + return test{ + db: dbc, + docID: "foo", + doc: map[string]interface{}{ + "_revisions": map[string]interface{}{ + "ids": []string{"ghi", "def", "abc"}, + "start": 3, + }, + "foo": "bar", }, - { - ID: "foo", - Rev: 3, - RevID: "ghi", - ParentRev: &[]int{2}[0], - ParentRevID: &[]string{"def"}[0], + options: kivik.Param("new_edits", false), + wantRev: "3-ghi", + wantRevs: []leaf{ + { + ID: "foo", + Rev: 1, + RevID: "abc", + }, + { + ID: "foo", + Rev: 2, + RevID: "def", + ParentRev: &[]int{1}[0], + ParentRevID: &[]string{"abc"}[0], + }, + { + ID: "foo", + Rev: 3, + RevID: "ghi", + ParentRev: &[]int{2}[0], + ParentRevID: &[]string{"def"}[0], + }, }, - }, + } }) - tests.Add("new_edits=false, with _revisions and some revs already exist with docs", test{ - setup: func(t *testing.T, d driver.DB) { - if _, err := d.Put(context.Background(), "foo", map[string]interface{}{ - "_rev": "2-def", - "moo": "oink", - }, kivik.Param("new_edits", false)); err != nil { - t.Fatal(err) - } - }, - docID: "foo", - doc: map[string]interface{}{ - "_revisions": map[string]interface{}{ - "ids": []string{"ghi", "def", "abc"}, - "start": 3, - }, - "foo": "bar", - }, - options: kivik.Param("new_edits", false), - wantRev: "3-ghi", - wantRevs: []leaf{ - { - ID: "foo", - Rev: 1, - RevID: "abc", - }, - { - ID: "foo", - Rev: 2, - RevID: "def", - ParentRev: &[]int{1}[0], - ParentRevID: &[]string{"abc"}[0], + tests.Add("new_edits=false, with _revisions and some revs already exist with docs", func(t *testing.T) interface{} { + db := newDB(t) + if _, err := db.Put(context.Background(), "foo", map[string]interface{}{ + "_rev": "2-def", + "moo": "oink", + }, kivik.Param("new_edits", false)); err != nil { + t.Fatal(err) + } + + return test{ + db: db, + docID: "foo", + doc: map[string]interface{}{ + "_revisions": map[string]interface{}{ + "ids": []string{"ghi", "def", "abc"}, + "start": 3, + }, + "foo": "bar", }, - { - ID: "foo", - Rev: 3, - RevID: "ghi", - ParentRev: &[]int{2}[0], - ParentRevID: &[]string{"def"}[0], + options: kivik.Param("new_edits", false), + wantRev: "3-ghi", + wantRevs: []leaf{ + { + ID: "foo", + Rev: 1, + RevID: "abc", + }, + { + ID: "foo", + Rev: 2, + RevID: "def", + ParentRev: &[]int{1}[0], + ParentRevID: &[]string{"abc"}[0], + }, + { + ID: "foo", + Rev: 3, + RevID: "ghi", + ParentRev: &[]int{2}[0], + ParentRevID: &[]string{"def"}[0], + }, }, - }, + } }) tests.Add("new_edits=true, with _revisions should conflict for new doc", test{ docID: "foo", @@ -558,138 +582,150 @@ func TestDBPut(t *testing.T) { wantStatus: http.StatusConflict, wantErr: "conflict", }) - tests.Add("new_edits=true, with _revisions should conflict for wrong rev", test{ - setup: func(t *testing.T, d driver.DB) { - _, err := d.Put(context.Background(), "foo", map[string]interface{}{ + tests.Add("new_edits=true, with _revisions should conflict for wrong rev", func(t *testing.T) interface{} { + db := newDB(t) + _, err := db.Put(context.Background(), "foo", map[string]interface{}{ + "foo": "bar", + }, mock.NilOption) + if err != nil { + t.Fatal(err) + } + + return test{ + db: db, + docID: "foo", + doc: map[string]interface{}{ + "_revisions": map[string]interface{}{ + "ids": []string{"ghi"}, + "start": 1, + }, "foo": "bar", - }, mock.NilOption) - if err != nil { - t.Fatal(err) - } - }, - docID: "foo", - doc: map[string]interface{}{ - "_revisions": map[string]interface{}{ - "ids": []string{"ghi"}, - "start": 1, }, - "foo": "bar", - }, - options: kivik.Param("new_edits", true), - wantStatus: http.StatusConflict, - wantErr: "conflict", + options: kivik.Param("new_edits", true), + wantStatus: http.StatusConflict, + wantErr: "conflict", + } }) - tests.Add("new_edits=true, with _revisions should succeed for correct rev", test{ - setup: func(t *testing.T, d driver.DB) { - _, err := d.Put(context.Background(), "foo", map[string]interface{}{ - "foo": "bar", - "_rev": "1-abc", - }, kivik.Param("new_edits", false)) - if err != nil { - t.Fatal(err) - } - }, - docID: "foo", - doc: map[string]interface{}{ - "_revisions": map[string]interface{}{ - "ids": []string{"abc"}, - "start": 1, - }, - "foo": "bar", - }, - options: kivik.Param("new_edits", true), - wantRev: "2-9bb58f26192e4ba00f01e2e7b136bbd8", - wantRevs: []leaf{ - { - ID: "foo", - Rev: 1, - RevID: "abc", + tests.Add("new_edits=true, with _revisions should succeed for correct rev", func(t *testing.T) interface{} { + db := newDB(t) + _, err := db.Put(context.Background(), "foo", map[string]interface{}{ + "foo": "bar", + "_rev": "1-abc", + }, kivik.Param("new_edits", false)) + if err != nil { + t.Fatal(err) + } + + return test{ + db: db, + docID: "foo", + doc: map[string]interface{}{ + "_revisions": map[string]interface{}{ + "ids": []string{"abc"}, + "start": 1, + }, + "foo": "bar", }, - { - ID: "foo", - Rev: 2, - RevID: "9bb58f26192e4ba00f01e2e7b136bbd8", - ParentRev: &[]int{1}[0], - ParentRevID: &[]string{"abc"}[0], + options: kivik.Param("new_edits", true), + wantRev: "2-9bb58f26192e4ba00f01e2e7b136bbd8", + wantRevs: []leaf{ + { + ID: "foo", + Rev: 1, + RevID: "abc", + }, + { + ID: "foo", + Rev: 2, + RevID: "9bb58f26192e4ba00f01e2e7b136bbd8", + ParentRev: &[]int{1}[0], + ParentRevID: &[]string{"abc"}[0], + }, }, - }, + } }) - tests.Add("new_edits=true, with _revisions should succeed for correct history", test{ - setup: func(t *testing.T, d driver.DB) { - _, err := d.Put(context.Background(), "foo", map[string]interface{}{ - "foo": "bar", + tests.Add("new_edits=true, with _revisions should succeed for correct history", func(t *testing.T) interface{} { + db := newDB(t) + _, err := db.Put(context.Background(), "foo", map[string]interface{}{ + "foo": "bar", + "_revisions": map[string]interface{}{ + "ids": []string{"ghi", "def", "abc"}, + "start": 3, + }, + }, kivik.Param("new_edits", false)) + if err != nil { + t.Fatal(err) + } + + return test{ + db: db, + docID: "foo", + doc: map[string]interface{}{ "_revisions": map[string]interface{}{ "ids": []string{"ghi", "def", "abc"}, "start": 3, }, - }, kivik.Param("new_edits", false)) - if err != nil { - t.Fatal(err) - } - }, - docID: "foo", - doc: map[string]interface{}{ + "foo": "bar", + }, + options: kivik.Param("new_edits", true), + wantRev: "4-9bb58f26192e4ba00f01e2e7b136bbd8", + wantRevs: []leaf{ + { + ID: "foo", + Rev: 1, + RevID: "abc", + }, + { + ID: "foo", + Rev: 2, + RevID: "def", + ParentRev: &[]int{1}[0], + ParentRevID: &[]string{"abc"}[0], + }, + { + ID: "foo", + Rev: 3, + RevID: "ghi", + ParentRev: &[]int{2}[0], + ParentRevID: &[]string{"def"}[0], + }, + { + ID: "foo", + Rev: 4, + RevID: "9bb58f26192e4ba00f01e2e7b136bbd8", + ParentRev: &[]int{3}[0], + ParentRevID: &[]string{"ghi"}[0], + }, + }, + } + }) + tests.Add("new_edits=true, with _revisions should fail for wrong history", func(t *testing.T) interface{} { + db := newDB(t) + _, err := db.Put(context.Background(), "foo", map[string]interface{}{ + "foo": "bar", "_revisions": map[string]interface{}{ "ids": []string{"ghi", "def", "abc"}, "start": 3, }, - "foo": "bar", - }, - options: kivik.Param("new_edits", true), - wantRev: "4-9bb58f26192e4ba00f01e2e7b136bbd8", - wantRevs: []leaf{ - { - ID: "foo", - Rev: 1, - RevID: "abc", - }, - { - ID: "foo", - Rev: 2, - RevID: "def", - ParentRev: &[]int{1}[0], - ParentRevID: &[]string{"abc"}[0], - }, - { - ID: "foo", - Rev: 3, - RevID: "ghi", - ParentRev: &[]int{2}[0], - ParentRevID: &[]string{"def"}[0], - }, - { - ID: "foo", - Rev: 4, - RevID: "9bb58f26192e4ba00f01e2e7b136bbd8", - ParentRev: &[]int{3}[0], - ParentRevID: &[]string{"ghi"}[0], - }, - }, - }) - tests.Add("new_edits=true, with _revisions should fail for wrong history", test{ - setup: func(t *testing.T, d driver.DB) { - _, err := d.Put(context.Background(), "foo", map[string]interface{}{ - "foo": "bar", + }, kivik.Param("new_edits", false)) + if err != nil { + t.Fatal(err) + } + + return test{ + db: db, + docID: "foo", + doc: map[string]interface{}{ "_revisions": map[string]interface{}{ - "ids": []string{"ghi", "def", "abc"}, + "ids": []string{"ghi", "xyz", "abc"}, "start": 3, }, - }, kivik.Param("new_edits", false)) - if err != nil { - t.Fatal(err) - } - }, - docID: "foo", - doc: map[string]interface{}{ - "_revisions": map[string]interface{}{ - "ids": []string{"ghi", "xyz", "abc"}, - "start": 3, + "foo": "bar", }, - "foo": "bar", - }, - options: kivik.Param("new_edits", true), - wantStatus: http.StatusConflict, - wantErr: "conflict", + options: kivik.Param("new_edits", true), + wantStatus: http.StatusConflict, + wantErr: "conflict", + } }) tests.Add("with attachment, no data", test{ docID: "foo", @@ -791,128 +827,135 @@ func TestDBPut(t *testing.T) { }, }, }) - tests.Add("update doc with attachments without deleting them", test{ - setup: func(t *testing.T, d driver.DB) { - _, err := d.Put(context.Background(), "foo", map[string]interface{}{ - "foo": "bar", - "_attachments": map[string]interface{}{ - "foo.txt": map[string]interface{}{ - "content_type": "text/plain", - "data": "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGluZw==", - }, - }, - }, mock.NilOption) - if err != nil { - t.Fatal(err) - } - }, - docID: "foo", - doc: map[string]interface{}{ - "_rev": "1-4b98474b255b67856668474854b0d5f8", - "foo": "baz", + tests.Add("update doc with attachments without deleting them", func(t *testing.T) interface{} { + db := newDB(t) + _, err := db.Put(context.Background(), "foo", map[string]interface{}{ + "foo": "bar", "_attachments": map[string]interface{}{ "foo.txt": map[string]interface{}{ - "stub": true, + "content_type": "text/plain", + "data": "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGluZw==", }, }, - }, - wantRev: "2-a7cadffe4f950734f8eeae832e15f6c2", - wantRevs: []leaf{ - { - ID: "foo", - Rev: 1, - RevID: "4b98474b255b67856668474854b0d5f8", - }, - { - ID: "foo", - Rev: 2, - RevID: "a7cadffe4f950734f8eeae832e15f6c2", - ParentRev: &[]int{1}[0], - ParentRevID: &[]string{"4b98474b255b67856668474854b0d5f8"}[0], - }, - }, - wantAttachments: []attachmentRow{ - { - DocID: "foo", - Rev: 1, - RevID: "4b98474b255b67856668474854b0d5f8", - Filename: "foo.txt", - ContentType: "text/plain", - Length: 25, - Digest: "md5-TmfHxaRgUrE9l3tkAn4s0Q==", - Data: "This is a base64 encoding", - }, - }, - }) - tests.Add("update doc with attachments, delete one", test{ - setup: func(t *testing.T, d driver.DB) { - _, err := d.Put(context.Background(), "foo", map[string]interface{}{ - "foo": "bar", + }, mock.NilOption) + if err != nil { + t.Fatal(err) + } + + return test{ + db: db, + docID: "foo", + doc: map[string]interface{}{ + "_rev": "1-4b98474b255b67856668474854b0d5f8", + "foo": "baz", "_attachments": map[string]interface{}{ "foo.txt": map[string]interface{}{ - "content_type": "text/plain", - "data": "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGluZw==", - }, - "bar.txt": map[string]interface{}{ - "content_type": "text/plain", - "data": "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGluZw==", + "stub": true, }, }, - }, mock.NilOption) - if err != nil { - t.Fatal(err) - } - }, - docID: "foo", - doc: map[string]interface{}{ - "_rev": "1-7884bb688778892bd22837c5d8cba96b", - "foo": "baz", + }, + wantRev: "2-a7cadffe4f950734f8eeae832e15f6c2", + wantRevs: []leaf{ + { + ID: "foo", + Rev: 1, + RevID: "4b98474b255b67856668474854b0d5f8", + }, + { + ID: "foo", + Rev: 2, + RevID: "a7cadffe4f950734f8eeae832e15f6c2", + ParentRev: &[]int{1}[0], + ParentRevID: &[]string{"4b98474b255b67856668474854b0d5f8"}[0], + }, + }, + wantAttachments: []attachmentRow{ + { + DocID: "foo", + Rev: 1, + RevID: "4b98474b255b67856668474854b0d5f8", + Filename: "foo.txt", + ContentType: "text/plain", + Length: 25, + Digest: "md5-TmfHxaRgUrE9l3tkAn4s0Q==", + Data: "This is a base64 encoding", + }, + }, + } + }) + tests.Add("update doc with attachments, delete one", func(t *testing.T) interface{} { + db := newDB(t) + _, err := db.Put(context.Background(), "foo", map[string]interface{}{ + "foo": "bar", "_attachments": map[string]interface{}{ "foo.txt": map[string]interface{}{ - "stub": true, + "content_type": "text/plain", + "data": "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGluZw==", + }, + "bar.txt": map[string]interface{}{ + "content_type": "text/plain", + "data": "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGluZw==", }, }, - }, - wantRev: "2-a7cadffe4f950734f8eeae832e15f6c2", - wantRevs: []leaf{ - { - ID: "foo", - Rev: 1, - RevID: "7884bb688778892bd22837c5d8cba96b", - }, - { - ID: "foo", - Rev: 2, - RevID: "a7cadffe4f950734f8eeae832e15f6c2", - ParentRev: &[]int{1}[0], - ParentRevID: &[]string{"7884bb688778892bd22837c5d8cba96b"}[0], + }, mock.NilOption) + if err != nil { + t.Fatal(err) + } + + return test{ + db: db, + docID: "foo", + doc: map[string]interface{}{ + "_rev": "1-7884bb688778892bd22837c5d8cba96b", + "foo": "baz", + "_attachments": map[string]interface{}{ + "foo.txt": map[string]interface{}{ + "stub": true, + }, + }, }, - }, - wantAttachments: []attachmentRow{ - { - DocID: "foo", - Rev: 1, - RevID: "7884bb688778892bd22837c5d8cba96b", - Filename: "bar.txt", - ContentType: "text/plain", - Length: 25, - Digest: "md5-TmfHxaRgUrE9l3tkAn4s0Q==", - Data: "This is a base64 encoding", - DeletedRev: &[]int{2}[0], - DeletedRevID: &[]string{"a7cadffe4f950734f8eeae832e15f6c2"}[0], + wantRev: "2-a7cadffe4f950734f8eeae832e15f6c2", + wantRevs: []leaf{ + { + ID: "foo", + Rev: 1, + RevID: "7884bb688778892bd22837c5d8cba96b", + }, + { + ID: "foo", + Rev: 2, + RevID: "a7cadffe4f950734f8eeae832e15f6c2", + ParentRev: &[]int{1}[0], + ParentRevID: &[]string{"7884bb688778892bd22837c5d8cba96b"}[0], + }, }, - { - DocID: "foo", - Rev: 1, - RevID: "7884bb688778892bd22837c5d8cba96b", - Filename: "foo.txt", - ContentType: "text/plain", - Length: 25, - Digest: "md5-TmfHxaRgUrE9l3tkAn4s0Q==", - Data: "This is a base64 encoding", + wantAttachments: []attachmentRow{ + { + DocID: "foo", + Rev: 1, + RevID: "7884bb688778892bd22837c5d8cba96b", + Filename: "bar.txt", + ContentType: "text/plain", + Length: 25, + Digest: "md5-TmfHxaRgUrE9l3tkAn4s0Q==", + Data: "This is a base64 encoding", + DeletedRev: &[]int{2}[0], + DeletedRevID: &[]string{"a7cadffe4f950734f8eeae832e15f6c2"}[0], + }, + { + DocID: "foo", + Rev: 1, + RevID: "7884bb688778892bd22837c5d8cba96b", + Filename: "foo.txt", + ContentType: "text/plain", + Length: 25, + Digest: "md5-TmfHxaRgUrE9l3tkAn4s0Q==", + Data: "This is a base64 encoding", + }, }, - }, + } }) + /* TODO: - update conflicting leaf @@ -928,9 +971,9 @@ func TestDBPut(t *testing.T) { tests.Run(t, func(t *testing.T, tt test) { t.Parallel() - dbc := newDB(t) - if tt.setup != nil { - tt.setup(t, dbc) + dbc := tt.db + if dbc == nil { + dbc = newDB(t) } opts := tt.options if opts == nil { diff --git a/x/sqlite/putattachment_test.go b/x/sqlite/putattachment_test.go index 7e1e44db0..446de458d 100644 --- a/x/sqlite/putattachment_test.go +++ b/x/sqlite/putattachment_test.go @@ -33,7 +33,7 @@ import ( func TestDBPutAttachment(t *testing.T) { t.Parallel() type test struct { - setup func(*testing.T, driver.DB) + db driver.DB docID string attachment *driver.Attachment options driver.Options @@ -74,47 +74,50 @@ func TestDBPutAttachment(t *testing.T) { }, }, }) - tests.Add("add attachment to existing doc", test{ - setup: func(t *testing.T, db driver.DB) { - _, err := db.Put(context.Background(), "foo", map[string]string{"foo": "bar"}, mock.NilOption) - if err != nil { - t.Fatal(err) - } - }, - docID: "foo", - attachment: &driver.Attachment{ - Filename: "foo.txt", - ContentType: "text/plain", - Content: io.NopCloser(strings.NewReader("Hello, world!")), - }, - options: kivik.Rev("1-9bb58f26192e4ba00f01e2e7b136bbd8"), - wantRev: "2-9bb58f26192e4ba00f01e2e7b136bbd8", - wantRevs: []leaf{ - { - ID: "foo", - Rev: 1, - RevID: "9bb58f26192e4ba00f01e2e7b136bbd8", - }, - { - ID: "foo", - Rev: 2, - RevID: "9bb58f26192e4ba00f01e2e7b136bbd8", - ParentRev: &[]int{1}[0], - ParentRevID: &[]string{"9bb58f26192e4ba00f01e2e7b136bbd8"}[0], - }, - }, - wantAttachments: []attachmentRow{ - { - DocID: "foo", - Rev: 2, - RevID: "9bb58f26192e4ba00f01e2e7b136bbd8", + tests.Add("add attachment to existing doc", func(t *testing.T) interface{} { + db := newDB(t) + rev, err := db.Put(context.Background(), "foo", map[string]string{"foo": "bar"}, mock.NilOption) + if err != nil { + t.Fatal(err) + } + + return test{ + db: db, + docID: "foo", + attachment: &driver.Attachment{ Filename: "foo.txt", ContentType: "text/plain", - Digest: "md5-bNNVbesNpUvKBgtMOUeYOQ==", - Length: 13, - Data: "Hello, world!", + Content: io.NopCloser(strings.NewReader("Hello, world!")), }, - }, + options: kivik.Rev(rev), + wantRev: "2-9bb58f26192e4ba00f01e2e7b136bbd8", + wantRevs: []leaf{ + { + ID: "foo", + Rev: 1, + RevID: "9bb58f26192e4ba00f01e2e7b136bbd8", + }, + { + ID: "foo", + Rev: 2, + RevID: "9bb58f26192e4ba00f01e2e7b136bbd8", + ParentRev: &[]int{1}[0], + ParentRevID: &[]string{"9bb58f26192e4ba00f01e2e7b136bbd8"}[0], + }, + }, + wantAttachments: []attachmentRow{ + { + DocID: "foo", + Rev: 2, + RevID: "9bb58f26192e4ba00f01e2e7b136bbd8", + Filename: "foo.txt", + ContentType: "text/plain", + Digest: "md5-bNNVbesNpUvKBgtMOUeYOQ==", + Length: 13, + Data: "Hello, world!", + }, + }, + } }) tests.Add("non-existing doc with rev should conflict", test{ docID: "foo", @@ -127,149 +130,158 @@ func TestDBPutAttachment(t *testing.T) { wantStatus: http.StatusConflict, wantErr: "conflict", }) - tests.Add("existing doc, wrong rev", test{ - setup: func(t *testing.T, db driver.DB) { - _, err := db.Put(context.Background(), "foo", map[string]string{"foo": "bar"}, mock.NilOption) - if err != nil { - t.Fatal(err) - } - }, - docID: "foo", - attachment: &driver.Attachment{ - Filename: "foo.txt", - ContentType: "text/plain", - Content: io.NopCloser(strings.NewReader("Hello, world!")), - }, - options: kivik.Rev("1-wrong"), - wantStatus: http.StatusConflict, - wantErr: "conflict", - }) - tests.Add("don't delete existing attachment", test{ - setup: func(t *testing.T, db driver.DB) { - _, err := db.Put(context.Background(), "foo", map[string]interface{}{ - "foo": "bar", - "_attachments": map[string]interface{}{ - "foo.txt": map[string]interface{}{ - "content_type": "text/plain", - "data": "SGVsbG8sIHdvcmxkIQ==", - }, - }, - }, mock.NilOption) - if err != nil { - t.Fatal(err) - } - }, - docID: "foo", - attachment: &driver.Attachment{ - Filename: "bar.txt", - ContentType: "text/plain", - Content: io.NopCloser(strings.NewReader("Hello, world!")), - }, - options: kivik.Rev("1-53929381825df5c0a2b57f34d168999d"), - wantRev: "2-53929381825df5c0a2b57f34d168999d", - wantRevs: []leaf{ - { - ID: "foo", - Rev: 1, - RevID: "53929381825df5c0a2b57f34d168999d", - }, - { - ID: "foo", - Rev: 2, - RevID: "53929381825df5c0a2b57f34d168999d", - ParentRev: &[]int{1}[0], - ParentRevID: &[]string{"53929381825df5c0a2b57f34d168999d"}[0], - }, - }, - wantAttachments: []attachmentRow{ - { - DocID: "foo", - Rev: 1, - RevID: "53929381825df5c0a2b57f34d168999d", + tests.Add("existing doc, wrong rev", func(t *testing.T) interface{} { + db := newDB(t) + _, err := db.Put(context.Background(), "foo", map[string]string{"foo": "bar"}, mock.NilOption) + if err != nil { + t.Fatal(err) + } + + return test{ + db: db, + docID: "foo", + attachment: &driver.Attachment{ Filename: "foo.txt", ContentType: "text/plain", - Digest: "md5-bNNVbesNpUvKBgtMOUeYOQ==", - Length: 13, - Data: "Hello, world!", + Content: io.NopCloser(strings.NewReader("Hello, world!")), }, - { - DocID: "foo", - Rev: 2, - RevID: "53929381825df5c0a2b57f34d168999d", + options: kivik.Rev("1-wrong"), + wantStatus: http.StatusConflict, + wantErr: "conflict", + } + }) + tests.Add("don't delete existing attachment", func(t *testing.T) interface{} { + db := newDB(t) + _, err := db.Put(context.Background(), "foo", map[string]interface{}{ + "foo": "bar", + "_attachments": map[string]interface{}{ + "foo.txt": map[string]interface{}{ + "content_type": "text/plain", + "data": "SGVsbG8sIHdvcmxkIQ==", + }, + }, + }, mock.NilOption) + if err != nil { + t.Fatal(err) + } + + return test{ + db: db, + docID: "foo", + attachment: &driver.Attachment{ Filename: "bar.txt", ContentType: "text/plain", - Digest: "md5-bNNVbesNpUvKBgtMOUeYOQ==", - Length: 13, - Data: "Hello, world!", + Content: io.NopCloser(strings.NewReader("Hello, world!")), }, - }, - }) - tests.Add("update existing attachment", test{ - setup: func(t *testing.T, db driver.DB) { - _, err := db.Put(context.Background(), "foo", map[string]interface{}{ - "foo": "bar", - "_attachments": map[string]interface{}{ - "foo.txt": map[string]interface{}{ - "content_type": "text/plain", - "data": "SGVsbG8sIHdvcmxkIQ==", - }, + options: kivik.Rev("1-53929381825df5c0a2b57f34d168999d"), + wantRev: "2-53929381825df5c0a2b57f34d168999d", + wantRevs: []leaf{ + { + ID: "foo", + Rev: 1, + RevID: "53929381825df5c0a2b57f34d168999d", + }, + { + ID: "foo", + Rev: 2, + RevID: "53929381825df5c0a2b57f34d168999d", + ParentRev: &[]int{1}[0], + ParentRevID: &[]string{"53929381825df5c0a2b57f34d168999d"}[0], }, - }, mock.NilOption) - if err != nil { - t.Fatal(err) - } - }, - docID: "foo", - attachment: &driver.Attachment{ - Filename: "foo.txt", - ContentType: "text/plain", - Content: io.NopCloser(strings.NewReader("Hello, everybody!")), - }, - options: kivik.Rev("1-53929381825df5c0a2b57f34d168999d"), - wantRev: "2-53929381825df5c0a2b57f34d168999d", - wantRevs: []leaf{ - { - ID: "foo", - Rev: 1, - RevID: "53929381825df5c0a2b57f34d168999d", }, - { - ID: "foo", - Rev: 2, - RevID: "53929381825df5c0a2b57f34d168999d", - ParentRev: &[]int{1}[0], - ParentRevID: &[]string{"53929381825df5c0a2b57f34d168999d"}[0], + wantAttachments: []attachmentRow{ + { + DocID: "foo", + Rev: 1, + RevID: "53929381825df5c0a2b57f34d168999d", + Filename: "foo.txt", + ContentType: "text/plain", + Digest: "md5-bNNVbesNpUvKBgtMOUeYOQ==", + Length: 13, + Data: "Hello, world!", + }, + { + DocID: "foo", + Rev: 2, + RevID: "53929381825df5c0a2b57f34d168999d", + Filename: "bar.txt", + ContentType: "text/plain", + Digest: "md5-bNNVbesNpUvKBgtMOUeYOQ==", + Length: 13, + Data: "Hello, world!", + }, }, - }, - wantAttachments: []attachmentRow{ - { - DocID: "foo", - Rev: 1, - RevID: "53929381825df5c0a2b57f34d168999d", - Filename: "foo.txt", - Digest: "md5-bNNVbesNpUvKBgtMOUeYOQ==", - Length: 13, - ContentType: "text/plain", - Data: "Hello, world!", + } + }) + tests.Add("update existing attachment", func(t *testing.T) interface{} { + db := newDB(t) + _, err := db.Put(context.Background(), "foo", map[string]interface{}{ + "foo": "bar", + "_attachments": map[string]interface{}{ + "foo.txt": map[string]interface{}{ + "content_type": "text/plain", + "data": "SGVsbG8sIHdvcmxkIQ==", + }, }, - { - DocID: "foo", - Rev: 2, - RevID: "53929381825df5c0a2b57f34d168999d", + }, mock.NilOption) + if err != nil { + t.Fatal(err) + } + + return test{ + db: db, + docID: "foo", + attachment: &driver.Attachment{ Filename: "foo.txt", ContentType: "text/plain", - Digest: "md5-kDqL1OTtoET1YR0WdPZ5tQ==", - Length: 17, - Data: "Hello, everybody!", + Content: io.NopCloser(strings.NewReader("Hello, everybody!")), }, - }, + options: kivik.Rev("1-53929381825df5c0a2b57f34d168999d"), + wantRev: "2-53929381825df5c0a2b57f34d168999d", + wantRevs: []leaf{ + { + ID: "foo", + Rev: 1, + RevID: "53929381825df5c0a2b57f34d168999d", + }, + { + ID: "foo", + Rev: 2, + RevID: "53929381825df5c0a2b57f34d168999d", + ParentRev: &[]int{1}[0], + ParentRevID: &[]string{"53929381825df5c0a2b57f34d168999d"}[0], + }, + }, + wantAttachments: []attachmentRow{ + { + DocID: "foo", + Rev: 1, + RevID: "53929381825df5c0a2b57f34d168999d", + Filename: "foo.txt", + Digest: "md5-bNNVbesNpUvKBgtMOUeYOQ==", + Length: 13, + ContentType: "text/plain", + Data: "Hello, world!", + }, + { + DocID: "foo", + Rev: 2, + RevID: "53929381825df5c0a2b57f34d168999d", + Filename: "foo.txt", + ContentType: "text/plain", + Digest: "md5-kDqL1OTtoET1YR0WdPZ5tQ==", + Length: 17, + Data: "Hello, everybody!", + }, + }, + } }) tests.Run(t, func(t *testing.T, tt test) { t.Parallel() - dbc := newDB(t) - if tt.setup != nil { - tt.setup(t, dbc) + dbc := tt.db + if dbc == nil { + dbc = newDB(t) } opts := tt.options if opts == nil { diff --git a/x/sqlite/views_test.go b/x/sqlite/views_test.go index ceba59ba3..6e1f7e570 100644 --- a/x/sqlite/views_test.go +++ b/x/sqlite/views_test.go @@ -39,7 +39,7 @@ type rowResult struct { func TestDBAllDocs(t *testing.T) { t.Parallel() type test struct { - setup func(*testing.T, driver.DB) + db driver.DB options driver.Options want []rowResult wantStatus int @@ -49,509 +49,559 @@ func TestDBAllDocs(t *testing.T) { tests.Add("no docs in db", test{ want: nil, }) - tests.Add("single doc", test{ - setup: func(t *testing.T, db driver.DB) { - _, err := db.Put(context.Background(), "foo", map[string]string{"cat": "meow"}, mock.NilOption) - if err != nil { - t.Fatal(err) - } - }, - want: []rowResult{ - { - ID: "foo", - Rev: "1-274558516009acbe973682d27a58b598", - Value: `{"value":{"rev":"1-274558516009acbe973682d27a58b598"}}` + "\n", + tests.Add("single doc", func(t *testing.T) interface{} { + db := newDB(t) + _, err := db.Put(context.Background(), "foo", map[string]string{"cat": "meow"}, mock.NilOption) + if err != nil { + t.Fatal(err) + } + + return test{ + db: db, + want: []rowResult{ + { + ID: "foo", + Rev: "1-274558516009acbe973682d27a58b598", + Value: `{"value":{"rev":"1-274558516009acbe973682d27a58b598"}}` + "\n", + }, }, - }, + } }) - tests.Add("include_docs=true", test{ - setup: func(t *testing.T, db driver.DB) { - _, err := db.Put(context.Background(), "foo", map[string]string{"cat": "meow"}, mock.NilOption) - if err != nil { - t.Fatal(err) - } - }, - options: kivik.Param("include_docs", true), - want: []rowResult{ - { - ID: "foo", - Rev: "1-274558516009acbe973682d27a58b598", - Value: `{"value":{"rev":"1-274558516009acbe973682d27a58b598"}}` + "\n", - Doc: `{"_id":"foo","_rev":"1-274558516009acbe973682d27a58b598","cat":"meow"}`, + tests.Add("include_docs=true", func(t *testing.T) interface{} { + db := newDB(t) + _, err := db.Put(context.Background(), "foo", map[string]string{"cat": "meow"}, mock.NilOption) + if err != nil { + t.Fatal(err) + } + + return test{ + db: db, + options: kivik.Param("include_docs", true), + want: []rowResult{ + { + ID: "foo", + Rev: "1-274558516009acbe973682d27a58b598", + Value: `{"value":{"rev":"1-274558516009acbe973682d27a58b598"}}` + "\n", + Doc: `{"_id":"foo","_rev":"1-274558516009acbe973682d27a58b598","cat":"meow"}`, + }, }, - }, + } }) - tests.Add("single doc multiple revisions", test{ - setup: func(t *testing.T, db driver.DB) { - _, err := db.Put(context.Background(), "foo", map[string]string{"cat": "meow"}, mock.NilOption) - if err != nil { - t.Fatal(err) - } - _, err = db.Put(context.Background(), "foo", map[string]string{"cat": "purr"}, kivik.Rev("1-274558516009acbe973682d27a58b598")) - if err != nil { - t.Fatal(err) - } - }, - want: []rowResult{ - { - ID: "foo", - Rev: "2-c1f7f9ed8874502b095381186a35af4b", - Value: `{"value":{"rev":"2-c1f7f9ed8874502b095381186a35af4b"}}` + "\n", + tests.Add("single doc multiple revisions", func(t *testing.T) interface{} { + db := newDB(t) + _, err := db.Put(context.Background(), "foo", map[string]string{"cat": "meow"}, mock.NilOption) + if err != nil { + t.Fatal(err) + } + _, err = db.Put(context.Background(), "foo", map[string]string{"cat": "purr"}, kivik.Rev("1-274558516009acbe973682d27a58b598")) + if err != nil { + t.Fatal(err) + } + + return test{ + db: db, + want: []rowResult{ + { + ID: "foo", + Rev: "2-c1f7f9ed8874502b095381186a35af4b", + Value: `{"value":{"rev":"2-c1f7f9ed8874502b095381186a35af4b"}}` + "\n", + }, }, - }, + } }) - tests.Add("conflicting document, select winning rev", test{ - setup: func(t *testing.T, db driver.DB) { - _, err := db.Put(context.Background(), "foo", map[string]string{ - "cat": "meow", - "_rev": "1-xxx", - }, kivik.Param("new_edits", false)) - if err != nil { - t.Fatal(err) - } - _, err = db.Put(context.Background(), "foo", map[string]string{ - "cat": "purr", - "_rev": "1-aaa", - }, kivik.Param("new_edits", false)) - if err != nil { - t.Fatal(err) - } - }, - want: []rowResult{ - { - ID: "foo", - Rev: "1-xxx", - Value: `{"value":{"rev":"1-xxx"}}` + "\n", + tests.Add("conflicting document, select winning rev", func(t *testing.T) interface{} { + db := newDB(t) + _, err := db.Put(context.Background(), "foo", map[string]string{ + "cat": "meow", + "_rev": "1-xxx", + }, kivik.Param("new_edits", false)) + if err != nil { + t.Fatal(err) + } + _, err = db.Put(context.Background(), "foo", map[string]string{ + "cat": "purr", + "_rev": "1-aaa", + }, kivik.Param("new_edits", false)) + if err != nil { + t.Fatal(err) + } + + return test{ + db: db, + want: []rowResult{ + { + ID: "foo", + Rev: "1-xxx", + Value: `{"value":{"rev":"1-xxx"}}` + "\n", + }, }, - }, + } }) - tests.Add("deleted doc", test{ - setup: func(t *testing.T, db driver.DB) { - _, err := db.Put(context.Background(), "foo", map[string]string{"cat": "meow"}, mock.NilOption) - if err != nil { - t.Fatal(err) - } - _, err = db.Delete(context.Background(), "foo", kivik.Rev("1-274558516009acbe973682d27a58b598")) - if err != nil { - t.Fatal(err) - } - }, - want: nil, + tests.Add("deleted doc", func(t *testing.T) interface{} { + db := newDB(t) + _, err := db.Put(context.Background(), "foo", map[string]string{"cat": "meow"}, mock.NilOption) + if err != nil { + t.Fatal(err) + } + _, err = db.Delete(context.Background(), "foo", kivik.Rev("1-274558516009acbe973682d27a58b598")) + if err != nil { + t.Fatal(err) + } + + return test{ + db: db, + want: nil, + } }) - tests.Add("select lower revision number when higher rev in winning branch has been deleted", test{ - setup: func(t *testing.T, db driver.DB) { - _, err := db.Put(context.Background(), "foo", map[string]string{ - "cat": "meow", - "_rev": "1-xxx", - }, kivik.Param("new_edits", false)) - if err != nil { - t.Fatal(err) - } - _, err = db.Put(context.Background(), "foo", map[string]string{ - "cat": "purr", - "_rev": "1-aaa", - }, kivik.Param("new_edits", false)) - if err != nil { - t.Fatal(err) - } - _, err = db.Delete(context.Background(), "foo", kivik.Rev("1-aaa")) - if err != nil { - t.Fatal(err) - } - }, - want: []rowResult{ - { - ID: "foo", - Rev: "1-xxx", - Value: `{"value":{"rev":"1-xxx"}}` + "\n", + tests.Add("select lower revision number when higher rev in winning branch has been deleted", func(t *testing.T) interface{} { + db := newDB(t) + _, err := db.Put(context.Background(), "foo", map[string]string{ + "cat": "meow", + "_rev": "1-xxx", + }, kivik.Param("new_edits", false)) + if err != nil { + t.Fatal(err) + } + _, err = db.Put(context.Background(), "foo", map[string]string{ + "cat": "purr", + "_rev": "1-aaa", + }, kivik.Param("new_edits", false)) + if err != nil { + t.Fatal(err) + } + _, err = db.Delete(context.Background(), "foo", kivik.Rev("1-aaa")) + if err != nil { + t.Fatal(err) + } + + return test{ + db: db, + want: []rowResult{ + { + ID: "foo", + Rev: "1-xxx", + Value: `{"value":{"rev":"1-xxx"}}` + "\n", + }, }, - }, + } }) - tests.Add("conflicts=true", test{ - setup: func(t *testing.T, db driver.DB) { - _, err := db.Put(context.Background(), "foo", map[string]string{ - "cat": "meow", - "_rev": "1-xxx", - }, kivik.Param("new_edits", false)) - if err != nil { - t.Fatal(err) - } - _, err = db.Put(context.Background(), "foo", map[string]string{ - "cat": "purr", - "_rev": "1-aaa", - }, kivik.Param("new_edits", false)) - if err != nil { - t.Fatal(err) - } - }, - options: kivik.Params(map[string]interface{}{ - "conflicts": true, - "include_docs": true, - }), - want: []rowResult{ - { - ID: "foo", - Rev: "1-xxx", - Value: `{"value":{"rev":"1-xxx"}}` + "\n", - Doc: `{"_id":"foo","_rev":"1-xxx","cat":"meow","_conflicts":["1-aaa"]}`, + tests.Add("conflicts=true", func(t *testing.T) interface{} { + db := newDB(t) + _, err := db.Put(context.Background(), "foo", map[string]string{ + "cat": "meow", + "_rev": "1-xxx", + }, kivik.Param("new_edits", false)) + if err != nil { + t.Fatal(err) + } + _, err = db.Put(context.Background(), "foo", map[string]string{ + "cat": "purr", + "_rev": "1-aaa", + }, kivik.Param("new_edits", false)) + if err != nil { + t.Fatal(err) + } + + return test{ + db: db, + options: kivik.Params(map[string]interface{}{ + "conflicts": true, + "include_docs": true, + }), + want: []rowResult{ + { + ID: "foo", + Rev: "1-xxx", + Value: `{"value":{"rev":"1-xxx"}}` + "\n", + Doc: `{"_id":"foo","_rev":"1-xxx","cat":"meow","_conflicts":["1-aaa"]}`, + }, }, - }, + } }) - tests.Add("conflicts=true ignored without include_docs", test{ - setup: func(t *testing.T, db driver.DB) { - _, err := db.Put(context.Background(), "foo", map[string]string{ - "cat": "meow", - "_rev": "1-xxx", - }, kivik.Param("new_edits", false)) - if err != nil { - t.Fatal(err) - } - _, err = db.Put(context.Background(), "foo", map[string]string{ - "cat": "purr", - "_rev": "1-aaa", - }, kivik.Param("new_edits", false)) - if err != nil { - t.Fatal(err) - } - }, - options: kivik.Params(map[string]interface{}{ - "conflicts": true, - }), - want: []rowResult{ - { - ID: "foo", - Rev: "1-xxx", - Value: `{"value":{"rev":"1-xxx"}}` + "\n", + tests.Add("conflicts=true ignored without include_docs", func(t *testing.T) interface{} { + db := newDB(t) + _, err := db.Put(context.Background(), "foo", map[string]string{ + "cat": "meow", + "_rev": "1-xxx", + }, kivik.Param("new_edits", false)) + if err != nil { + t.Fatal(err) + } + _, err = db.Put(context.Background(), "foo", map[string]string{ + "cat": "purr", + "_rev": "1-aaa", + }, kivik.Param("new_edits", false)) + if err != nil { + t.Fatal(err) + } + + return test{ + db: db, + options: kivik.Params(map[string]interface{}{ + "conflicts": true, + }), + want: []rowResult{ + { + ID: "foo", + Rev: "1-xxx", + Value: `{"value":{"rev":"1-xxx"}}` + "\n", + }, }, - }, + } }) - tests.Add("default sorting", test{ - setup: func(t *testing.T, db driver.DB) { - _, err := db.Put(context.Background(), "cat", map[string]string{ - "cat": "meow", - }, mock.NilOption) - if err != nil { - t.Fatal(err) - } - _, err = db.Put(context.Background(), "dog", map[string]string{ - "dog": "woof", - }, mock.NilOption) - if err != nil { - t.Fatal(err) - } - _, err = db.Put(context.Background(), "cow", map[string]string{ - "cow": "moo", - }, mock.NilOption) - if err != nil { - t.Fatal(err) - } - }, - want: []rowResult{ - { - ID: "cat", - Rev: "1-274558516009acbe973682d27a58b598", - Value: `{"value":{"rev":"1-274558516009acbe973682d27a58b598"}}` + "\n", - }, - { - ID: "cow", - Rev: "1-80b1ed11e92f08613f0007cc2b2f486d", - Value: `{"value":{"rev":"1-80b1ed11e92f08613f0007cc2b2f486d"}}` + "\n", - }, - { - ID: "dog", - Rev: "1-a5f1dc478532231c6252f63fa94f433a", - Value: `{"value":{"rev":"1-a5f1dc478532231c6252f63fa94f433a"}}` + "\n", + tests.Add("default sorting", func(t *testing.T) interface{} { + db := newDB(t) + _, err := db.Put(context.Background(), "cat", map[string]string{ + "cat": "meow", + }, mock.NilOption) + if err != nil { + t.Fatal(err) + } + _, err = db.Put(context.Background(), "dog", map[string]string{ + "dog": "woof", + }, mock.NilOption) + if err != nil { + t.Fatal(err) + } + _, err = db.Put(context.Background(), "cow", map[string]string{ + "cow": "moo", + }, mock.NilOption) + if err != nil { + t.Fatal(err) + } + + return test{ + db: db, + want: []rowResult{ + { + ID: "cat", + Rev: "1-274558516009acbe973682d27a58b598", + Value: `{"value":{"rev":"1-274558516009acbe973682d27a58b598"}}` + "\n", + }, + { + ID: "cow", + Rev: "1-80b1ed11e92f08613f0007cc2b2f486d", + Value: `{"value":{"rev":"1-80b1ed11e92f08613f0007cc2b2f486d"}}` + "\n", + }, + { + ID: "dog", + Rev: "1-a5f1dc478532231c6252f63fa94f433a", + Value: `{"value":{"rev":"1-a5f1dc478532231c6252f63fa94f433a"}}` + "\n", + }, }, - }, + } }) - tests.Add("descending=true", test{ - setup: func(t *testing.T, db driver.DB) { - _, err := db.Put(context.Background(), "cat", map[string]string{ - "cat": "meow", - }, mock.NilOption) - if err != nil { - t.Fatal(err) - } - _, err = db.Put(context.Background(), "dog", map[string]string{ - "dog": "woof", - }, mock.NilOption) - if err != nil { - t.Fatal(err) - } - _, err = db.Put(context.Background(), "cow", map[string]string{ - "cow": "moo", - }, mock.NilOption) - if err != nil { - t.Fatal(err) - } - }, - options: kivik.Param("descending", true), - want: []rowResult{ - { - ID: "dog", - Rev: "1-a5f1dc478532231c6252f63fa94f433a", - Value: `{"value":{"rev":"1-a5f1dc478532231c6252f63fa94f433a"}}` + "\n", - }, - { - ID: "cow", - Rev: "1-80b1ed11e92f08613f0007cc2b2f486d", - Value: `{"value":{"rev":"1-80b1ed11e92f08613f0007cc2b2f486d"}}` + "\n", - }, - { - ID: "cat", - Rev: "1-274558516009acbe973682d27a58b598", - Value: `{"value":{"rev":"1-274558516009acbe973682d27a58b598"}}` + "\n", + tests.Add("descending=true", func(t *testing.T) interface{} { + db := newDB(t) + _, err := db.Put(context.Background(), "cat", map[string]string{ + "cat": "meow", + }, mock.NilOption) + if err != nil { + t.Fatal(err) + } + _, err = db.Put(context.Background(), "dog", map[string]string{ + "dog": "woof", + }, mock.NilOption) + if err != nil { + t.Fatal(err) + } + _, err = db.Put(context.Background(), "cow", map[string]string{ + "cow": "moo", + }, mock.NilOption) + if err != nil { + t.Fatal(err) + } + + return test{ + db: db, + options: kivik.Param("descending", true), + want: []rowResult{ + { + ID: "dog", + Rev: "1-a5f1dc478532231c6252f63fa94f433a", + Value: `{"value":{"rev":"1-a5f1dc478532231c6252f63fa94f433a"}}` + "\n", + }, + { + ID: "cow", + Rev: "1-80b1ed11e92f08613f0007cc2b2f486d", + Value: `{"value":{"rev":"1-80b1ed11e92f08613f0007cc2b2f486d"}}` + "\n", + }, + { + ID: "cat", + Rev: "1-274558516009acbe973682d27a58b598", + Value: `{"value":{"rev":"1-274558516009acbe973682d27a58b598"}}` + "\n", + }, }, - }, + } }) - tests.Add("endkey", test{ - setup: func(t *testing.T, db driver.DB) { - _, err := db.Put(context.Background(), "cat", map[string]string{ - "cat": "meow", - }, mock.NilOption) - if err != nil { - t.Fatal(err) - } - _, err = db.Put(context.Background(), "dog", map[string]string{ - "dog": "woof", - }, mock.NilOption) - if err != nil { - t.Fatal(err) - } - _, err = db.Put(context.Background(), "cow", map[string]string{ - "cow": "moo", - }, mock.NilOption) - if err != nil { - t.Fatal(err) - } - }, - options: kivik.Param("endkey", "cow"), - want: []rowResult{ - { - ID: "cat", - Rev: "1-274558516009acbe973682d27a58b598", - Value: `{"value":{"rev":"1-274558516009acbe973682d27a58b598"}}` + "\n", - }, - { - ID: "cow", - Rev: "1-80b1ed11e92f08613f0007cc2b2f486d", - Value: `{"value":{"rev":"1-80b1ed11e92f08613f0007cc2b2f486d"}}` + "\n", + tests.Add("endkey", func(t *testing.T) interface{} { + db := newDB(t) + _, err := db.Put(context.Background(), "cat", map[string]string{ + "cat": "meow", + }, mock.NilOption) + if err != nil { + t.Fatal(err) + } + _, err = db.Put(context.Background(), "dog", map[string]string{ + "dog": "woof", + }, mock.NilOption) + if err != nil { + t.Fatal(err) + } + _, err = db.Put(context.Background(), "cow", map[string]string{ + "cow": "moo", + }, mock.NilOption) + if err != nil { + t.Fatal(err) + } + + return test{ + db: db, + options: kivik.Param("endkey", "cow"), + want: []rowResult{ + { + ID: "cat", + Rev: "1-274558516009acbe973682d27a58b598", + Value: `{"value":{"rev":"1-274558516009acbe973682d27a58b598"}}` + "\n", + }, + { + ID: "cow", + Rev: "1-80b1ed11e92f08613f0007cc2b2f486d", + Value: `{"value":{"rev":"1-80b1ed11e92f08613f0007cc2b2f486d"}}` + "\n", + }, }, - }, + } }) - tests.Add("descending=true, endkey", test{ - setup: func(t *testing.T, db driver.DB) { - _, err := db.Put(context.Background(), "cat", map[string]string{ - "cat": "meow", - }, mock.NilOption) - if err != nil { - t.Fatal(err) - } - _, err = db.Put(context.Background(), "dog", map[string]string{ - "dog": "woof", - }, mock.NilOption) - if err != nil { - t.Fatal(err) - } - _, err = db.Put(context.Background(), "cow", map[string]string{ - "cow": "moo", - }, mock.NilOption) - if err != nil { - t.Fatal(err) - } - }, - options: kivik.Params(map[string]interface{}{ - "endkey": "cow", - "descending": true, - }), - want: []rowResult{ - { - ID: "dog", - Rev: "1-a5f1dc478532231c6252f63fa94f433a", - Value: `{"value":{"rev":"1-a5f1dc478532231c6252f63fa94f433a"}}` + "\n", - }, - { - ID: "cow", - Rev: "1-80b1ed11e92f08613f0007cc2b2f486d", - Value: `{"value":{"rev":"1-80b1ed11e92f08613f0007cc2b2f486d"}}` + "\n", + tests.Add("descending=true, endkey", func(t *testing.T) interface{} { + db := newDB(t) + _, err := db.Put(context.Background(), "cat", map[string]string{ + "cat": "meow", + }, mock.NilOption) + if err != nil { + t.Fatal(err) + } + _, err = db.Put(context.Background(), "dog", map[string]string{ + "dog": "woof", + }, mock.NilOption) + if err != nil { + t.Fatal(err) + } + _, err = db.Put(context.Background(), "cow", map[string]string{ + "cow": "moo", + }, mock.NilOption) + if err != nil { + t.Fatal(err) + } + + return test{ + db: db, + options: kivik.Params(map[string]interface{}{ + "endkey": "cow", + "descending": true, + }), + want: []rowResult{ + { + ID: "dog", + Rev: "1-a5f1dc478532231c6252f63fa94f433a", + Value: `{"value":{"rev":"1-a5f1dc478532231c6252f63fa94f433a"}}` + "\n", + }, + { + ID: "cow", + Rev: "1-80b1ed11e92f08613f0007cc2b2f486d", + Value: `{"value":{"rev":"1-80b1ed11e92f08613f0007cc2b2f486d"}}` + "\n", + }, }, - }, + } }) - tests.Add("end_key", test{ - setup: func(t *testing.T, db driver.DB) { - _, err := db.Put(context.Background(), "cat", map[string]string{ - "cat": "meow", - }, mock.NilOption) - if err != nil { - t.Fatal(err) - } - _, err = db.Put(context.Background(), "dog", map[string]string{ - "dog": "woof", - }, mock.NilOption) - if err != nil { - t.Fatal(err) - } - _, err = db.Put(context.Background(), "cow", map[string]string{ - "cow": "moo", - }, mock.NilOption) - if err != nil { - t.Fatal(err) - } - }, - options: kivik.Param("end_key", "cow"), - want: []rowResult{ - { - ID: "cat", - Rev: "1-274558516009acbe973682d27a58b598", - Value: `{"value":{"rev":"1-274558516009acbe973682d27a58b598"}}` + "\n", - }, - { - ID: "cow", - Rev: "1-80b1ed11e92f08613f0007cc2b2f486d", - Value: `{"value":{"rev":"1-80b1ed11e92f08613f0007cc2b2f486d"}}` + "\n", + tests.Add("end_key", func(t *testing.T) interface{} { + db := newDB(t) + _, err := db.Put(context.Background(), "cat", map[string]string{ + "cat": "meow", + }, mock.NilOption) + if err != nil { + t.Fatal(err) + } + _, err = db.Put(context.Background(), "dog", map[string]string{ + "dog": "woof", + }, mock.NilOption) + if err != nil { + t.Fatal(err) + } + _, err = db.Put(context.Background(), "cow", map[string]string{ + "cow": "moo", + }, mock.NilOption) + if err != nil { + t.Fatal(err) + } + + return test{ + db: db, + options: kivik.Param("end_key", "cow"), + want: []rowResult{ + { + ID: "cat", + Rev: "1-274558516009acbe973682d27a58b598", + Value: `{"value":{"rev":"1-274558516009acbe973682d27a58b598"}}` + "\n", + }, + { + ID: "cow", + Rev: "1-80b1ed11e92f08613f0007cc2b2f486d", + Value: `{"value":{"rev":"1-80b1ed11e92f08613f0007cc2b2f486d"}}` + "\n", + }, }, - }, + } }) - tests.Add("endkey, inclusive_end=false", test{ - setup: func(t *testing.T, db driver.DB) { - _, err := db.Put(context.Background(), "cat", map[string]string{ - "cat": "meow", - }, mock.NilOption) - if err != nil { - t.Fatal(err) - } - _, err = db.Put(context.Background(), "dog", map[string]string{ - "dog": "woof", - }, mock.NilOption) - if err != nil { - t.Fatal(err) - } - _, err = db.Put(context.Background(), "cow", map[string]string{ - "cow": "moo", - }, mock.NilOption) - if err != nil { - t.Fatal(err) - } - }, - options: kivik.Params(map[string]interface{}{ - "endkey": "cow", - "inclusive_end": false, - }), - want: []rowResult{ - { - ID: "cat", - Rev: "1-274558516009acbe973682d27a58b598", - Value: `{"value":{"rev":"1-274558516009acbe973682d27a58b598"}}` + "\n", + tests.Add("endkey, inclusive_end=false", func(t *testing.T) interface{} { + db := newDB(t) + _, err := db.Put(context.Background(), "cat", map[string]string{ + "cat": "meow", + }, mock.NilOption) + if err != nil { + t.Fatal(err) + } + _, err = db.Put(context.Background(), "dog", map[string]string{ + "dog": "woof", + }, mock.NilOption) + if err != nil { + t.Fatal(err) + } + _, err = db.Put(context.Background(), "cow", map[string]string{ + "cow": "moo", + }, mock.NilOption) + if err != nil { + t.Fatal(err) + } + + return test{ + db: db, + options: kivik.Params(map[string]interface{}{ + "endkey": "cow", + "inclusive_end": false, + }), + want: []rowResult{ + { + ID: "cat", + Rev: "1-274558516009acbe973682d27a58b598", + Value: `{"value":{"rev":"1-274558516009acbe973682d27a58b598"}}` + "\n", + }, }, - }, + } }) - tests.Add("startkey", test{ - setup: func(t *testing.T, db driver.DB) { - _, err := db.Put(context.Background(), "cat", map[string]string{ - "cat": "meow", - }, mock.NilOption) - if err != nil { - t.Fatal(err) - } - _, err = db.Put(context.Background(), "dog", map[string]string{ - "dog": "woof", - }, mock.NilOption) - if err != nil { - t.Fatal(err) - } - _, err = db.Put(context.Background(), "cow", map[string]string{ - "cow": "moo", - }, mock.NilOption) - if err != nil { - t.Fatal(err) - } - }, - options: kivik.Param("startkey", "cow"), - want: []rowResult{ - { - ID: "cow", - Rev: "1-80b1ed11e92f08613f0007cc2b2f486d", - Value: `{"value":{"rev":"1-80b1ed11e92f08613f0007cc2b2f486d"}}` + "\n", - }, - { - ID: "dog", - Rev: "1-a5f1dc478532231c6252f63fa94f433a", - Value: `{"value":{"rev":"1-a5f1dc478532231c6252f63fa94f433a"}}` + "\n", + tests.Add("startkey", func(t *testing.T) interface{} { + db := newDB(t) + _, err := db.Put(context.Background(), "cat", map[string]string{ + "cat": "meow", + }, mock.NilOption) + if err != nil { + t.Fatal(err) + } + _, err = db.Put(context.Background(), "dog", map[string]string{ + "dog": "woof", + }, mock.NilOption) + if err != nil { + t.Fatal(err) + } + _, err = db.Put(context.Background(), "cow", map[string]string{ + "cow": "moo", + }, mock.NilOption) + if err != nil { + t.Fatal(err) + } + + return test{ + db: db, + options: kivik.Param("startkey", "cow"), + want: []rowResult{ + { + ID: "cow", + Rev: "1-80b1ed11e92f08613f0007cc2b2f486d", + Value: `{"value":{"rev":"1-80b1ed11e92f08613f0007cc2b2f486d"}}` + "\n", + }, + { + ID: "dog", + Rev: "1-a5f1dc478532231c6252f63fa94f433a", + Value: `{"value":{"rev":"1-a5f1dc478532231c6252f63fa94f433a"}}` + "\n", + }, }, - }, + } }) - tests.Add("start_key", test{ - setup: func(t *testing.T, db driver.DB) { - _, err := db.Put(context.Background(), "cat", map[string]string{ - "cat": "meow", - }, mock.NilOption) - if err != nil { - t.Fatal(err) - } - _, err = db.Put(context.Background(), "dog", map[string]string{ - "dog": "woof", - }, mock.NilOption) - if err != nil { - t.Fatal(err) - } - _, err = db.Put(context.Background(), "cow", map[string]string{ - "cow": "moo", - }, mock.NilOption) - if err != nil { - t.Fatal(err) - } - }, - options: kivik.Param("start_key", "cow"), - want: []rowResult{ - { - ID: "cow", - Rev: "1-80b1ed11e92f08613f0007cc2b2f486d", - Value: `{"value":{"rev":"1-80b1ed11e92f08613f0007cc2b2f486d"}}` + "\n", - }, - { - ID: "dog", - Rev: "1-a5f1dc478532231c6252f63fa94f433a", - Value: `{"value":{"rev":"1-a5f1dc478532231c6252f63fa94f433a"}}` + "\n", + tests.Add("start_key", func(t *testing.T) interface{} { + db := newDB(t) + _, err := db.Put(context.Background(), "cat", map[string]string{ + "cat": "meow", + }, mock.NilOption) + if err != nil { + t.Fatal(err) + } + _, err = db.Put(context.Background(), "dog", map[string]string{ + "dog": "woof", + }, mock.NilOption) + if err != nil { + t.Fatal(err) + } + _, err = db.Put(context.Background(), "cow", map[string]string{ + "cow": "moo", + }, mock.NilOption) + if err != nil { + t.Fatal(err) + } + return test{ + db: db, + options: kivik.Param("start_key", "cow"), + want: []rowResult{ + { + ID: "cow", + Rev: "1-80b1ed11e92f08613f0007cc2b2f486d", + Value: `{"value":{"rev":"1-80b1ed11e92f08613f0007cc2b2f486d"}}` + "\n", + }, + { + ID: "dog", + Rev: "1-a5f1dc478532231c6252f63fa94f433a", + Value: `{"value":{"rev":"1-a5f1dc478532231c6252f63fa94f433a"}}` + "\n", + }, }, - }, + } }) - tests.Add("startkey, descending", test{ - setup: func(t *testing.T, db driver.DB) { - _, err := db.Put(context.Background(), "cat", map[string]string{ - "cat": "meow", - }, mock.NilOption) - if err != nil { - t.Fatal(err) - } - _, err = db.Put(context.Background(), "dog", map[string]string{ - "dog": "woof", - }, mock.NilOption) - if err != nil { - t.Fatal(err) - } - _, err = db.Put(context.Background(), "cow", map[string]string{ - "cow": "moo", - }, mock.NilOption) - if err != nil { - t.Fatal(err) - } - }, - options: kivik.Params(map[string]interface{}{ - "startkey": "cow", - "descending": true, - }), - want: []rowResult{ - { - ID: "cow", - Rev: "1-80b1ed11e92f08613f0007cc2b2f486d", - Value: `{"value":{"rev":"1-80b1ed11e92f08613f0007cc2b2f486d"}}` + "\n", - }, - { - ID: "cat", - Rev: "1-274558516009acbe973682d27a58b598", - Value: `{"value":{"rev":"1-274558516009acbe973682d27a58b598"}}` + "\n", + tests.Add("startkey, descending", func(t *testing.T) interface{} { + db := newDB(t) + _, err := db.Put(context.Background(), "cat", map[string]string{ + "cat": "meow", + }, mock.NilOption) + if err != nil { + t.Fatal(err) + } + _, err = db.Put(context.Background(), "dog", map[string]string{ + "dog": "woof", + }, mock.NilOption) + if err != nil { + t.Fatal(err) + } + _, err = db.Put(context.Background(), "cow", map[string]string{ + "cow": "moo", + }, mock.NilOption) + if err != nil { + t.Fatal(err) + } + + return test{ + db: db, + options: kivik.Params(map[string]interface{}{ + "startkey": "cow", + "descending": true, + }), + want: []rowResult{ + { + ID: "cow", + Rev: "1-80b1ed11e92f08613f0007cc2b2f486d", + Value: `{"value":{"rev":"1-80b1ed11e92f08613f0007cc2b2f486d"}}` + "\n", + }, + { + ID: "cat", + Rev: "1-274558516009acbe973682d27a58b598", + Value: `{"value":{"rev":"1-274558516009acbe973682d27a58b598"}}` + "\n", + }, }, - }, + } }) /* @@ -586,11 +636,11 @@ func TestDBAllDocs(t *testing.T) { tests.Run(t, func(t *testing.T, tt test) { t.Parallel() - db := newDB(t) - opts := tt.options - if tt.setup != nil { - tt.setup(t, db) + db := tt.db + if db == nil { + db = newDB(t) } + opts := tt.options if opts == nil { opts = mock.NilOption }