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  }