...

Source file src/github.com/go-kivik/kivik/v4/x/memorydb/db_test.go

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

     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 memorydb
    14  
    15  import (
    16  	"context"
    17  	"encoding/json"
    18  	"net/http"
    19  	"strings"
    20  	"testing"
    21  
    22  	"gitlab.com/flimzy/testy"
    23  
    24  	"github.com/go-kivik/kivik/v4"
    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  	type statTest struct {
    31  		Name     string
    32  		DBName   string
    33  		Setup    func(driver.Client)
    34  		Expected *driver.DBStats
    35  		Error    string
    36  	}
    37  	tests := []statTest{
    38  		{
    39  			Name:   "NoDBs",
    40  			DBName: "foo",
    41  			Setup: func(c driver.Client) {
    42  				if e := c.CreateDB(context.Background(), "foo", nil); e != nil {
    43  					panic(e)
    44  				}
    45  			},
    46  			Expected: &driver.DBStats{Name: "foo"},
    47  		},
    48  	}
    49  	for _, test := range tests {
    50  		func(test statTest) {
    51  			t.Run(test.Name, func(t *testing.T) {
    52  				c := setup(t, test.Setup)
    53  				db, err := c.DB(test.DBName, nil)
    54  				if err != nil {
    55  					t.Fatal(err)
    56  				}
    57  				result, err := db.Stats(context.Background())
    58  				var msg string
    59  				if err != nil {
    60  					msg = err.Error()
    61  				}
    62  				if msg != test.Error {
    63  					t.Errorf("Unexpected error: %s", msg)
    64  				}
    65  				if err != nil {
    66  					return
    67  				}
    68  				if d := testy.DiffInterface(test.Expected, result); d != nil {
    69  					t.Error(d)
    70  				}
    71  			})
    72  		}(test)
    73  	}
    74  }
    75  
    76  func setupDB(t *testing.T) *db {
    77  	t.Helper()
    78  	c := setup(t, nil)
    79  	if err := c.CreateDB(context.Background(), "foo", nil); err != nil {
    80  		t.Fatal(err)
    81  	}
    82  	d, err := c.DB("foo", nil)
    83  	if err != nil {
    84  		t.Fatal(err)
    85  	}
    86  	return d.(*db)
    87  }
    88  
    89  func TestPut(t *testing.T) {
    90  	type putTest struct {
    91  		Name     string
    92  		DocID    string
    93  		Doc      interface{}
    94  		Setup    func() *db
    95  		Expected interface{}
    96  		Status   int
    97  		Error    string
    98  	}
    99  	tests := []putTest{
   100  		{
   101  			Name:   "LeadingUnderscoreInID",
   102  			DocID:  "_badid",
   103  			Doc:    map[string]string{"_id": "_badid", "foo": "bar"},
   104  			Status: 400,
   105  			Error:  "only reserved document ids may start with underscore",
   106  		},
   107  		{
   108  			Name:     "MismatchedIDs",
   109  			DocID:    "foo",
   110  			Doc:      map[string]string{"_id": "bar"},
   111  			Expected: map[string]string{"_id": "foo", "_rev": "1-xxx"},
   112  		},
   113  		{
   114  			Name:     "Success",
   115  			DocID:    "foo",
   116  			Doc:      map[string]string{"_id": "foo", "foo": "bar"},
   117  			Expected: map[string]string{"_id": "foo", "foo": "bar", "_rev": "1-xxx"},
   118  		},
   119  		{
   120  			Name:  "Conflict",
   121  			DocID: "foo",
   122  			Doc:   map[string]string{"_id": "foo", "_rev": "bar"},
   123  			Setup: func() *db {
   124  				db := setupDB(t)
   125  				if _, err := db.Put(context.Background(), "foo", map[string]string{"_id": "foo"}, nil); err != nil {
   126  					t.Fatal(err)
   127  				}
   128  				return db
   129  			},
   130  			Status: 409,
   131  			Error:  "document update conflict",
   132  		},
   133  		{
   134  			Name:  "Unmarshalable",
   135  			DocID: "foo",
   136  			Doc: func() interface{} {
   137  				return map[string]interface{}{
   138  					"channel": make(chan int),
   139  				}
   140  			}(),
   141  			Status: 400,
   142  			Error:  "json: unsupported type: chan int",
   143  		},
   144  		{
   145  			Name:   "InitialRev",
   146  			DocID:  "foo",
   147  			Doc:    map[string]string{"_id": "foo", "_rev": "bar"},
   148  			Status: 409,
   149  			Error:  "document update conflict",
   150  		},
   151  		func() putTest {
   152  			dbv := setupDB(t)
   153  			rev, err := dbv.Put(context.Background(), "foo", map[string]string{"_id": "foo", "foo": "bar"}, nil)
   154  			if err != nil {
   155  				panic(err)
   156  			}
   157  			return putTest{
   158  				Name:     "Update",
   159  				DocID:    "foo",
   160  				Setup:    func() *db { return dbv },
   161  				Doc:      map[string]string{"_id": "foo", "_rev": rev},
   162  				Expected: map[string]string{"_id": "foo", "_rev": "2-xxx"},
   163  			}
   164  		}(),
   165  		{
   166  			Name:     "DesignDoc",
   167  			DocID:    "_design/foo",
   168  			Doc:      map[string]string{"foo": "bar"},
   169  			Expected: map[string]string{"_id": "_design/foo", "foo": "bar", "_rev": "1-xxx"},
   170  		},
   171  		{
   172  			Name:     "LocalDoc",
   173  			DocID:    "_local/foo",
   174  			Doc:      map[string]string{"foo": "bar"},
   175  			Expected: map[string]string{"_id": "_local/foo", "foo": "bar", "_rev": "1-0"},
   176  		},
   177  		{
   178  			Name:     "RecreateDeleted",
   179  			DocID:    "foo",
   180  			Doc:      map[string]string{"foo": "bar"},
   181  			Expected: map[string]string{"_id": "foo", "foo": "bar", "_rev": "3-xxx"},
   182  			Setup: func() *db {
   183  				db := setupDB(t)
   184  				rev, err := db.Put(context.Background(), "foo", map[string]string{"_id": "foo"}, nil)
   185  				if err != nil {
   186  					t.Fatal(err)
   187  				}
   188  				if _, e := db.Delete(context.Background(), "foo", kivik.Rev(rev)); e != nil {
   189  					t.Fatal(e)
   190  				}
   191  				return db
   192  			},
   193  		},
   194  		{
   195  			Name:     "LocalDoc",
   196  			DocID:    "_local/foo",
   197  			Doc:      map[string]string{"foo": "baz"},
   198  			Expected: map[string]string{"_id": "_local/foo", "foo": "baz", "_rev": "1-0"},
   199  			Setup: func() *db {
   200  				db := setupDB(t)
   201  				_, err := db.Put(context.Background(), "_local/foo", map[string]string{"foo": "bar"}, nil)
   202  				if err != nil {
   203  					t.Fatal(err)
   204  				}
   205  				return db
   206  			},
   207  		},
   208  		{
   209  			Name:  "WithAttachments",
   210  			DocID: "duck",
   211  			Doc: map[string]interface{}{
   212  				"_id":   "duck",
   213  				"value": "quack",
   214  				"_attachments": []map[string]interface{}{
   215  					{"foo.css": map[string]string{
   216  						"content_type": "text/css",
   217  						"data":         "LyogYW4gZW1wdHkgQ1NTIGZpbGUgKi8=",
   218  					}},
   219  				},
   220  			},
   221  			Expected: map[string]string{
   222  				"_id":   "duck",
   223  				"_rev":  "1-xxx",
   224  				"value": "quack",
   225  			},
   226  		},
   227  		{
   228  			Name: "Deleted DB",
   229  			Setup: func() *db {
   230  				c := setup(t, nil)
   231  				if err := c.CreateDB(context.Background(), "deleted0", nil); err != nil {
   232  					t.Fatal(err)
   233  				}
   234  				dbv, err := c.DB("deleted0", nil)
   235  				if err != nil {
   236  					t.Fatal(err)
   237  				}
   238  				if e := c.DestroyDB(context.Background(), "deleted0", nil); e != nil {
   239  					t.Fatal(e)
   240  				}
   241  				return dbv.(*db)
   242  			},
   243  			Status: http.StatusPreconditionFailed,
   244  			Error:  "database does not exist",
   245  		},
   246  	}
   247  	for _, test := range tests {
   248  		func(test putTest) {
   249  			t.Run(test.Name, func(t *testing.T) {
   250  				t.Parallel()
   251  				var db *db
   252  				if test.Setup != nil {
   253  					db = test.Setup()
   254  				} else {
   255  					db = setupDB(t)
   256  				}
   257  				_, err := db.Put(context.Background(), test.DocID, test.Doc, nil)
   258  				if d := internal.StatusErrorDiff(test.Error, test.Status, err); d != "" {
   259  					t.Error(d)
   260  				}
   261  				if err != nil {
   262  					return
   263  				}
   264  				rows, err := db.Get(context.Background(), test.DocID, kivik.Params(nil))
   265  				if err != nil {
   266  					t.Fatal(err)
   267  				}
   268  				var result map[string]interface{}
   269  				if e := json.NewDecoder(rows.Body).Decode(&result); e != nil {
   270  					t.Fatal(e)
   271  				}
   272  				if !strings.HasPrefix(test.DocID, "_local/") {
   273  					if rev, ok := result["_rev"].(string); ok {
   274  						parts := strings.SplitN(rev, "-", 2)
   275  						result["_rev"] = parts[0] + "-xxx"
   276  					}
   277  				}
   278  				if d := testy.DiffAsJSON(test.Expected, result); d != nil {
   279  					t.Error(d)
   280  				}
   281  			})
   282  		}(test)
   283  	}
   284  }
   285  
   286  func TestGet(t *testing.T) {
   287  	type getTest struct {
   288  		Name     string
   289  		ID       string
   290  		options  kivik.Option
   291  		DB       *db
   292  		Status   int
   293  		Error    string
   294  		Expected interface{}
   295  	}
   296  	tests := []getTest{
   297  		{
   298  			Name:   "NotFound",
   299  			ID:     "foo",
   300  			Status: 404,
   301  			Error:  "missing",
   302  		},
   303  		func() getTest {
   304  			db := setupDB(t)
   305  			_, err := db.Put(context.Background(), "foo", map[string]string{"_id": "foo", "foo": "bar"}, nil)
   306  			if err != nil {
   307  				panic(err)
   308  			}
   309  			return getTest{
   310  				Name:     "ExistingDoc",
   311  				ID:       "foo",
   312  				DB:       db,
   313  				Expected: map[string]string{"_id": "foo", "foo": "bar"},
   314  			}
   315  		}(),
   316  		func() getTest {
   317  			db := setupDB(t)
   318  			rev, err := db.Put(context.Background(), "foo", map[string]string{"_id": "foo", "foo": "Bar"}, nil)
   319  			if err != nil {
   320  				panic(err)
   321  			}
   322  			return getTest{
   323  				Name:     "SpecificRev",
   324  				ID:       "foo",
   325  				DB:       db,
   326  				options:  kivik.Rev(rev),
   327  				Expected: map[string]string{"_id": "foo", "foo": "Bar"},
   328  			}
   329  		}(),
   330  		func() getTest {
   331  			db := setupDB(t)
   332  			rev, err := db.Put(context.Background(), "foo", map[string]string{"_id": "foo", "foo": "Bar"}, nil)
   333  			if err != nil {
   334  				panic(err)
   335  			}
   336  			_, err = db.Put(context.Background(), "foo", map[string]string{"_id": "foo", "foo": "baz", "_rev": rev}, nil)
   337  			if err != nil {
   338  				panic(err)
   339  			}
   340  			return getTest{
   341  				Name:     "OldRev",
   342  				ID:       "foo",
   343  				DB:       db,
   344  				options:  kivik.Rev(rev),
   345  				Expected: map[string]string{"_id": "foo", "foo": "Bar"},
   346  			}
   347  		}(),
   348  		{
   349  			Name:    "MissingRev",
   350  			ID:      "foo",
   351  			options: kivik.Rev("1-4c6114c65e295552ab1019e2b046b10e"),
   352  			DB: func() *db {
   353  				db := setupDB(t)
   354  				_, err := db.Put(context.Background(), "foo", map[string]string{"_id": "foo", "foo": "Bar"}, nil)
   355  				if err != nil {
   356  					panic(err)
   357  				}
   358  				return db
   359  			}(),
   360  			Status: 404,
   361  			Error:  "missing",
   362  		},
   363  		func() getTest {
   364  			db := setupDB(t)
   365  			rev, err := db.Put(context.Background(), "foo", map[string]string{"_id": "foo"}, nil)
   366  			if err != nil {
   367  				panic(err)
   368  			}
   369  			if _, e := db.Delete(context.Background(), "foo", kivik.Rev(rev)); e != nil {
   370  				panic(e)
   371  			}
   372  			return getTest{
   373  				Name:   "DeletedDoc",
   374  				ID:     "foo",
   375  				DB:     db,
   376  				Status: 404,
   377  				Error:  "missing",
   378  			}
   379  		}(),
   380  		{
   381  			Name: "Deleted DB",
   382  			DB: func() *db {
   383  				c := setup(t, nil)
   384  				if err := c.CreateDB(context.Background(), "deleted0", nil); err != nil {
   385  					t.Fatal(err)
   386  				}
   387  				dbv, err := c.DB("deleted0", nil)
   388  				if err != nil {
   389  					t.Fatal(err)
   390  				}
   391  				if e := c.DestroyDB(context.Background(), "deleted0", nil); e != nil {
   392  					t.Fatal(e)
   393  				}
   394  				return dbv.(*db)
   395  			}(),
   396  			Error:  "database does not exist",
   397  			Status: http.StatusPreconditionFailed,
   398  		},
   399  	}
   400  	for _, test := range tests {
   401  		func(test getTest) {
   402  			t.Run(test.Name, func(t *testing.T) {
   403  				t.Parallel()
   404  				db := test.DB
   405  				if db == nil {
   406  					db = setupDB(t)
   407  				}
   408  				opts := test.options
   409  				if opts == nil {
   410  					opts = kivik.Params(nil)
   411  				}
   412  				rows, err := db.Get(context.Background(), test.ID, opts)
   413  				if d := internal.StatusErrorDiff(test.Error, test.Status, err); d != "" {
   414  					t.Error(d)
   415  				}
   416  				if err != nil {
   417  					return
   418  				}
   419  
   420  				var result map[string]interface{}
   421  				if err := json.NewDecoder(rows.Body).Decode(&result); err != nil {
   422  					t.Fatal(err)
   423  				}
   424  
   425  				if result != nil {
   426  					delete(result, "_rev")
   427  				}
   428  				if d := testy.DiffAsJSON(test.Expected, result); d != nil {
   429  					t.Error(d)
   430  				}
   431  			})
   432  		}(test)
   433  	}
   434  }
   435  
   436  func TestDeleteDoc(t *testing.T) {
   437  	type delTest struct {
   438  		Name   string
   439  		ID     string
   440  		Rev    string
   441  		DB     *db
   442  		Status int
   443  		Error  string
   444  	}
   445  	tests := []delTest{
   446  		{
   447  			Name:   "NonExistingDoc",
   448  			ID:     "foo",
   449  			Rev:    "1-4c6114c65e295552ab1019e2b046b10e",
   450  			Status: 404,
   451  			Error:  "missing",
   452  		},
   453  		func() delTest {
   454  			db := setupDB(t)
   455  			rev, err := db.Put(context.Background(), "foo", map[string]string{"_id": "foo"}, nil)
   456  			if err != nil {
   457  				panic(err)
   458  			}
   459  			return delTest{
   460  				Name: "Success",
   461  				ID:   "foo",
   462  				DB:   db,
   463  				Rev:  rev,
   464  			}
   465  		}(),
   466  		{
   467  			Name:   "InvalidRevFormat",
   468  			ID:     "foo",
   469  			Rev:    "invalid rev format",
   470  			Status: 400,
   471  			Error:  "invalid rev format",
   472  		},
   473  		{
   474  			Name: "LocalNoRev",
   475  			ID:   "_local/foo",
   476  			Rev:  "",
   477  			DB: func() *db {
   478  				db := setupDB(t)
   479  				if _, err := db.Put(context.Background(), "_local/foo", map[string]string{"foo": "bar"}, nil); err != nil {
   480  					panic(err)
   481  				}
   482  				return db
   483  			}(),
   484  		},
   485  		{
   486  			Name: "LocalWithRev",
   487  			ID:   "_local/foo",
   488  			Rev:  "0-1",
   489  			DB: func() *db {
   490  				db := setupDB(t)
   491  				if _, err := db.Put(context.Background(), "_local/foo", map[string]string{"foo": "bar"}, nil); err != nil {
   492  					panic(err)
   493  				}
   494  				return db
   495  			}(),
   496  		},
   497  		{
   498  			Name: "DB deleted",
   499  			ID:   "foo",
   500  			DB: func() *db {
   501  				c := setup(t, nil)
   502  				if err := c.CreateDB(context.Background(), "deleted0", nil); err != nil {
   503  					t.Fatal(err)
   504  				}
   505  				dbv, err := c.DB("deleted0", nil)
   506  				if err != nil {
   507  					t.Fatal(err)
   508  				}
   509  				if e := c.DestroyDB(context.Background(), "deleted0", nil); e != nil {
   510  					t.Fatal(e)
   511  				}
   512  				return dbv.(*db)
   513  			}(),
   514  			Status: http.StatusPreconditionFailed,
   515  			Error:  "database does not exist",
   516  		},
   517  	}
   518  	for _, test := range tests {
   519  		func(test delTest) {
   520  			t.Run(test.Name, func(t *testing.T) {
   521  				t.Parallel()
   522  				db := test.DB
   523  				if db == nil {
   524  					db = setupDB(t)
   525  				}
   526  				rev, err := db.Delete(context.Background(), test.ID, kivik.Rev(test.Rev))
   527  				var msg string
   528  				var status int
   529  				if err != nil {
   530  					msg = err.Error()
   531  					status = kivik.HTTPStatus(err)
   532  				}
   533  				if msg != test.Error {
   534  					t.Errorf("Unexpected error: %s", msg)
   535  				}
   536  				if status != test.Status {
   537  					t.Errorf("Unexpected status: %d", status)
   538  				}
   539  				if err != nil {
   540  					return
   541  				}
   542  				result, err := db.Get(context.Background(), test.ID, kivik.Rev(rev))
   543  				if err != nil {
   544  					t.Fatal(err)
   545  				}
   546  				var doc interface{}
   547  				if e := json.NewDecoder(result.Body).Decode(&doc); e != nil {
   548  					t.Fatal(e)
   549  				}
   550  				expected := map[string]interface{}{
   551  					"_id":      test.ID,
   552  					"_rev":     rev,
   553  					"_deleted": true,
   554  				}
   555  				if d := testy.DiffAsJSON(expected, doc); d != nil {
   556  					t.Error(d)
   557  				}
   558  			})
   559  		}(test)
   560  	}
   561  }
   562  

View as plain text