1package kivik
2
3import (
4	"context"
5	"encoding/json"
6	"fmt"
7	"io"
8	"testing"
9	"time"
10
11	"github.com/flimzy/diff"
12	"github.com/flimzy/testy"
13)
14
15type TestFeed struct {
16	max      int64
17	i        int64
18	closeErr error
19}
20
21var _ iterator = &TestFeed{}
22
23func (f *TestFeed) Close() error { return f.closeErr }
24func (f *TestFeed) Next(ifce interface{}) error {
25	i, ok := ifce.(*int64)
26	if ok {
27		*i = f.i
28		f.i++
29		if f.i > f.max {
30			return io.EOF
31		}
32		time.Sleep(5 * time.Millisecond)
33		return nil
34	}
35	panic(fmt.Sprintf("unknown type: %T", ifce))
36}
37
38func TestIterator(t *testing.T) {
39	iter := newIterator(context.Background(), &TestFeed{max: 10}, func() interface{} { var i int64; return &i }())
40	expected := []int64{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
41	result := []int64{}
42	for iter.Next() {
43		val, ok := iter.curVal.(*int64)
44		if !ok {
45			panic("Unexpected type")
46		}
47		result = append(result, *val)
48	}
49	if err := iter.Err(); err != nil {
50		t.Errorf("Unexpected error: %s", err)
51	}
52	if d := diff.AsJSON(expected, result); d != nil {
53		t.Errorf("Unexpected result:\n%s\n", d)
54	}
55}
56
57func TestCancelledIterator(t *testing.T) {
58	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Millisecond)
59	defer cancel()
60	iter := newIterator(ctx, &TestFeed{max: 10000}, func() interface{} { var i int64; return &i }())
61	for iter.Next() {
62	}
63	if err := iter.Err(); err.Error() != "context deadline exceeded" {
64		t.Errorf("Unexpected error: %s", err)
65	}
66}
67
68func TestIteratorScan(t *testing.T) {
69	type Test struct {
70		name     string
71		dst      interface{}
72		input    json.RawMessage
73		expected interface{}
74		status   int
75		err      string
76	}
77	tests := []Test{
78		{
79			name:   "non-pointer",
80			dst:    map[string]string{},
81			input:  []byte(`{"foo":123.4}`),
82			status: StatusBadRequest,
83			err:    "kivik: destination is not a pointer",
84		},
85		func() Test {
86			dst := map[string]interface{}{}
87			expected := map[string]interface{}{"foo": 123.4}
88			return Test{
89				name:     "standard unmarshal",
90				dst:      &dst,
91				input:    []byte(`{"foo":123.4}`),
92				expected: &expected,
93			}
94		}(),
95		func() Test {
96			dst := map[string]interface{}{}
97			return Test{
98				name:   "invalid JSON",
99				dst:    &dst,
100				input:  []byte(`invalid JSON`),
101				status: StatusBadResponse,
102				err:    "invalid character 'i' looking for beginning of value",
103			}
104		}(),
105		func() Test {
106			var dst *json.RawMessage
107			return Test{
108				name:   "nil *json.RawMessage",
109				dst:    dst,
110				input:  []byte(`{"foo":123.4}`),
111				status: StatusBadRequest,
112				err:    "kivik: destination pointer is nil",
113			}
114		}(),
115		func() Test {
116			var dst *[]byte
117			return Test{
118				name:   "nil *[]byte",
119				dst:    dst,
120				input:  []byte(`{"foo":123.4}`),
121				status: StatusBadRequest,
122				err:    "kivik: destination pointer is nil",
123			}
124		}(),
125		func() Test {
126			dst := []byte{}
127			expected := []byte(`{"foo":123.4}`)
128			return Test{
129				name:     "[]byte",
130				dst:      &dst,
131				input:    []byte(`{"foo":123.4}`),
132				expected: &expected,
133			}
134		}(),
135		func() Test {
136			dst := json.RawMessage{}
137			expected := json.RawMessage(`{"foo":123.4}`)
138			return Test{
139				name:     "json.RawMessage",
140				dst:      &dst,
141				input:    []byte(`{"foo":123.4}`),
142				expected: &expected,
143			}
144		}(),
145	}
146	for _, test := range tests {
147		t.Run(test.name, func(t *testing.T) {
148			err := scan(test.dst, test.input)
149			testy.StatusError(t, test.err, test.status, err)
150			if d := diff.Interface(test.expected, test.dst); d != nil {
151				t.Error(d)
152			}
153		})
154	}
155}
156