github.com/go-kivik/kivik/v4@v4.3.2/bulk_test.go (about) 1 // Licensed under the Apache License, Version 2.0 (the "License"); you may not 2 // use this file except in compliance with the License. You may obtain a copy of 3 // the License at 4 // 5 // http://www.apache.org/licenses/LICENSE-2.0 6 // 7 // Unless required by applicable law or agreed to in writing, software 8 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 9 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 10 // License for the specific language governing permissions and limitations under 11 // the License. 12 13 package kivik 14 15 import ( 16 "context" 17 "encoding/json" 18 "errors" 19 "fmt" 20 "net/http" 21 "testing" 22 23 "gitlab.com/flimzy/testy" 24 25 "github.com/go-kivik/kivik/v4/driver" 26 internal "github.com/go-kivik/kivik/v4/int/errors" 27 "github.com/go-kivik/kivik/v4/int/mock" 28 ) 29 30 func TestDocsInterfaceSlice(t *testing.T) { 31 type diTest struct { 32 name string 33 input []interface{} 34 expected interface{} 35 status int 36 err string 37 } 38 tests := []diTest{ 39 { 40 name: "InterfaceSlice", 41 input: []interface{}{map[string]string{"foo": "bar"}}, 42 expected: []interface{}{map[string]string{"foo": "bar"}}, 43 }, 44 { 45 name: "JSONDoc", 46 input: []interface{}{ 47 map[string]string{"foo": "bar"}, 48 json.RawMessage(`{"foo":"bar"}`), 49 }, 50 expected: []interface{}{ 51 map[string]string{"foo": "bar"}, 52 map[string]string{"foo": "bar"}, 53 }, 54 }, 55 } 56 for _, test := range tests { 57 func(test diTest) { 58 t.Run(test.name, func(t *testing.T) { 59 result, err := docsInterfaceSlice(test.input) 60 if d := internal.StatusErrorDiff(test.err, test.status, err); d != "" { 61 t.Error(d) 62 } 63 if d := testy.DiffAsJSON(test.expected, result); d != nil { 64 t.Errorf("%s", d) 65 } 66 }) 67 }(test) 68 } 69 } 70 71 func TestBulkDocs(t *testing.T) { // nolint: gocyclo 72 type tt struct { 73 db *DB 74 docs []interface{} 75 options Option 76 expected []BulkResult 77 status int 78 err string 79 } 80 81 tests := testy.NewTable() 82 tests.Add("invalid JSON", tt{ 83 db: &DB{ 84 client: &Client{}, 85 driverDB: &mock.BulkDocer{ 86 BulkDocsFunc: func(_ context.Context, docs []interface{}, _ driver.Options) ([]driver.BulkResult, error) { 87 _, err := json.Marshal(docs) 88 return nil, err 89 }, 90 }, 91 }, 92 docs: []interface{}{json.RawMessage("invalid json")}, 93 status: http.StatusInternalServerError, 94 err: "json: error calling MarshalJSON for type json.RawMessage: invalid character 'i' looking for beginning of value", 95 }) 96 tests.Add("emulated BulkDocs support", tt{ 97 db: &DB{ 98 client: &Client{}, 99 driverDB: &mock.DocCreator{ 100 DB: mock.DB{ 101 PutFunc: func(_ context.Context, docID string, doc interface{}, options driver.Options) (string, error) { 102 if docID == "error" { 103 return "", errors.New("error") 104 } 105 if docID != "foo" { // nolint: goconst 106 return "", fmt.Errorf("Unexpected docID: %s", docID) 107 } 108 expectedDoc := map[string]string{"_id": "foo"} 109 if d := testy.DiffInterface(expectedDoc, doc); d != nil { 110 return "", fmt.Errorf("Unexpected doc:\n%s", d) 111 } 112 gotOpts := map[string]interface{}{} 113 options.Apply(gotOpts) 114 if d := testy.DiffInterface(testOptions, gotOpts); d != nil { 115 return "", fmt.Errorf("Unexpected opts:\n%s", d) 116 } 117 return "2-xxx", nil // nolint: goconst 118 }, 119 }, 120 CreateDocFunc: func(_ context.Context, doc interface{}, options driver.Options) (string, string, error) { 121 gotOpts := map[string]interface{}{} 122 options.Apply(gotOpts) 123 expectedDoc := int(123) 124 if d := testy.DiffInterface(expectedDoc, doc); d != nil { 125 return "", "", fmt.Errorf("Unexpected doc:\n%s", d) 126 } 127 if d := testy.DiffInterface(testOptions, gotOpts); d != nil { 128 return "", "", fmt.Errorf("Unexpected opts:\n%s", d) 129 } 130 return "newDocID", "1-xxx", nil // nolint: goconst 131 }, 132 }, 133 }, 134 docs: []interface{}{ 135 map[string]string{"_id": "foo"}, 136 123, 137 map[string]string{"_id": "error"}, 138 }, 139 options: Params(testOptions), 140 expected: []BulkResult{ 141 {ID: "foo", Rev: "2-xxx"}, 142 {ID: "newDocID", Rev: "1-xxx"}, 143 {ID: "error", Error: errors.New("error")}, 144 }, 145 }) 146 tests.Add("new_edits", tt{ 147 db: &DB{ 148 client: &Client{}, 149 driverDB: &mock.BulkDocer{ 150 BulkDocsFunc: func(_ context.Context, docs []interface{}, options driver.Options) ([]driver.BulkResult, error) { 151 expectedDocs := []interface{}{map[string]string{"_id": "foo"}, 123} 152 wantOpts := map[string]interface{}{"new_edits": true} 153 gotOpts := map[string]interface{}{} 154 options.Apply(gotOpts) 155 if d := testy.DiffInterface(expectedDocs, docs); d != nil { 156 return nil, fmt.Errorf("Unexpected docs:\n%s", d) 157 } 158 if d := testy.DiffInterface(wantOpts, gotOpts); d != nil { 159 return nil, fmt.Errorf("Unexpected opts:\n%s", d) 160 } 161 return []driver.BulkResult{ 162 {ID: "foo"}, 163 }, nil 164 }, 165 }, 166 }, 167 docs: []interface{}{ 168 map[string]string{"_id": "foo"}, 169 123, 170 }, 171 options: Param("new_edits", true), 172 expected: []BulkResult{ 173 {ID: "foo"}, 174 }, 175 }) 176 tests.Add("client closed", tt{ 177 db: &DB{ 178 client: &Client{ 179 closed: true, 180 }, 181 }, 182 docs: []interface{}{ 183 map[string]string{"_id": "foo"}, 184 }, 185 status: http.StatusServiceUnavailable, 186 err: "kivik: client closed", 187 }) 188 tests.Add("db error", tt{ 189 db: &DB{ 190 err: errors.New("db error"), 191 }, 192 status: http.StatusInternalServerError, 193 err: "db error", 194 }) 195 tests.Add("unreadable doc", tt{ 196 db: &DB{ 197 client: &Client{}, 198 driverDB: &mock.BulkDocer{ 199 BulkDocsFunc: func(_ context.Context, docs []interface{}, _ driver.Options) ([]driver.BulkResult, error) { 200 _, err := json.Marshal(docs) 201 return nil, err 202 }, 203 }, 204 }, 205 docs: []interface{}{testy.ErrorReader("", errors.New("read error"))}, 206 status: http.StatusBadRequest, 207 err: "read error", 208 }) 209 tests.Add("no docs", tt{ 210 db: &DB{ 211 client: &Client{}, 212 driverDB: &mock.BulkDocer{ 213 BulkDocsFunc: func(_ context.Context, docs []interface{}, _ driver.Options) ([]driver.BulkResult, error) { 214 _, err := json.Marshal(docs) 215 return nil, err 216 }, 217 }, 218 }, 219 docs: []interface{}{}, 220 status: http.StatusBadRequest, 221 err: "kivik: no documents provided", 222 }) 223 224 tests.Run(t, func(t *testing.T, tt tt) { 225 result, err := tt.db.BulkDocs(context.Background(), tt.docs, tt.options) 226 if d := internal.StatusErrorDiff(tt.err, tt.status, err); d != "" { 227 t.Error(d) 228 } 229 if d := testy.DiffInterface(tt.expected, result); d != nil { 230 t.Error(d) 231 } 232 }) 233 }