...

Source file src/github.com/go-kivik/kivik/v4/x/memorydb/find_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  	"bytes"
    17  	"context"
    18  	"encoding/json"
    19  	"strings"
    20  	"testing"
    21  
    22  	"gitlab.com/flimzy/testy"
    23  
    24  	"github.com/go-kivik/kivik/v4/driver"
    25  )
    26  
    27  func TestIndexSpecUnmarshalJSON(t *testing.T) {
    28  	type isuTest struct {
    29  		name     string
    30  		input    string
    31  		expected *indexSpec
    32  		err      string
    33  	}
    34  	tests := []isuTest{
    35  		{
    36  			name:     "ddoc only",
    37  			input:    `"foo"`,
    38  			expected: &indexSpec{ddoc: "foo"},
    39  		},
    40  		{
    41  			name:     "ddoc and index",
    42  			input:    `["foo","bar"]`,
    43  			expected: &indexSpec{ddoc: "foo", index: "bar"},
    44  		},
    45  		{
    46  			name:  "invalid json",
    47  			input: "asdf",
    48  			err:   "invalid character 'a' looking for beginning of value",
    49  		},
    50  		{
    51  			name:  "extra fields",
    52  			input: `["foo","bar","baz"]`,
    53  			err:   "invalid index specification",
    54  		},
    55  		{
    56  			name:     "One field",
    57  			input:    `["foo"]`,
    58  			expected: &indexSpec{ddoc: "foo"},
    59  		},
    60  		{
    61  			name:  "Empty array",
    62  			input: `[]`,
    63  			err:   "invalid index specification",
    64  		},
    65  	}
    66  	for _, test := range tests {
    67  		t.Run(test.name, func(t *testing.T) {
    68  			result := &indexSpec{}
    69  			err := result.UnmarshalJSON([]byte(test.input))
    70  			var msg string
    71  			if err != nil {
    72  				msg = err.Error()
    73  			}
    74  			if msg != test.err {
    75  				t.Errorf("Unexpected error: %s", err)
    76  			}
    77  			if err != nil {
    78  				return
    79  			}
    80  			if d := testy.DiffInterface(test.expected, result); d != nil {
    81  				t.Error(d)
    82  			}
    83  		})
    84  	}
    85  }
    86  
    87  func TestCreateIndex(t *testing.T) {
    88  	d := &db{}
    89  	err := d.CreateIndex(context.Background(), "foo", "bar", "baz", nil)
    90  	if err != errFindNotImplemented {
    91  		t.Errorf("Unexpected error: %s", err)
    92  	}
    93  }
    94  
    95  func TestGetIndexes(t *testing.T) {
    96  	d := &db{}
    97  	_, err := d.GetIndexes(context.Background(), nil)
    98  	if err != errFindNotImplemented {
    99  		t.Errorf("Unexpected error: %s", err)
   100  	}
   101  }
   102  
   103  func TestDeleteIndex(t *testing.T) {
   104  	d := &db{}
   105  	err := d.DeleteIndex(context.Background(), "foo", "bar", nil)
   106  	if err != errFindNotImplemented {
   107  		t.Errorf("Unexpected error: %s", err)
   108  	}
   109  }
   110  
   111  // TestFind tests selectors, to see that the proper doc IDs are returned.
   112  func TestFind(t *testing.T) {
   113  	type findTest struct {
   114  		name        string
   115  		db          *db
   116  		query       interface{}
   117  		expectedIDs []string
   118  		err         string
   119  		rowsErr     string
   120  	}
   121  	tests := []findTest{
   122  		{
   123  			name:  "invalid query",
   124  			query: make(chan int),
   125  			err:   "json: unsupported type: chan int",
   126  		},
   127  		{
   128  			name:  "Invalid JSON query",
   129  			query: "asdf",
   130  			err:   "invalid character 'a' looking for beginning of value",
   131  		},
   132  		{
   133  			name: "No query",
   134  			err:  "Missing required key: selector",
   135  		},
   136  		{
   137  			name:  "empty selector",
   138  			query: `{"selector":{}}`,
   139  			db: func() *db {
   140  				db := setupDB(t)
   141  				for _, id := range []string{"a", "c", "z", "q", "chicken"} {
   142  					if _, err := db.Put(context.Background(), id, map[string]string{"value": id}, nil); err != nil {
   143  						t.Fatal(err)
   144  					}
   145  				}
   146  				return db
   147  			}(),
   148  			expectedIDs: []string{"a", "c", "chicken", "q", "z"},
   149  		},
   150  		{
   151  			name:  "simple selector",
   152  			query: `{"selector":{"value":"chicken"}}`,
   153  			db: func() *db {
   154  				db := setupDB(t)
   155  				for _, id := range []string{"a", "c", "z", "q", "chicken"} {
   156  					if _, err := db.Put(context.Background(), id, map[string]string{"value": id}, nil); err != nil {
   157  						t.Fatal(err)
   158  					}
   159  				}
   160  				return db
   161  			}(),
   162  			expectedIDs: []string{"chicken"},
   163  		},
   164  	}
   165  	for _, test := range tests {
   166  		t.Run(test.name, func(t *testing.T) {
   167  			db := test.db
   168  			if db == nil {
   169  				db = setupDB(t)
   170  			}
   171  			rows, err := db.Find(context.Background(), test.query, nil)
   172  			var msg string
   173  			if err != nil {
   174  				msg = err.Error()
   175  			}
   176  			if msg != test.err {
   177  				t.Errorf("Unexpected error: %s", err)
   178  			}
   179  			if err != nil {
   180  				return
   181  			}
   182  			checkRows(t, rows, test.expectedIDs, test.rowsErr)
   183  		})
   184  	}
   185  }
   186  
   187  // TestFindDoc is the same as Testfind, but assumes only a single result
   188  // (ignores any others), and compares the entire document.
   189  func TestFindDoc(t *testing.T) {
   190  	type fdTest struct {
   191  		name     string
   192  		db       *db
   193  		query    interface{}
   194  		expected interface{}
   195  	}
   196  	tests := []fdTest{
   197  		{
   198  			name:  "simple selector",
   199  			query: `{"selector":{}}`,
   200  			db: func() *db {
   201  				db := setupDB(t)
   202  				id := "chicken"
   203  				if _, err := db.Put(context.Background(), id, map[string]string{"value": id}, nil); err != nil {
   204  					t.Fatal(err)
   205  				}
   206  				return db
   207  			}(),
   208  			expected: map[string]interface{}{
   209  				"_id":   "chicken",
   210  				"_rev":  "1-xxx",
   211  				"value": "chicken",
   212  			},
   213  		},
   214  		{
   215  			name:  "fields",
   216  			query: `{"selector":{}, "fields":["value","_rev"]}`,
   217  			db: func() *db {
   218  				db := setupDB(t)
   219  				if _, err := db.Put(context.Background(), "foo", map[string]string{"value": "foo"}, nil); err != nil {
   220  					t.Fatal(err)
   221  				}
   222  				return db
   223  			}(),
   224  			expected: map[string]interface{}{
   225  				"value": "foo",
   226  				"_rev":  "1-xxx",
   227  			},
   228  		},
   229  	}
   230  	for _, test := range tests {
   231  		t.Run(test.name, func(t *testing.T) {
   232  			db := test.db
   233  			if db == nil {
   234  				db = setupDB(t)
   235  			}
   236  			rows, err := db.Find(context.Background(), test.query, nil)
   237  			if err != nil {
   238  				t.Fatal(err)
   239  			}
   240  			var row driver.Row
   241  			if e := rows.Next(&row); e != nil {
   242  				t.Fatal(e)
   243  			}
   244  			_ = rows.Close()
   245  			var result map[string]interface{}
   246  			if e := json.NewDecoder(row.Doc).Decode(&result); e != nil {
   247  				t.Fatal(e)
   248  			}
   249  			parts := strings.Split(result["_rev"].(string), "-")
   250  			result["_rev"] = parts[0] + "-xxx"
   251  			if d := testy.DiffAsJSON(test.expected, result); d != nil {
   252  				t.Error(d)
   253  			}
   254  		})
   255  	}
   256  }
   257  
   258  func TestResultWarning(t *testing.T) {
   259  	rows := &findResults{}
   260  	expected := "no matching index found, create an index to optimize query time"
   261  	if w := rows.Warning(); w != expected {
   262  		t.Errorf("Unexpected warning: %s", w)
   263  	}
   264  }
   265  
   266  func TestFilterDoc(t *testing.T) {
   267  	type fdTest struct {
   268  		name     string
   269  		rows     *findResults
   270  		data     string
   271  		expected string
   272  		err      string
   273  	}
   274  	tests := []fdTest{
   275  		{
   276  			name:     "no filter",
   277  			rows:     &findResults{},
   278  			data:     `{"foo":"bar"}`,
   279  			expected: `{"foo":"bar"}`,
   280  		},
   281  		{
   282  			name:     "with filter",
   283  			rows:     &findResults{fields: map[string]struct{}{"foo": {}}},
   284  			data:     `{"foo":"bar", "baz":"qux"}`,
   285  			expected: `{"foo":"bar"}`,
   286  		},
   287  		{
   288  			name: "invalid json",
   289  			rows: &findResults{fields: map[string]struct{}{"foo": {}}},
   290  			data: `{"foo":"bar", "baz":"qux}`,
   291  			err:  "unexpected end of JSON input",
   292  		},
   293  	}
   294  	for _, test := range tests {
   295  		t.Run(test.name, func(t *testing.T) {
   296  			result, err := test.rows.filterDoc([]byte(test.data))
   297  			var msg string
   298  			if err != nil {
   299  				msg = err.Error()
   300  			}
   301  			if msg != test.err {
   302  				t.Errorf("Unexpected error: %s", msg)
   303  			}
   304  			if err != nil {
   305  				return
   306  			}
   307  			if d := testy.DiffJSON([]byte(test.expected), result); d != nil {
   308  				t.Error(d)
   309  			}
   310  		})
   311  	}
   312  }
   313  
   314  func TestToJSON(t *testing.T) {
   315  	type tjTest struct {
   316  		Name     string
   317  		Input    interface{}
   318  		Expected string
   319  	}
   320  	tests := []tjTest{
   321  		{
   322  			Name:     "Null",
   323  			Expected: "null",
   324  		},
   325  		{
   326  			Name:     "String",
   327  			Input:    `{"foo":"bar"}`,
   328  			Expected: `{"foo":"bar"}`,
   329  		},
   330  		{
   331  			Name:     "ByteSlice",
   332  			Input:    []byte(`{"foo":"bar"}`),
   333  			Expected: `{"foo":"bar"}`,
   334  		},
   335  		{
   336  			Name:     "RawMessage",
   337  			Input:    json.RawMessage(`{"foo":"bar"}`),
   338  			Expected: `{"foo":"bar"}`,
   339  		},
   340  		{
   341  			Name:     "Interface",
   342  			Input:    map[string]string{"foo": "bar"},
   343  			Expected: `{"foo":"bar"}`,
   344  		},
   345  	}
   346  	for _, test := range tests {
   347  		t.Run(test.Name, func(t *testing.T) {
   348  			r, err := toJSON(test.Input)
   349  			if err != nil {
   350  				t.Fatalf("jsonify failed: %s", err)
   351  			}
   352  			buf := &bytes.Buffer{}
   353  			_, _ = buf.ReadFrom(r)
   354  			result := strings.TrimSpace(buf.String())
   355  			if result != test.Expected {
   356  				t.Errorf("Expected: `%s`\n  Actual: `%s`", test.Expected, result)
   357  			}
   358  		})
   359  	}
   360  }
   361  

View as plain text