...

Source file src/github.com/go-kivik/kivik/v4/couchdb/dbstats_test.go

Documentation: github.com/go-kivik/kivik/v4/couchdb

     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  	"errors"
    18  	"io"
    19  	"net/http"
    20  	"strings"
    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  )
    28  
    29  func TestStats(t *testing.T) {
    30  	tests := []struct {
    31  		name     string
    32  		db       *db
    33  		expected *driver.DBStats
    34  		status   int
    35  		err      string
    36  	}{
    37  		{
    38  			name:   "network error",
    39  			db:     newTestDB(nil, errors.New("net error")),
    40  			status: http.StatusBadGateway,
    41  			err:    `Get "?http://example.com/testdb"?: net error`,
    42  		},
    43  		{
    44  			name: "read error",
    45  			db: newTestDB(&http.Response{
    46  				StatusCode: http.StatusOK,
    47  				Body: &mockReadCloser{
    48  					ReadFunc: func(_ []byte) (int, error) {
    49  						return 0, errors.New("read error")
    50  					},
    51  					CloseFunc: func() error { return nil },
    52  				},
    53  			}, nil),
    54  			status: http.StatusBadGateway,
    55  			err:    "read error",
    56  		},
    57  		{
    58  			name: "invalid JSON response",
    59  			db: newTestDB(&http.Response{
    60  				StatusCode: http.StatusOK,
    61  				Body:       io.NopCloser(strings.NewReader(`invalid json`)),
    62  			}, nil),
    63  			status: http.StatusBadGateway,
    64  			err:    "invalid character 'i' looking for beginning of value",
    65  		},
    66  		{
    67  			name: "error response",
    68  			db: newTestDB(&http.Response{
    69  				StatusCode: http.StatusBadRequest,
    70  				Body:       io.NopCloser(strings.NewReader("")),
    71  			}, nil),
    72  			status: http.StatusBadRequest,
    73  			err:    "Bad Request",
    74  		},
    75  		{
    76  			name: "1.6.1",
    77  			db: newTestDB(&http.Response{
    78  				StatusCode: http.StatusOK,
    79  				Header: http.Header{
    80  					"Server":         {"CouchDB/1.6.1 (Erlang OTP/17)"},
    81  					"Date":           {"Thu, 26 Oct 2017 12:58:14 GMT"},
    82  					"Content-Type":   {"text/plain; charset=utf-8"},
    83  					"Content-Length": {"235"},
    84  					"Cache-Control":  {"must-revalidate"},
    85  				},
    86  				Body: io.NopCloser(strings.NewReader(`{"db_name":"_users","doc_count":3,"doc_del_count":14,"update_seq":31,"purge_seq":0,"compact_running":false,"disk_size":127080,"data_size":6028,"instance_start_time":"1509022681259533","disk_format_version":6,"committed_update_seq":31}`)),
    87  			}, nil),
    88  			expected: &driver.DBStats{
    89  				Name:         "_users",
    90  				DocCount:     3,
    91  				DeletedCount: 14,
    92  				UpdateSeq:    "31",
    93  				DiskSize:     127080,
    94  				ActiveSize:   6028,
    95  				RawResponse:  []byte(`{"db_name":"_users","doc_count":3,"doc_del_count":14,"update_seq":31,"purge_seq":0,"compact_running":false,"disk_size":127080,"data_size":6028,"instance_start_time":"1509022681259533","disk_format_version":6,"committed_update_seq":31}`),
    96  			},
    97  		},
    98  		{
    99  			name: "2.0.0",
   100  			db: newTestDB(&http.Response{
   101  				StatusCode: http.StatusOK,
   102  				Header: http.Header{
   103  					"Server":              {"CouchDB/2.0.0 (Erlang OTP/17)"},
   104  					"Date":                {"Thu, 26 Oct 2017 13:01:13 GMT"},
   105  					"Content-Type":        {"application/json"},
   106  					"Content-Length":      {"429"},
   107  					"Cache-Control":       {"must-revalidate"},
   108  					"X-Couch-Request-ID":  {"2486f27546"},
   109  					"X-CouchDB-Body-Time": {"0"},
   110  				},
   111  				Body: io.NopCloser(strings.NewReader(`{"db_name":"_users","update_seq":"13-g1AAAAEzeJzLYWBg4MhgTmHgzcvPy09JdcjLz8gvLskBCjMlMiTJ____PyuRAYeCJAUgmWQPVsOCS40DSE08WA0rLjUJIDX1eO3KYwGSDA1ACqhsPiF1CyDq9mclMuFVdwCi7j4hdQ8g6kDuywIAkRBjAw","sizes":{"file":87323,"external":2495,"active":6082},"purge_seq":0,"other":{"data_size":2495},"doc_del_count":6,"doc_count":1,"disk_size":87323,"disk_format_version":6,"data_size":6082,"compact_running":false,"instance_start_time":"0"}`)),
   112  			}, nil),
   113  			expected: &driver.DBStats{
   114  				Name:         "_users",
   115  				DocCount:     1,
   116  				DeletedCount: 6,
   117  				UpdateSeq:    "13-g1AAAAEzeJzLYWBg4MhgTmHgzcvPy09JdcjLz8gvLskBCjMlMiTJ____PyuRAYeCJAUgmWQPVsOCS40DSE08WA0rLjUJIDX1eO3KYwGSDA1ACqhsPiF1CyDq9mclMuFVdwCi7j4hdQ8g6kDuywIAkRBjAw",
   118  				DiskSize:     87323,
   119  				ActiveSize:   6082,
   120  				ExternalSize: 2495,
   121  				RawResponse:  []byte(`{"db_name":"_users","update_seq":"13-g1AAAAEzeJzLYWBg4MhgTmHgzcvPy09JdcjLz8gvLskBCjMlMiTJ____PyuRAYeCJAUgmWQPVsOCS40DSE08WA0rLjUJIDX1eO3KYwGSDA1ACqhsPiF1CyDq9mclMuFVdwCi7j4hdQ8g6kDuywIAkRBjAw","sizes":{"file":87323,"external":2495,"active":6082},"purge_seq":0,"other":{"data_size":2495},"doc_del_count":6,"doc_count":1,"disk_size":87323,"disk_format_version":6,"data_size":6082,"compact_running":false,"instance_start_time":"0"}`),
   122  			},
   123  		},
   124  		{
   125  			name: "2.1.1",
   126  			db: newTestDB(&http.Response{
   127  				StatusCode: http.StatusOK,
   128  				Header: http.Header{
   129  					"Server":              {"CouchDB/2.0.0 (Erlang OTP/17)"},
   130  					"Date":                {"Thu, 26 Oct 2017 13:01:13 GMT"},
   131  					"Content-Type":        {"application/json"},
   132  					"Content-Length":      {"429"},
   133  					"Cache-Control":       {"must-revalidate"},
   134  					"X-Couch-Request-ID":  {"2486f27546"},
   135  					"X-CouchDB-Body-Time": {"0"},
   136  				},
   137  				Body: io.NopCloser(strings.NewReader(`{"db_name":"_users","update_seq":"13-g1AAAAEzeJzLYWBg4MhgTmHgzcvPy09JdcjLz8gvLskBCjMlMiTJ____PyuRAYeCJAUgmWQPVsOCS40DSE08WA0rLjUJIDX1eO3KYwGSDA1ACqhsPiF1CyDq9mclMuFVdwCi7j4hdQ8g6kDuywIAkRBjAw","sizes":{"file":87323,"external":2495,"active":6082},"purge_seq":0,"other":{"data_size":2495},"doc_del_count":6,"doc_count":1,"disk_size":87323,"disk_format_version":6,"data_size":6082,"compact_running":false,"instance_start_time":"0","cluster":{"n":1,"q":2,"r":3,"w":4}}`)),
   138  			}, nil),
   139  			expected: &driver.DBStats{
   140  				Name:         "_users",
   141  				DocCount:     1,
   142  				DeletedCount: 6,
   143  				UpdateSeq:    "13-g1AAAAEzeJzLYWBg4MhgTmHgzcvPy09JdcjLz8gvLskBCjMlMiTJ____PyuRAYeCJAUgmWQPVsOCS40DSE08WA0rLjUJIDX1eO3KYwGSDA1ACqhsPiF1CyDq9mclMuFVdwCi7j4hdQ8g6kDuywIAkRBjAw",
   144  				DiskSize:     87323,
   145  				ActiveSize:   6082,
   146  				ExternalSize: 2495,
   147  				Cluster: &driver.ClusterStats{
   148  					Replicas:    1,
   149  					Shards:      2,
   150  					ReadQuorum:  3,
   151  					WriteQuorum: 4,
   152  				},
   153  				RawResponse: []byte(`{"db_name":"_users","update_seq":"13-g1AAAAEzeJzLYWBg4MhgTmHgzcvPy09JdcjLz8gvLskBCjMlMiTJ____PyuRAYeCJAUgmWQPVsOCS40DSE08WA0rLjUJIDX1eO3KYwGSDA1ACqhsPiF1CyDq9mclMuFVdwCi7j4hdQ8g6kDuywIAkRBjAw","sizes":{"file":87323,"external":2495,"active":6082},"purge_seq":0,"other":{"data_size":2495},"doc_del_count":6,"doc_count":1,"disk_size":87323,"disk_format_version":6,"data_size":6082,"compact_running":false,"instance_start_time":"0","cluster":{"n":1,"q":2,"r":3,"w":4}}`),
   154  			},
   155  		},
   156  	}
   157  	for _, test := range tests {
   158  		t.Run(test.name, func(t *testing.T) {
   159  			result, err := test.db.Stats(context.Background())
   160  			if d := internal.StatusErrorDiffRE(test.err, test.status, err); d != "" {
   161  				t.Error(d)
   162  			}
   163  			if d := testy.DiffInterface(test.expected, result); d != nil {
   164  				t.Error(d)
   165  			}
   166  		})
   167  	}
   168  }
   169  
   170  func TestDbsStats(t *testing.T) {
   171  	tests := []struct {
   172  		name     string
   173  		client   *client
   174  		dbnames  []string
   175  		expected interface{}
   176  		status   int
   177  		err      string
   178  	}{
   179  		{
   180  			name:    "network error",
   181  			client:  newTestClient(nil, errors.New("net error")),
   182  			dbnames: []string{"foo", "bar"},
   183  			status:  http.StatusBadGateway,
   184  			err:     `Post "?http://example.com/_dbs_info"?: net error`,
   185  		},
   186  		{
   187  			name: "read error",
   188  			client: newTestClient(&http.Response{
   189  				StatusCode: http.StatusOK,
   190  				Body: &mockReadCloser{
   191  					ReadFunc: func(_ []byte) (int, error) {
   192  						return 0, errors.New("read error")
   193  					},
   194  					CloseFunc: func() error { return nil },
   195  				},
   196  			}, nil),
   197  			status: http.StatusBadGateway,
   198  			err:    "read error",
   199  		},
   200  		{
   201  			name: "invalid JSON response",
   202  			client: newTestClient(&http.Response{
   203  				StatusCode: http.StatusOK,
   204  				Body:       io.NopCloser(strings.NewReader(`invalid json`)),
   205  			}, nil),
   206  			status: http.StatusBadGateway,
   207  			err:    "invalid character 'i' looking for beginning of value",
   208  		},
   209  		{
   210  			name: "error response",
   211  			client: newTestClient(&http.Response{
   212  				StatusCode: http.StatusBadRequest,
   213  				Body:       io.NopCloser(strings.NewReader("")),
   214  			}, nil),
   215  			status: http.StatusBadRequest,
   216  			err:    "Bad Request",
   217  		},
   218  		{
   219  			name: "2.1.2",
   220  			client: newTestClient(&http.Response{
   221  				StatusCode: http.StatusNotFound,
   222  				Header: http.Header{
   223  					"Server":              {"CouchDB/2.1.2 (Erlang OTP/17)"},
   224  					"Date":                {"Sat, 01 Sep 2018 15:42:53 GMT"},
   225  					"Content-Type":        {"application/json"},
   226  					"Content-Length":      {"58"},
   227  					"Cache-Control":       {"must-revalidate"},
   228  					"X-Couch-Request-ID":  {"e1264663f9"},
   229  					"X-CouchDB-Body-Time": {"0"},
   230  				},
   231  				Body: io.NopCloser(strings.NewReader(`{"error":"not_found","reason":"Database does not exist."}`)),
   232  			}, nil),
   233  			dbnames: []string{"foo", "bar"},
   234  			err:     "Not Found",
   235  			status:  http.StatusNotFound,
   236  		},
   237  		{
   238  			name: "2.2.0",
   239  			client: newTestClient(&http.Response{
   240  				StatusCode: http.StatusOK,
   241  				Header: http.Header{
   242  					"Server":              {"CouchDB/2.2.0 (Erlang OTP/19)"},
   243  					"Date":                {"Sat, 01 Sep 2018 15:50:56 GMT"},
   244  					"Content-Type":        {"application/json"},
   245  					"Transfer-Encoding":   {"chunked"},
   246  					"Cache-Control":       {"must-revalidate"},
   247  					"X-Couch-Request-ID":  {"1bf258cfbe"},
   248  					"X-CouchDB-Body-Time": {"0"},
   249  				},
   250  				Body: io.NopCloser(strings.NewReader(`[{"key":"foo","error":"not_found"},{"key":"bar","error":"not_found"},{"key":"_users","info":{"db_name":"_users","update_seq":"1-g1AAAAEzeJzLYWBg4MhgTmHgzcvPy09JdcjLz8gvLskBCjMlMiTJ____PyuRAYeCJAUgmWSPX40DSE08WA0jLjUJIDX1eM3JYwGSDA1ACqhsPiF1CyDq9hNSdwCi7j4hdQ8g6kDuywIAiVhi9w","sizes":{"file":24423,"external":5361,"active":2316},"purge_seq":0,"other":{"data_size":5361},"doc_del_count":0,"doc_count":1,"disk_size":24423,"disk_format_version":6,"data_size":2316,"compact_running":false,"cluster":{"q":8,"n":1,"w":1,"r":1},"instance_start_time":"0"}}]
   251  `)),
   252  			}, nil),
   253  			expected: []*driver.DBStats{
   254  				nil,
   255  				nil,
   256  				{
   257  					Name:         "_users",
   258  					DocCount:     1,
   259  					UpdateSeq:    "1-g1AAAAEzeJzLYWBg4MhgTmHgzcvPy09JdcjLz8gvLskBCjMlMiTJ____PyuRAYeCJAUgmWSPX40DSE08WA0jLjUJIDX1eM3JYwGSDA1ACqhsPiF1CyDq9hNSdwCi7j4hdQ8g6kDuywIAiVhi9w",
   260  					DiskSize:     24423,
   261  					ActiveSize:   2316,
   262  					ExternalSize: 5361,
   263  					Cluster: &driver.ClusterStats{
   264  						Replicas:    1,
   265  						Shards:      8,
   266  						ReadQuorum:  1,
   267  						WriteQuorum: 1,
   268  					},
   269  					RawResponse: []byte(`{"db_name":"_users","update_seq":"1-g1AAAAEzeJzLYWBg4MhgTmHgzcvPy09JdcjLz8gvLskBCjMlMiTJ____PyuRAYeCJAUgmWSPX40DSE08WA0jLjUJIDX1eM3JYwGSDA1ACqhsPiF1CyDq9hNSdwCi7j4hdQ8g6kDuywIAiVhi9w","sizes":{"file":24423,"external":5361,"active":2316},"purge_seq":0,"other":{"data_size":5361},"doc_del_count":0,"doc_count":1,"disk_size":24423,"disk_format_version":6,"data_size":2316,"compact_running":false,"cluster":{"q":8,"n":1,"w":1,"r":1},"instance_start_time":"0"}`),
   270  				},
   271  			},
   272  		},
   273  	}
   274  	for _, test := range tests {
   275  		t.Run(test.name, func(t *testing.T) {
   276  			result, err := test.client.DBsStats(context.Background(), test.dbnames)
   277  			if d := internal.StatusErrorDiffRE(test.err, test.status, err); d != "" {
   278  				t.Error(d)
   279  			}
   280  			if err != nil {
   281  				return
   282  			}
   283  			if d := testy.DiffInterface(test.expected, result); d != nil {
   284  				t.Error(d)
   285  			}
   286  		})
   287  	}
   288  }
   289  
   290  func TestPartitionStats(t *testing.T) {
   291  	if isGopherJS {
   292  		t.Skip("https://github.com/gopherjs/gopherjs/issues/1228")
   293  	}
   294  	type tt struct {
   295  		db     *db
   296  		name   string
   297  		status int
   298  		err    string
   299  	}
   300  
   301  	tests := testy.NewTable()
   302  	tests.Add("network error", tt{
   303  		db:     newTestDB(nil, errors.New("net error")),
   304  		name:   "partXX",
   305  		status: http.StatusBadGateway,
   306  		err:    `Get "?http://example.com/testdb/_partition/partXX"?: net error`,
   307  	})
   308  	tests.Add("read error", tt{
   309  		db: newTestDB(&http.Response{
   310  			StatusCode: http.StatusOK,
   311  			Body: &mockReadCloser{
   312  				ReadFunc: func(_ []byte) (int, error) {
   313  					return 0, errors.New("read error")
   314  				},
   315  				CloseFunc: func() error { return nil },
   316  			},
   317  		}, nil),
   318  		status: http.StatusBadGateway,
   319  		err:    "read error",
   320  	})
   321  	tests.Add("invalid JSON response", tt{
   322  		db: newTestDB(&http.Response{
   323  			StatusCode: http.StatusOK,
   324  			Body:       io.NopCloser(strings.NewReader(`invalid json`)),
   325  		}, nil),
   326  		status: http.StatusBadGateway,
   327  		err:    "invalid character 'i' looking for beginning of value",
   328  	})
   329  	tests.Add("error response", tt{
   330  		db: newTestDB(&http.Response{
   331  			StatusCode: http.StatusBadRequest,
   332  			Body:       io.NopCloser(strings.NewReader("")),
   333  		}, nil),
   334  		status: http.StatusBadRequest,
   335  		err:    "Bad Request",
   336  	})
   337  	tests.Add("3.0.0-pre", tt{
   338  		db: newTestDB(&http.Response{
   339  			StatusCode: http.StatusOK,
   340  			Header: http.Header{
   341  				"Server":              {"CouchDB/2.3.0-a1e11cea9 (Erlang OTP/21)"},
   342  				"Date":                {"Thu, 24 Jan 2019 17:19:59 GMT"},
   343  				"Content-Type":        {"application/json"},
   344  				"Content-Length":      {"119"},
   345  				"Cache-Control":       {"must-revalidate"},
   346  				"X-Couch-Request-ID":  {"2486f27546"},
   347  				"X-CouchDB-Body-Time": {"0"},
   348  			},
   349  			Body: io.NopCloser(strings.NewReader(`{"db_name":"my_new_db","doc_count":1,"doc_del_count":0,"partition":"sensor-260","sizes":{"active":244,"external":347}}
   350  `)),
   351  		}, nil),
   352  	})
   353  
   354  	tests.Run(t, func(t *testing.T, tt tt) {
   355  		result, err := tt.db.PartitionStats(context.Background(), tt.name)
   356  		if d := internal.StatusErrorDiffRE(tt.err, tt.status, err); d != "" {
   357  			t.Error(d)
   358  		}
   359  		if err != nil {
   360  			return
   361  		}
   362  		if d := testy.DiffInterface(testy.Snapshot(t), result); d != nil {
   363  			t.Error(d)
   364  		}
   365  	})
   366  }
   367  

View as plain text