1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package pouchdb
16
17 import (
18 "context"
19 "encoding/json"
20 "fmt"
21 "runtime"
22 "strconv"
23 "strings"
24 "testing"
25
26 "github.com/gopherjs/gopherjs/js"
27 "gitlab.com/flimzy/testy"
28
29 "github.com/go-kivik/kivik/v4/driver"
30 "github.com/go-kivik/kivik/v4/pouchdb/bindings"
31 "github.com/go-kivik/kivik/v4/pouchdb/internal"
32 )
33
34 func init() {
35 memPouch := js.Global.Get("PouchDB").Call("defaults", map[string]interface{}{
36 "db": js.Global.Call("require", "memdown"),
37 })
38 js.Global.Set("PouchDB", memPouch)
39 }
40
41 func TestBuildIndex(t *testing.T) {
42 tests := []struct {
43 Ddoc string
44 Name string
45 Index interface{}
46 Expected string
47 }{
48 {Expected: `{}`},
49 {Index: `{"fields":["foo"]}`, Expected: `{"fields":["foo"]}`},
50 {Index: `{"fields":["foo"]}`, Name: "test", Expected: `{"fields":["foo"],"name":"test"}`},
51 {Index: `{"fields":["foo"]}`, Name: "test", Ddoc: "_foo", Expected: `{"fields":["foo"],"name":"test","ddoc":"_foo"}`},
52 }
53 for i, test := range tests {
54 t.Run(strconv.Itoa(i), func(t *testing.T) {
55 result, err := buildIndex(test.Ddoc, test.Name, test.Index)
56 if err != nil {
57 t.Errorf("Build Index failed: %s", err)
58 }
59 r := js.Global.Get("JSON").Call("stringify", result).String()
60 if d := testy.DiffJSON([]byte(test.Expected), []byte(r)); d != nil {
61 t.Errorf("BuildIndex result differs:\n%s\n", d)
62 }
63 })
64 }
65 }
66
67 func TestExplain(t *testing.T) {
68 defaultLimit := int64(0)
69 if ver := internal.PouchDBVersion(t); strings.HasPrefix(ver, "9") {
70 defaultLimit = 25
71 }
72 type test struct {
73 db *db
74 query interface{}
75 expected *driver.QueryPlan
76 err string
77 }
78 tests := testy.NewTable()
79 tests.Add("query error", test{
80 db: &db{db: bindings.GlobalPouchDB().New("foo", nil)},
81 query: nil,
82 err: "TypeError: Cannot read propert",
83 })
84 tests.Add("simple selector", func(t *testing.T) interface{} {
85 options := map[string]interface{}{
86 "bookmark": "nil",
87 "conflicts": false,
88 "r": []interface{}{49},
89 "sort": map[string]interface{}{},
90 "use_index": []interface{}{},
91 }
92 if defaultLimit > 0 {
93 options["limit"] = defaultLimit
94 }
95
96 return test{
97 db: &db{db: bindings.GlobalPouchDB().New("foo", nil)},
98 query: map[string]interface{}{"selector": map[string]interface{}{"_id": "foo"}},
99 expected: &driver.QueryPlan{
100 DBName: "foo",
101 Limit: defaultLimit,
102 Index: map[string]interface{}{
103 "ddoc": nil,
104 "def": map[string]interface{}{
105 "fields": []interface{}{map[string]interface{}{"_id": "asc"}},
106 },
107 "name": "_all_docs",
108 "type": "special",
109 },
110 Options: options,
111 Selector: map[string]interface{}{"_id": map[string]interface{}{"$eq": "foo"}},
112 Fields: func() []interface{} {
113 fmt.Println(runtime.Version())
114 if ver := runtime.Version(); strings.HasPrefix(ver, "go1.16") {
115 return []interface{}{}
116 }
117
118 return nil
119 }(),
120 Range: map[string]interface{}{},
121 },
122 }
123 })
124 tests.Add("fields list", func(t *testing.T) interface{} {
125 options := map[string]interface{}{
126 "bookmark": "nil",
127 "conflicts": false,
128 "fields": []interface{}{"_id", map[string]interface{}{"type": "desc"}},
129 "r": []interface{}{49},
130 "sort": map[string]interface{}{},
131 "use_index": []interface{}{},
132 }
133 if defaultLimit > 0 {
134 options["limit"] = defaultLimit
135 }
136
137 return test{
138 db: &db{db: bindings.GlobalPouchDB().New("foo", nil)},
139 query: map[string]interface{}{
140 "selector": map[string]interface{}{"_id": "foo"},
141 "fields": []interface{}{"_id", map[string]interface{}{"type": "desc"}},
142 },
143 expected: &driver.QueryPlan{
144 DBName: "foo",
145 Limit: defaultLimit,
146 Index: map[string]interface{}{
147 "ddoc": nil,
148 "def": map[string]interface{}{
149 "fields": []interface{}{map[string]interface{}{"_id": "asc"}},
150 },
151 "name": "_all_docs",
152 "type": "special",
153 },
154 Options: options,
155 Selector: map[string]interface{}{"_id": map[string]interface{}{"$eq": "foo"}},
156 Fields: []interface{}{"_id", map[string]interface{}{"type": "desc"}},
157 Range: map[string]interface{}{},
158 },
159 }
160 })
161 tests.Run(t, func(t *testing.T, tt test) {
162 result, err := tt.db.Explain(context.Background(), tt.query, nil)
163 if !testy.ErrorMatchesRE(tt.err, err) {
164 t.Errorf("Unexpected error: %s", err)
165 }
166 if err != nil {
167 return
168 }
169 if d := testy.DiffAsJSON(tt.expected, result); d != nil {
170 t.Error(d)
171 }
172 })
173 }
174
175 func TestUnmarshalQueryPlan(t *testing.T) {
176 tests := []struct {
177 name string
178 input string
179 expected *queryPlan
180 err string
181 }{
182 {
183 name: "non-array",
184 input: `{"fields":{}}`,
185 err: "json: cannot unmarshal object into Go",
186 },
187 {
188 name: "all_fields",
189 input: `{"fields":"all_fields","dbname":"foo"}`,
190 expected: &queryPlan{DBName: "foo"},
191 },
192 {
193 name: "simple field list",
194 input: `{"fields":["foo","bar"],"dbname":"foo"}`,
195 expected: &queryPlan{Fields: []interface{}{"foo", "bar"}, DBName: "foo"},
196 },
197 {
198 name: "complex field list",
199 input: `{"dbname":"foo", "fields":[{"foo":"asc"},{"bar":"desc"}]}`,
200 expected: &queryPlan{
201 DBName: "foo",
202 Fields: []interface{}{
203 map[string]interface{}{"foo": "asc"},
204 map[string]interface{}{"bar": "desc"},
205 },
206 },
207 },
208 {
209 name: "invalid bare string",
210 input: `{"fields":"not_all_fields"}`,
211 err: "json: cannot unmarshal string into Go",
212 },
213 }
214 for _, test := range tests {
215 t.Run(test.name, func(t *testing.T) {
216 result := new(queryPlan)
217 err := json.Unmarshal([]byte(test.input), &result)
218 if !testy.ErrorMatchesRE(test.err, err) {
219 t.Errorf("Unexpected error: %s", err)
220 }
221 if err != nil {
222 return
223 }
224 if d := testy.DiffInterface(test.expected, result); d != nil {
225 t.Error(d)
226 }
227 })
228 }
229 }
230
View as plain text