github.com/go-kivik/kivik/v4@v4.3.2/couchdb/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 couchdb 14 15 import ( 16 "context" 17 "encoding/json" 18 "errors" 19 "io" 20 "net/http" 21 "strings" 22 "testing" 23 24 kivik "github.com/go-kivik/kivik/v4" 25 internal "github.com/go-kivik/kivik/v4/int/errors" 26 "github.com/go-kivik/kivik/v4/int/mock" 27 ) 28 29 func TestBulkDocs(t *testing.T) { 30 tests := []struct { 31 name string 32 db *db 33 docs []interface{} 34 options kivik.Option 35 status int 36 err string 37 }{ 38 { 39 name: "network error", 40 db: newTestDB(nil, errors.New("net error")), 41 status: http.StatusBadGateway, 42 err: `Post "?http://example.com/testdb/_bulk_docs"?: net error`, 43 }, 44 { 45 name: "JSON encoding error", 46 db: newTestDB(&http.Response{ 47 StatusCode: http.StatusOK, 48 Body: io.NopCloser(strings.NewReader("")), 49 }, nil), 50 docs: []interface{}{make(chan int)}, 51 status: http.StatusBadRequest, 52 err: `Post "?http://example.com/testdb/_bulk_docs"?: json: unsupported type: chan int`, 53 }, 54 { 55 name: "docs rejected", 56 db: newTestDB(&http.Response{ 57 StatusCode: http.StatusExpectationFailed, 58 Body: io.NopCloser(strings.NewReader("[]")), 59 }, nil), 60 docs: []interface{}{1, 2, 3}, 61 status: http.StatusExpectationFailed, 62 err: "Expectation Failed: one or more document was rejected", 63 }, 64 { 65 name: "error response", 66 db: newTestDB(&http.Response{ 67 StatusCode: http.StatusBadRequest, 68 Body: io.NopCloser(strings.NewReader("")), 69 }, nil), 70 docs: []interface{}{1, 2, 3}, 71 status: http.StatusBadRequest, 72 err: "Bad Request", 73 }, 74 { 75 name: "invalid JSON response", 76 db: newTestDB(&http.Response{ 77 StatusCode: http.StatusCreated, 78 Body: io.NopCloser(strings.NewReader("invalid json")), 79 }, nil), 80 docs: []interface{}{1, 2, 3}, 81 status: http.StatusBadGateway, 82 err: "invalid character 'i' looking for beginning of value", 83 }, 84 { 85 name: "unexpected response code", 86 db: newTestDB(&http.Response{ 87 StatusCode: http.StatusOK, 88 Body: io.NopCloser(strings.NewReader("[]")), 89 }, nil), 90 docs: []interface{}{1, 2, 3}, 91 }, 92 { 93 name: "new_edits", 94 options: kivik.Param("new_edits", true), 95 db: newCustomDB(func(req *http.Request) (*http.Response, error) { 96 defer req.Body.Close() // nolint: errcheck 97 var body struct { 98 NewEdits bool `json:"new_edits"` 99 } 100 if err := json.NewDecoder(req.Body).Decode(&body); err != nil { 101 return nil, err 102 } 103 if !body.NewEdits { 104 return nil, errors.New("`new_edits` not set") 105 } 106 return &http.Response{ 107 StatusCode: http.StatusCreated, 108 Body: io.NopCloser(strings.NewReader("[]")), 109 }, nil 110 }), 111 }, 112 { 113 name: "full commit", 114 options: OptionFullCommit(), 115 db: newCustomDB(func(req *http.Request) (*http.Response, error) { 116 defer req.Body.Close() // nolint: errcheck 117 var body map[string]interface{} 118 if err := json.NewDecoder(req.Body).Decode(&body); err != nil { 119 return nil, err 120 } 121 if value := req.Header.Get("X-Couch-Full-Commit"); value != "true" { 122 return nil, errors.New("X-Couch-Full-Commit not set to true") 123 } 124 return &http.Response{ 125 StatusCode: http.StatusCreated, 126 Body: io.NopCloser(strings.NewReader("[]")), 127 }, nil 128 }), 129 }, 130 } 131 for _, test := range tests { 132 t.Run(test.name, func(t *testing.T) { 133 opts := test.options 134 if opts == nil { 135 opts = mock.NilOption 136 } 137 _, err := test.db.BulkDocs(context.Background(), test.docs, opts) 138 if d := internal.StatusErrorDiffRE(test.err, test.status, err); d != "" { 139 t.Error(d) 140 } 141 }) 142 } 143 } 144 145 type closeTracker struct { 146 closed bool 147 io.ReadCloser 148 } 149 150 func (c *closeTracker) Close() error { 151 c.closed = true 152 return c.ReadCloser.Close() 153 }