...

Source file src/github.com/go-kivik/kivik/v4/find_test.go

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

     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 TestFind(t *testing.T) {
    31  	tests := []struct {
    32  		name     string
    33  		db       *DB
    34  		query    interface{}
    35  		options  []Option
    36  		expected *ResultSet
    37  		status   int
    38  		err      string
    39  	}{
    40  		{
    41  			name: "non-finder",
    42  			db: &DB{
    43  				client:   &Client{},
    44  				driverDB: &mock.DB{},
    45  			},
    46  			status: http.StatusNotImplemented,
    47  			err:    "kivik: driver does not support Find interface",
    48  		},
    49  		{
    50  			name: "db error",
    51  			db: &DB{
    52  				client: &Client{},
    53  				driverDB: &mock.Finder{
    54  					FindFunc: func(context.Context, interface{}, driver.Options) (driver.Rows, error) {
    55  						return nil, errors.New("db error")
    56  					},
    57  				},
    58  			},
    59  			status: http.StatusInternalServerError,
    60  			err:    "db error",
    61  		},
    62  		{
    63  			name: "success",
    64  			db: &DB{
    65  				client: &Client{},
    66  				driverDB: &mock.Finder{
    67  					FindFunc: func(_ context.Context, query interface{}, _ driver.Options) (driver.Rows, error) {
    68  						expectedQuery := json.RawMessage(`{"limit":3,"selector":{"foo":"bar"},"skip":10}`)
    69  						if d := testy.DiffInterface(expectedQuery, query); d != nil {
    70  							return nil, fmt.Errorf("Unexpected query:\n%s", d)
    71  						}
    72  						return &mock.Rows{ID: "a"}, nil
    73  					},
    74  				},
    75  			},
    76  			query: map[string]interface{}{"selector": map[string]interface{}{"foo": "bar"}},
    77  			options: []Option{
    78  				Param("limit", 3),
    79  				Param("skip", 10),
    80  			},
    81  			expected: &ResultSet{
    82  				iter: &iter{
    83  					feed: &rowsIterator{
    84  						Rows: &mock.Rows{ID: "a"},
    85  					},
    86  					curVal: &driver.Row{},
    87  				},
    88  				rowsi: &mock.Rows{ID: "a"},
    89  			},
    90  		},
    91  		{
    92  			name: "db error",
    93  			db: &DB{
    94  				err: errors.New("db error"),
    95  			},
    96  			status: http.StatusInternalServerError,
    97  			err:    "db error",
    98  		},
    99  		{
   100  			name: "client closed",
   101  			db: &DB{
   102  				client: &Client{
   103  					closed: true,
   104  				},
   105  				driverDB: &mock.Finder{},
   106  			},
   107  			status: http.StatusServiceUnavailable,
   108  			err:    "kivik: client closed",
   109  		},
   110  	}
   111  
   112  	for _, test := range tests {
   113  		t.Run(test.name, func(t *testing.T) {
   114  			rs := test.db.Find(context.Background(), test.query, test.options...)
   115  			err := rs.Err()
   116  			if d := internal.StatusErrorDiff(test.err, test.status, err); d != "" {
   117  				t.Error(d)
   118  			}
   119  			if err != nil {
   120  				return
   121  			}
   122  			rs.cancel = nil  // Determinism
   123  			rs.onClose = nil // Determinism
   124  			if d := testy.DiffInterface(test.expected, rs); d != nil {
   125  				t.Error(d)
   126  			}
   127  		})
   128  	}
   129  	t.Run("standalone", func(t *testing.T) {
   130  		t.Run("after err, close doesn't block", func(t *testing.T) {
   131  			db := &DB{
   132  				client: &Client{},
   133  				driverDB: &mock.Finder{
   134  					FindFunc: func(context.Context, interface{}, driver.Options) (driver.Rows, error) {
   135  						return nil, errors.New("sdfsdf")
   136  					},
   137  				},
   138  			}
   139  			rows := db.Find(context.Background(), nil)
   140  			if err := rows.Err(); err == nil {
   141  				t.Fatal("expected an error, got none")
   142  			}
   143  			_ = db.Close() // Should not block
   144  		})
   145  		t.Run("not finder, close doesn't block", func(t *testing.T) {
   146  			db := &DB{
   147  				client:   &Client{},
   148  				driverDB: &mock.DB{},
   149  			}
   150  			rows := db.Find(context.Background(), nil)
   151  			if err := rows.Err(); err == nil {
   152  				t.Fatal("expected an error, got none")
   153  			}
   154  			_ = db.Close() // Should not block
   155  		})
   156  	})
   157  }
   158  
   159  func TestCreateIndex(t *testing.T) {
   160  	tests := []struct {
   161  		testName   string
   162  		db         *DB
   163  		ddoc, name string
   164  		index      interface{}
   165  		status     int
   166  		err        string
   167  	}{
   168  		{
   169  			testName: "non-finder",
   170  			db: &DB{
   171  				client:   &Client{},
   172  				driverDB: &mock.DB{},
   173  			},
   174  			status: http.StatusNotImplemented,
   175  			err:    "kivik: driver does not support Find interface",
   176  		},
   177  		{
   178  			testName: "db error",
   179  			db: &DB{
   180  				client: &Client{},
   181  				driverDB: &mock.Finder{
   182  					CreateIndexFunc: func(context.Context, string, string, interface{}, driver.Options) error {
   183  						return errors.New("db error")
   184  					},
   185  				},
   186  			},
   187  			status: http.StatusInternalServerError,
   188  			err:    "db error",
   189  		},
   190  		{
   191  			testName: "success",
   192  			db: &DB{
   193  				client: &Client{},
   194  				driverDB: &mock.Finder{
   195  					CreateIndexFunc: func(_ context.Context, ddoc, name string, index interface{}, _ driver.Options) error {
   196  						expectedDdoc := "foo"
   197  						expectedName := "bar"
   198  						expectedIndex := int(3)
   199  						if expectedDdoc != ddoc {
   200  							return fmt.Errorf("Unexpected ddoc: %s", ddoc)
   201  						}
   202  						if expectedName != name {
   203  							return fmt.Errorf("Unexpected name: %s", name)
   204  						}
   205  						if d := testy.DiffInterface(expectedIndex, index); d != nil {
   206  							return fmt.Errorf("Unexpected index:\n%s", d)
   207  						}
   208  						return nil
   209  					},
   210  				},
   211  			},
   212  			ddoc:  "foo",
   213  			name:  "bar",
   214  			index: int(3),
   215  		},
   216  		{
   217  			name: "closed",
   218  			db: &DB{
   219  				client: &Client{
   220  					closed: true,
   221  				},
   222  			},
   223  			status: http.StatusServiceUnavailable,
   224  			err:    "kivik: client closed",
   225  		},
   226  		{
   227  			name: "db error",
   228  			db: &DB{
   229  				client: &Client{},
   230  				err:    errors.New("db error"),
   231  			},
   232  			status: http.StatusInternalServerError,
   233  			err:    "db error",
   234  		},
   235  	}
   236  
   237  	for _, test := range tests {
   238  		t.Run(test.testName, func(t *testing.T) {
   239  			err := test.db.CreateIndex(context.Background(), test.ddoc, test.name, test.index)
   240  			if d := internal.StatusErrorDiff(test.err, test.status, err); d != "" {
   241  				t.Error(d)
   242  			}
   243  		})
   244  	}
   245  }
   246  
   247  func TestDeleteIndex(t *testing.T) {
   248  	tests := []struct {
   249  		testName   string
   250  		db         *DB
   251  		ddoc, name string
   252  		status     int
   253  		err        string
   254  	}{
   255  		{
   256  			testName: "non-finder",
   257  			db: &DB{
   258  				client:   &Client{},
   259  				driverDB: &mock.DB{},
   260  			},
   261  			status: http.StatusNotImplemented,
   262  			err:    "kivik: driver does not support Find interface",
   263  		},
   264  		{
   265  			testName: "db error",
   266  			db: &DB{
   267  				client: &Client{},
   268  				driverDB: &mock.Finder{
   269  					DeleteIndexFunc: func(context.Context, string, string, driver.Options) error {
   270  						return errors.New("db error")
   271  					},
   272  				},
   273  			},
   274  			status: http.StatusInternalServerError,
   275  			err:    "db error",
   276  		},
   277  		{
   278  			testName: "success",
   279  			db: &DB{
   280  				client: &Client{},
   281  				driverDB: &mock.Finder{
   282  					DeleteIndexFunc: func(_ context.Context, ddoc, name string, _ driver.Options) error {
   283  						expectedDdoc := "foo"
   284  						expectedName := "bar"
   285  						if expectedDdoc != ddoc {
   286  							return fmt.Errorf("Unexpected ddoc: %s", ddoc)
   287  						}
   288  						if expectedName != name {
   289  							return fmt.Errorf("Unexpected name: %s", name)
   290  						}
   291  						return nil
   292  					},
   293  				},
   294  			},
   295  			ddoc: "foo",
   296  			name: "bar",
   297  		},
   298  		{
   299  			testName: "client closed",
   300  			db: &DB{
   301  				client: &Client{
   302  					closed: true,
   303  				},
   304  			},
   305  			status: http.StatusServiceUnavailable,
   306  			err:    "kivik: client closed",
   307  		},
   308  		{
   309  			testName: "db error",
   310  			db: &DB{
   311  				err: errors.New("db error"),
   312  			},
   313  			status: http.StatusInternalServerError,
   314  			err:    "db error",
   315  		},
   316  	}
   317  
   318  	for _, test := range tests {
   319  		t.Run(test.testName, func(t *testing.T) {
   320  			err := test.db.DeleteIndex(context.Background(), test.ddoc, test.name)
   321  			if d := internal.StatusErrorDiff(test.err, test.status, err); d != "" {
   322  				t.Error(d)
   323  			}
   324  		})
   325  	}
   326  }
   327  
   328  func TestGetIndexes(t *testing.T) {
   329  	tests := []struct {
   330  		testName string
   331  		db       *DB
   332  		expected []Index
   333  		status   int
   334  		err      string
   335  	}{
   336  		{
   337  			testName: "non-finder",
   338  			db: &DB{
   339  				client:   &Client{},
   340  				driverDB: &mock.DB{},
   341  			},
   342  			status: http.StatusNotImplemented,
   343  			err:    "kivik: driver does not support Find interface",
   344  		},
   345  		{
   346  			testName: "db error",
   347  			db: &DB{
   348  				client: &Client{},
   349  				driverDB: &mock.Finder{
   350  					GetIndexesFunc: func(context.Context, driver.Options) ([]driver.Index, error) {
   351  						return nil, errors.New("db error")
   352  					},
   353  				},
   354  			},
   355  			status: http.StatusInternalServerError,
   356  			err:    "db error",
   357  		},
   358  		{
   359  			testName: "success",
   360  			db: &DB{
   361  				client: &Client{},
   362  				driverDB: &mock.Finder{
   363  					GetIndexesFunc: func(context.Context, driver.Options) ([]driver.Index, error) {
   364  						return []driver.Index{
   365  							{Name: "a"},
   366  							{Name: "b"},
   367  						}, nil
   368  					},
   369  				},
   370  			},
   371  			expected: []Index{
   372  				{
   373  					Name: "a",
   374  				},
   375  				{
   376  					Name: "b",
   377  				},
   378  			},
   379  		},
   380  		{
   381  			testName: "client closed",
   382  			db: &DB{
   383  				client: &Client{
   384  					closed: true,
   385  				},
   386  			},
   387  			status: http.StatusServiceUnavailable,
   388  			err:    "kivik: client closed",
   389  		},
   390  		{
   391  			testName: "db error",
   392  			db: &DB{
   393  				err: errors.New("db error"),
   394  			},
   395  			status: http.StatusInternalServerError,
   396  			err:    "db error",
   397  		},
   398  	}
   399  
   400  	for _, test := range tests {
   401  		t.Run(test.testName, func(t *testing.T) {
   402  			result, err := test.db.GetIndexes(context.Background())
   403  			if d := internal.StatusErrorDiff(test.err, test.status, err); d != "" {
   404  				t.Error(d)
   405  			}
   406  			if err != nil {
   407  				return
   408  			}
   409  			if d := testy.DiffInterface(test.expected, result); d != nil {
   410  				t.Error(d)
   411  			}
   412  		})
   413  	}
   414  }
   415  
   416  func TestExplain(t *testing.T) {
   417  	type tt struct {
   418  		db       *DB
   419  		query    interface{}
   420  		expected *QueryPlan
   421  		status   int
   422  		err      string
   423  	}
   424  
   425  	tests := testy.NewTable()
   426  	tests.Add("non-finder", tt{
   427  		db: &DB{
   428  			client:   &Client{},
   429  			driverDB: &mock.DB{},
   430  		},
   431  		status: http.StatusNotImplemented,
   432  		err:    "kivik: driver does not support Find interface",
   433  	})
   434  	tests.Add("explain error", tt{
   435  		db: &DB{
   436  			client: &Client{},
   437  			driverDB: &mock.Finder{
   438  				ExplainFunc: func(context.Context, interface{}, driver.Options) (*driver.QueryPlan, error) {
   439  					return nil, errors.New("explain error")
   440  				},
   441  			},
   442  		},
   443  		status: http.StatusInternalServerError,
   444  		err:    "explain error",
   445  	})
   446  	tests.Add("success", tt{
   447  		db: &DB{
   448  			client: &Client{},
   449  			driverDB: &mock.Finder{
   450  				ExplainFunc: func(_ context.Context, query interface{}, _ driver.Options) (*driver.QueryPlan, error) {
   451  					expectedQuery := int(3)
   452  					if d := testy.DiffInterface(expectedQuery, query); d != nil {
   453  						return nil, fmt.Errorf("Unexpected query:\n%s", d)
   454  					}
   455  					return &driver.QueryPlan{DBName: "foo"}, nil
   456  				},
   457  			},
   458  		},
   459  		query:    int(3),
   460  		expected: &QueryPlan{DBName: "foo"},
   461  	})
   462  	tests.Add("client closed", tt{
   463  		db: &DB{
   464  			driverDB: &mock.Finder{},
   465  			client: &Client{
   466  				closed: true,
   467  			},
   468  		},
   469  		status: http.StatusServiceUnavailable,
   470  		err:    "kivik: client closed",
   471  	})
   472  	tests.Add("db error", tt{
   473  		db: &DB{
   474  			err: errors.New("db error"),
   475  		},
   476  		status: http.StatusInternalServerError,
   477  		err:    "db error",
   478  	})
   479  
   480  	tests.Run(t, func(t *testing.T, tt tt) {
   481  		result, err := tt.db.Explain(context.Background(), tt.query)
   482  		if d := internal.StatusErrorDiff(tt.err, tt.status, err); d != "" {
   483  			t.Error(d)
   484  		}
   485  		if d := testy.DiffInterface(tt.expected, result); d != nil {
   486  			t.Error(d)
   487  		}
   488  	})
   489  }
   490  

View as plain text