1
2
3
4
5
6
7
8
9
10
11
12
13 package kivik
14
15 import (
16 "context"
17 "errors"
18 "io"
19 "net/http"
20 "strconv"
21 "testing"
22
23 "github.com/google/go-cmp/cmp"
24 "gitlab.com/flimzy/testy"
25
26 "github.com/go-kivik/kivik/v4/driver"
27 internal "github.com/go-kivik/kivik/v4/int/errors"
28 "github.com/go-kivik/kivik/v4/int/mock"
29 )
30
31 func TestDBUpdatesNext(t *testing.T) {
32 tests := []struct {
33 name string
34 updates *DBUpdates
35 expected bool
36 }{
37 {
38 name: "nothing more",
39 updates: &DBUpdates{
40 iter: &iter{state: stateClosed},
41 },
42 expected: false,
43 },
44 {
45 name: "more",
46 updates: &DBUpdates{
47 iter: &iter{
48 feed: &mockIterator{
49 NextFunc: func(_ interface{}) error { return nil },
50 },
51 },
52 },
53 expected: true,
54 },
55 }
56 for _, test := range tests {
57 t.Run(test.name, func(t *testing.T) {
58 result := test.updates.Next()
59 if result != test.expected {
60 t.Errorf("Unexpected result: %v", result)
61 }
62 })
63 }
64 }
65
66 func TestDBUpdatesClose(t *testing.T) {
67 expected := "close error"
68 u := &DBUpdates{
69 iter: &iter{
70 feed: &mockIterator{CloseFunc: func() error { return errors.New(expected) }},
71 },
72 }
73 err := u.Close()
74 if !testy.ErrorMatches(expected, err) {
75 t.Errorf("Unexpected error: %s", err)
76 }
77 }
78
79 func TestDBUpdatesErr(t *testing.T) {
80 expected := "foo error"
81 u := &DBUpdates{
82 iter: &iter{err: errors.New(expected)},
83 }
84 err := u.Err()
85 if !testy.ErrorMatches(expected, err) {
86 t.Errorf("Unexpected error: %s", err)
87 }
88 }
89
90 func TestDBUpdatesIteratorNext(t *testing.T) {
91 expected := "foo error"
92 u := &updatesIterator{
93 DBUpdates: &mock.DBUpdates{
94 NextFunc: func(_ *driver.DBUpdate) error { return errors.New(expected) },
95 },
96 }
97 var i driver.DBUpdate
98 err := u.Next(&i)
99 if !testy.ErrorMatches(expected, err) {
100 t.Errorf("Unexpected error: %s", err)
101 }
102 }
103
104 func TestDBUpdatesIteratorNew(t *testing.T) {
105 u := newDBUpdates(context.Background(), nil, &mock.DBUpdates{})
106 expected := &DBUpdates{
107 iter: &iter{
108 feed: &updatesIterator{
109 DBUpdates: &mock.DBUpdates{},
110 },
111 curVal: &driver.DBUpdate{},
112 },
113 }
114 u.cancel = nil
115 if d := testy.DiffInterface(expected, u); d != nil {
116 t.Error(d)
117 }
118 }
119
120 func TestDBUpdateGetters(t *testing.T) {
121 dbname := "foo"
122 updateType := "chicken"
123 seq := "abc123"
124 u := &DBUpdates{
125 iter: &iter{
126 state: stateRowReady,
127 curVal: &driver.DBUpdate{
128 DBName: dbname,
129 Type: updateType,
130 Seq: seq,
131 },
132 },
133 }
134
135 t.Run("DBName", func(t *testing.T) {
136 result := u.DBName()
137 if result != dbname {
138 t.Errorf("Unexpected result: %s", result)
139 }
140 })
141
142 t.Run("Type", func(t *testing.T) {
143 result := u.Type()
144 if result != updateType {
145 t.Errorf("Unexpected result: %s", result)
146 }
147 })
148
149 t.Run("Seq", func(t *testing.T) {
150 result := u.Seq()
151 if result != seq {
152 t.Errorf("Unexpected result: %s", result)
153 }
154 })
155
156 t.Run("LastSeq, should error during iteration", func(t *testing.T) {
157 result, err := u.LastSeq()
158 if result != "" {
159 t.Errorf("Unexpected result: %s", result)
160 }
161 if !testy.ErrorMatches("LastSeq must not be called until results iteration is complete", err) {
162 t.Errorf("Unexpected error: %s", err)
163 }
164 })
165
166 t.Run("Not Ready", func(t *testing.T) {
167 u.state = stateReady
168
169 t.Run("DBName", func(t *testing.T) {
170 result := u.DBName()
171 if result != "" {
172 t.Errorf("Unexpected result: %s", result)
173 }
174 })
175
176 t.Run("Type", func(t *testing.T) {
177 result := u.Type()
178 if result != "" {
179 t.Errorf("Unexpected result: %s", result)
180 }
181 })
182
183 t.Run("Seq", func(t *testing.T) {
184 result := u.Seq()
185 if result != "" {
186 t.Errorf("Unexpected result: %s", result)
187 }
188 })
189 })
190 }
191
192 func TestDBUpdates(t *testing.T) {
193 tests := []struct {
194 name string
195 client *Client
196 expected *DBUpdates
197 status int
198 err string
199 }{
200 {
201 name: "non-DBUpdater",
202 client: &Client{
203 driverClient: &mock.Client{},
204 },
205 status: http.StatusNotImplemented,
206 err: "kivik: driver does not implement DBUpdater",
207 },
208 {
209 name: "db error",
210 client: &Client{
211 driverClient: &mock.DBUpdater{
212 DBUpdatesFunc: func(context.Context, driver.Options) (driver.DBUpdates, error) {
213 return nil, errors.New("db error")
214 },
215 },
216 },
217 status: http.StatusInternalServerError,
218 err: "db error",
219 },
220 {
221 name: "success",
222 client: &Client{
223 driverClient: &mock.DBUpdater{
224 DBUpdatesFunc: func(context.Context, driver.Options) (driver.DBUpdates, error) {
225 return &mock.DBUpdates{ID: "a"}, nil
226 },
227 },
228 },
229 expected: &DBUpdates{
230 iter: &iter{
231 feed: &updatesIterator{
232 DBUpdates: &mock.DBUpdates{ID: "a"},
233 },
234 curVal: &driver.DBUpdate{},
235 },
236 },
237 },
238 {
239 name: "client closed",
240 client: &Client{
241 closed: true,
242 driverClient: &mock.DBUpdater{},
243 },
244 status: http.StatusServiceUnavailable,
245 err: "kivik: client closed",
246 },
247 }
248 for _, test := range tests {
249 t.Run(test.name, func(t *testing.T) {
250 result := test.client.DBUpdates(context.Background())
251 err := result.Err()
252 if d := internal.StatusErrorDiff(test.err, test.status, err); d != "" {
253 t.Error(d)
254 }
255 if err != nil {
256 return
257 }
258 result.cancel = nil
259 result.onClose = nil
260 if d := testy.DiffInterface(test.expected, result); d != nil {
261 t.Error(d)
262 }
263 })
264 }
265 t.Run("standalone", func(t *testing.T) {
266 t.Run("after err, close doesn't block", func(t *testing.T) {
267 client := &Client{
268 driverClient: &mock.DBUpdater{
269 DBUpdatesFunc: func(context.Context, driver.Options) (driver.DBUpdates, error) {
270 return nil, errors.New("asdfsad")
271 },
272 },
273 }
274 rows := client.DBUpdates(context.Background())
275 if err := rows.Err(); err == nil {
276 t.Fatal("expected an error, got none")
277 }
278 _ = client.Close()
279 })
280 t.Run("not updater, close doesn't block", func(t *testing.T) {
281 client := &Client{
282 driverClient: &mock.Client{},
283 }
284 rows := client.DBUpdates(context.Background())
285 if err := rows.Err(); err == nil {
286 t.Fatal("expected an error, got none")
287 }
288 _ = client.Close()
289 })
290 })
291 }
292
293 func TestDBUpdates_Next_resets_iterator_value(t *testing.T) {
294 idx := 0
295 client := &Client{
296 driverClient: &mock.DBUpdater{
297 DBUpdatesFunc: func(context.Context, driver.Options) (driver.DBUpdates, error) {
298 return &mock.DBUpdates{
299 NextFunc: func(update *driver.DBUpdate) error {
300 idx++
301 switch idx {
302 case 1:
303 update.DBName = strconv.Itoa(idx)
304 return nil
305 case 2:
306 return nil
307 }
308 return io.EOF
309 },
310 }, nil
311 },
312 },
313 }
314
315 updates := client.DBUpdates(context.Background())
316
317 wantDBNames := []string{"1", ""}
318 gotDBNames := []string{}
319 for updates.Next() {
320 gotDBNames = append(gotDBNames, updates.DBName())
321 }
322 if d := cmp.Diff(wantDBNames, gotDBNames); d != "" {
323 t.Error(d)
324 }
325 }
326
327 func TestDBUpdates_LastSeq(t *testing.T) {
328 t.Run("non-LastSeqer", func(t *testing.T) {
329 client := &Client{
330 driverClient: &mock.DBUpdater{
331 DBUpdatesFunc: func(context.Context, driver.Options) (driver.DBUpdates, error) {
332 return &mock.DBUpdates{
333 NextFunc: func(_ *driver.DBUpdate) error {
334 return io.EOF
335 },
336 }, nil
337 },
338 },
339 }
340
341 updates := client.DBUpdates(context.Background())
342 for updates.Next() {
343
344 }
345 lastSeq, err := updates.LastSeq()
346 if err != nil {
347 t.Fatal(err)
348 }
349 if lastSeq != "" {
350 t.Errorf("Unexpected lastSeq: %s", lastSeq)
351 }
352 })
353 t.Run("LastSeqer", func(t *testing.T) {
354 client := &Client{
355 driverClient: &mock.DBUpdater{
356 DBUpdatesFunc: func(context.Context, driver.Options) (driver.DBUpdates, error) {
357 return &mock.LastSeqer{
358 DBUpdates: &mock.DBUpdates{
359 NextFunc: func(_ *driver.DBUpdate) error {
360 return io.EOF
361 },
362 },
363 LastSeqFunc: func() (string, error) {
364 return "99-last", nil
365 },
366 }, nil
367 },
368 },
369 }
370
371 updates := client.DBUpdates(context.Background())
372 for updates.Next() {
373
374 }
375 lastSeq, err := updates.LastSeq()
376 if err != nil {
377 t.Fatal(err)
378 }
379 if lastSeq != "99-last" {
380 t.Errorf("Unexpected lastSeq: %s", lastSeq)
381 }
382 })
383 }
384
View as plain text