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