~nskaggs/+junk/xenial-test

« back to all changes in this revision

Viewing changes to src/google.golang.org/cloud/datastore/query_test.go

  • Committer: Nicholas Skaggs
  • Date: 2016-10-24 20:56:05 UTC
  • Revision ID: nicholas.skaggs@canonical.com-20161024205605-z8lta0uvuhtxwzwl
Initi with beta15

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Copyright 2014 Google Inc. All Rights Reserved.
 
2
//
 
3
// Licensed under the Apache License, Version 2.0 (the "License");
 
4
// you may not use this file except in compliance with the License.
 
5
// You may obtain a copy of the License at
 
6
//
 
7
//      http://www.apache.org/licenses/LICENSE-2.0
 
8
//
 
9
// Unless required by applicable law or agreed to in writing, software
 
10
// distributed under the License is distributed on an "AS IS" BASIS,
 
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 
12
// See the License for the specific language governing permissions and
 
13
// limitations under the License.
 
14
 
 
15
package datastore
 
16
 
 
17
import (
 
18
        "bytes"
 
19
        "fmt"
 
20
        "io/ioutil"
 
21
        "net/http"
 
22
        "reflect"
 
23
        "testing"
 
24
 
 
25
        "github.com/golang/protobuf/proto"
 
26
        "google.golang.org/cloud"
 
27
        pb "google.golang.org/cloud/internal/datastore"
 
28
)
 
29
 
 
30
var (
 
31
        key1 = &pb.Key{
 
32
                PathElement: []*pb.Key_PathElement{
 
33
                        {
 
34
                                Kind: proto.String("Gopher"),
 
35
                                Id:   proto.Int64(6),
 
36
                        },
 
37
                },
 
38
        }
 
39
        key2 = &pb.Key{
 
40
                PathElement: []*pb.Key_PathElement{
 
41
                        {
 
42
                                Kind: proto.String("Gopher"),
 
43
                                Id:   proto.Int64(6),
 
44
                        },
 
45
                        {
 
46
                                Kind: proto.String("Gopher"),
 
47
                                Id:   proto.Int64(8),
 
48
                        },
 
49
                },
 
50
        }
 
51
)
 
52
 
 
53
type fakeTransport struct {
 
54
        Handler func(req proto.Message, resp proto.Message) (err error)
 
55
}
 
56
 
 
57
func (t *fakeTransport) RoundTrip(req *http.Request) (*http.Response, error) {
 
58
 
 
59
        body, err := ioutil.ReadAll(req.Body)
 
60
 
 
61
        var in pb.RunQueryRequest
 
62
        var resp http.Response
 
63
        if err = proto.Unmarshal(body, &in); err != nil {
 
64
                // Get back an error
 
65
                resp = http.Response{
 
66
                        StatusCode: http.StatusBadRequest,
 
67
                }
 
68
        } else {
 
69
                // Run our fake query and serialize the response
 
70
                var out pb.RunQueryResponse
 
71
                err := t.Handler(&in, &out)
 
72
                if err != nil {
 
73
 
 
74
                        resp = http.Response{
 
75
                                StatusCode: http.StatusBadRequest,
 
76
                        }
 
77
                } else {
 
78
                        payload, err := proto.Marshal(&out)
 
79
                        if err != nil {
 
80
                                resp = http.Response{
 
81
                                        StatusCode: http.StatusBadRequest,
 
82
                                }
 
83
                        } else {
 
84
                                resp = http.Response{
 
85
                                        StatusCode: http.StatusOK,
 
86
                                        Body:       ioutil.NopCloser(bytes.NewBuffer(payload)),
 
87
                                }
 
88
                        }
 
89
 
 
90
                }
 
91
        }
 
92
 
 
93
        // Set common response fields
 
94
        resp.Proto = "HTTP/1.0"
 
95
        resp.ProtoMajor = 1
 
96
        resp.ProtoMinor = 1
 
97
 
 
98
        return &resp, nil
 
99
}
 
100
 
 
101
func fakeRunQuery(in *pb.RunQueryRequest, out *pb.RunQueryResponse) error {
 
102
        expectedIn := &pb.RunQueryRequest{
 
103
                Query: &pb.Query{
 
104
                        Kind: []*pb.KindExpression{&pb.KindExpression{Name: proto.String("Gopher")}},
 
105
                },
 
106
        }
 
107
        if !proto.Equal(in, expectedIn) {
 
108
                return fmt.Errorf("unsupported argument: got %v want %v", in, expectedIn)
 
109
        }
 
110
        *out = pb.RunQueryResponse{
 
111
                Batch: &pb.QueryResultBatch{
 
112
                        MoreResults:      pb.QueryResultBatch_NO_MORE_RESULTS.Enum(),
 
113
                        EntityResultType: pb.EntityResult_FULL.Enum(),
 
114
                        EntityResult: []*pb.EntityResult{
 
115
                                &pb.EntityResult{
 
116
                                        Entity: &pb.Entity{
 
117
                                                Key: key1,
 
118
                                                Property: []*pb.Property{
 
119
                                                        {
 
120
                                                                Name:  proto.String("Name"),
 
121
                                                                Value: &pb.Value{StringValue: proto.String("George")},
 
122
                                                        },
 
123
                                                        {
 
124
                                                                Name: proto.String("Height"),
 
125
                                                                Value: &pb.Value{
 
126
                                                                        IntegerValue: proto.Int64(32),
 
127
                                                                },
 
128
                                                        },
 
129
                                                },
 
130
                                        },
 
131
                                },
 
132
                                &pb.EntityResult{
 
133
                                        Entity: &pb.Entity{
 
134
                                                Key: key2,
 
135
                                                Property: []*pb.Property{
 
136
                                                        {
 
137
                                                                Name:  proto.String("Name"),
 
138
                                                                Value: &pb.Value{StringValue: proto.String("Rufus")},
 
139
                                                        },
 
140
                                                        // No height for Rufus.
 
141
                                                },
 
142
                                        },
 
143
                                },
 
144
                        },
 
145
                },
 
146
        }
 
147
        return nil
 
148
}
 
149
 
 
150
type StructThatImplementsPLS struct{}
 
151
 
 
152
func (StructThatImplementsPLS) Load(p []Property) error   { return nil }
 
153
func (StructThatImplementsPLS) Save() ([]Property, error) { return nil, nil }
 
154
 
 
155
var _ PropertyLoadSaver = StructThatImplementsPLS{}
 
156
 
 
157
type StructPtrThatImplementsPLS struct{}
 
158
 
 
159
func (*StructPtrThatImplementsPLS) Load(p []Property) error   { return nil }
 
160
func (*StructPtrThatImplementsPLS) Save() ([]Property, error) { return nil, nil }
 
161
 
 
162
var _ PropertyLoadSaver = &StructPtrThatImplementsPLS{}
 
163
 
 
164
type PropertyMap map[string]Property
 
165
 
 
166
func (m PropertyMap) Load(props []Property) error {
 
167
        for _, p := range props {
 
168
                m[p.Name] = p
 
169
        }
 
170
        return nil
 
171
}
 
172
 
 
173
func (m PropertyMap) Save() ([]Property, error) {
 
174
        props := make([]Property, 0, len(m))
 
175
        for _, p := range m {
 
176
                props = append(props, p)
 
177
        }
 
178
        return props, nil
 
179
}
 
180
 
 
181
var _ PropertyLoadSaver = PropertyMap{}
 
182
 
 
183
type Gopher struct {
 
184
        Name   string
 
185
        Height int
 
186
}
 
187
 
 
188
// typeOfEmptyInterface is the type of interface{}, but we can't use
 
189
// reflect.TypeOf((interface{})(nil)) directly because TypeOf takes an
 
190
// interface{}.
 
191
var typeOfEmptyInterface = reflect.TypeOf((*interface{})(nil)).Elem()
 
192
 
 
193
func TestCheckMultiArg(t *testing.T) {
 
194
        testCases := []struct {
 
195
                v        interface{}
 
196
                mat      multiArgType
 
197
                elemType reflect.Type
 
198
        }{
 
199
                // Invalid cases.
 
200
                {nil, multiArgTypeInvalid, nil},
 
201
                {Gopher{}, multiArgTypeInvalid, nil},
 
202
                {&Gopher{}, multiArgTypeInvalid, nil},
 
203
                {PropertyList{}, multiArgTypeInvalid, nil}, // This is a special case.
 
204
                {PropertyMap{}, multiArgTypeInvalid, nil},
 
205
                {[]*PropertyList(nil), multiArgTypeInvalid, nil},
 
206
                {[]*PropertyMap(nil), multiArgTypeInvalid, nil},
 
207
                {[]**Gopher(nil), multiArgTypeInvalid, nil},
 
208
                {[]*interface{}(nil), multiArgTypeInvalid, nil},
 
209
                // Valid cases.
 
210
                {
 
211
                        []PropertyList(nil),
 
212
                        multiArgTypePropertyLoadSaver,
 
213
                        reflect.TypeOf(PropertyList{}),
 
214
                },
 
215
                {
 
216
                        []PropertyMap(nil),
 
217
                        multiArgTypePropertyLoadSaver,
 
218
                        reflect.TypeOf(PropertyMap{}),
 
219
                },
 
220
                {
 
221
                        []StructThatImplementsPLS(nil),
 
222
                        multiArgTypePropertyLoadSaver,
 
223
                        reflect.TypeOf(StructThatImplementsPLS{}),
 
224
                },
 
225
                {
 
226
                        []StructPtrThatImplementsPLS(nil),
 
227
                        multiArgTypePropertyLoadSaver,
 
228
                        reflect.TypeOf(StructPtrThatImplementsPLS{}),
 
229
                },
 
230
                {
 
231
                        []Gopher(nil),
 
232
                        multiArgTypeStruct,
 
233
                        reflect.TypeOf(Gopher{}),
 
234
                },
 
235
                {
 
236
                        []*Gopher(nil),
 
237
                        multiArgTypeStructPtr,
 
238
                        reflect.TypeOf(Gopher{}),
 
239
                },
 
240
                {
 
241
                        []interface{}(nil),
 
242
                        multiArgTypeInterface,
 
243
                        typeOfEmptyInterface,
 
244
                },
 
245
        }
 
246
        for _, tc := range testCases {
 
247
                mat, elemType := checkMultiArg(reflect.ValueOf(tc.v))
 
248
                if mat != tc.mat || elemType != tc.elemType {
 
249
                        t.Errorf("checkMultiArg(%T): got %v, %v want %v, %v",
 
250
                                tc.v, mat, elemType, tc.mat, tc.elemType)
 
251
                }
 
252
        }
 
253
}
 
254
 
 
255
func TestSimpleQuery(t *testing.T) {
 
256
        struct1 := Gopher{Name: "George", Height: 32}
 
257
        struct2 := Gopher{Name: "Rufus"}
 
258
        pList1 := PropertyList{
 
259
                {
 
260
                        Name:  "Name",
 
261
                        Value: "George",
 
262
                },
 
263
                {
 
264
                        Name:  "Height",
 
265
                        Value: int64(32),
 
266
                },
 
267
        }
 
268
        pList2 := PropertyList{
 
269
                {
 
270
                        Name:  "Name",
 
271
                        Value: "Rufus",
 
272
                },
 
273
        }
 
274
        pMap1 := PropertyMap{
 
275
                "Name": Property{
 
276
                        Name:  "Name",
 
277
                        Value: "George",
 
278
                },
 
279
                "Height": Property{
 
280
                        Name:  "Height",
 
281
                        Value: int64(32),
 
282
                },
 
283
        }
 
284
        pMap2 := PropertyMap{
 
285
                "Name": Property{
 
286
                        Name:  "Name",
 
287
                        Value: "Rufus",
 
288
                },
 
289
        }
 
290
 
 
291
        testCases := []struct {
 
292
                dst  interface{}
 
293
                want interface{}
 
294
        }{
 
295
                // The destination must have type *[]P, *[]S or *[]*S, for some non-interface
 
296
                // type P such that *P implements PropertyLoadSaver, or for some struct type S.
 
297
                {new([]Gopher), &[]Gopher{struct1, struct2}},
 
298
                {new([]*Gopher), &[]*Gopher{&struct1, &struct2}},
 
299
                {new([]PropertyList), &[]PropertyList{pList1, pList2}},
 
300
                {new([]PropertyMap), &[]PropertyMap{pMap1, pMap2}},
 
301
 
 
302
                // Any other destination type is invalid.
 
303
                {0, nil},
 
304
                {Gopher{}, nil},
 
305
                {PropertyList{}, nil},
 
306
                {PropertyMap{}, nil},
 
307
                {[]int{}, nil},
 
308
                {[]Gopher{}, nil},
 
309
                {[]PropertyList{}, nil},
 
310
                {new(int), nil},
 
311
                {new(Gopher), nil},
 
312
                {new(PropertyList), nil}, // This is a special case.
 
313
                {new(PropertyMap), nil},
 
314
                {new([]int), nil},
 
315
                {new([]map[int]int), nil},
 
316
                {new([]map[string]Property), nil},
 
317
                {new([]map[string]interface{}), nil},
 
318
                {new([]*int), nil},
 
319
                {new([]*map[int]int), nil},
 
320
                {new([]*map[string]Property), nil},
 
321
                {new([]*map[string]interface{}), nil},
 
322
                {new([]**Gopher), nil},
 
323
                {new([]*PropertyList), nil},
 
324
                {new([]*PropertyMap), nil},
 
325
        }
 
326
        for _, tc := range testCases {
 
327
                nCall := 0
 
328
                ctx := cloud.NewContext("queryTest", &http.Client{
 
329
                        Transport: &fakeTransport{Handler: func(in proto.Message, out proto.Message) error {
 
330
                                nCall++
 
331
                                return fakeRunQuery(in.(*pb.RunQueryRequest), out.(*pb.RunQueryResponse))
 
332
                        }}})
 
333
 
 
334
                var (
 
335
                        expectedErr   error
 
336
                        expectedNCall int
 
337
                )
 
338
                if tc.want == nil {
 
339
                        expectedErr = ErrInvalidEntityType
 
340
                } else {
 
341
                        expectedNCall = 1
 
342
                }
 
343
                keys, err := NewQuery("Gopher").GetAll(ctx, tc.dst)
 
344
                if err != expectedErr {
 
345
                        t.Errorf("dst type %T: got error %v, want %v", tc.dst, err, expectedErr)
 
346
                        continue
 
347
                }
 
348
                if nCall != expectedNCall {
 
349
                        t.Errorf("dst type %T: Context.Call was called an incorrect number of times: got %d want %d", tc.dst, nCall, expectedNCall)
 
350
                        continue
 
351
                }
 
352
                if err != nil {
 
353
                        continue
 
354
                }
 
355
 
 
356
                key1 := NewKey(ctx, "Gopher", "", 6, nil)
 
357
                expectedKeys := []*Key{
 
358
                        key1,
 
359
                        NewKey(ctx, "Gopher", "", 8, key1),
 
360
                }
 
361
                if l1, l2 := len(keys), len(expectedKeys); l1 != l2 {
 
362
                        t.Errorf("dst type %T: got %d keys, want %d keys", tc.dst, l1, l2)
 
363
                        continue
 
364
                }
 
365
                for i, key := range keys {
 
366
                        if !keysEqual(key, expectedKeys[i]) {
 
367
                                t.Errorf("dst type %T: got key #%d %v, want %v", tc.dst, i, key, expectedKeys[i])
 
368
                                continue
 
369
                        }
 
370
                }
 
371
 
 
372
                if !reflect.DeepEqual(tc.dst, tc.want) {
 
373
                        t.Errorf("dst type %T: Entities got %+v, want %+v", tc.dst, tc.dst, tc.want)
 
374
                        continue
 
375
                }
 
376
        }
 
377
}
 
378
 
 
379
// keysEqual is like (*Key).Equal, but ignores the App ID.
 
380
func keysEqual(a, b *Key) bool {
 
381
        for a != nil && b != nil {
 
382
                if a.Kind() != b.Kind() || a.Name() != b.Name() || a.ID() != b.ID() {
 
383
                        return false
 
384
                }
 
385
                a, b = a.Parent(), b.Parent()
 
386
        }
 
387
        return a == b
 
388
}
 
389
 
 
390
func TestQueriesAreImmutable(t *testing.T) {
 
391
        // Test that deriving q2 from q1 does not modify q1.
 
392
        q0 := NewQuery("foo")
 
393
        q1 := NewQuery("foo")
 
394
        q2 := q1.Offset(2)
 
395
        if !reflect.DeepEqual(q0, q1) {
 
396
                t.Errorf("q0 and q1 were not equal")
 
397
        }
 
398
        if reflect.DeepEqual(q1, q2) {
 
399
                t.Errorf("q1 and q2 were equal")
 
400
        }
 
401
 
 
402
        // Test that deriving from q4 twice does not conflict, even though
 
403
        // q4 has a long list of order clauses. This tests that the arrays
 
404
        // backed by a query's slice of orders are not shared.
 
405
        f := func() *Query {
 
406
                q := NewQuery("bar")
 
407
                // 47 is an ugly number that is unlikely to be near a re-allocation
 
408
                // point in repeated append calls. For example, it's not near a power
 
409
                // of 2 or a multiple of 10.
 
410
                for i := 0; i < 47; i++ {
 
411
                        q = q.Order(fmt.Sprintf("x%d", i))
 
412
                }
 
413
                return q
 
414
        }
 
415
        q3 := f().Order("y")
 
416
        q4 := f()
 
417
        q5 := q4.Order("y")
 
418
        q6 := q4.Order("z")
 
419
        if !reflect.DeepEqual(q3, q5) {
 
420
                t.Errorf("q3 and q5 were not equal")
 
421
        }
 
422
        if reflect.DeepEqual(q5, q6) {
 
423
                t.Errorf("q5 and q6 were equal")
 
424
        }
 
425
}
 
426
 
 
427
func TestFilterParser(t *testing.T) {
 
428
        testCases := []struct {
 
429
                filterStr     string
 
430
                wantOK        bool
 
431
                wantFieldName string
 
432
                wantOp        operator
 
433
        }{
 
434
                // Supported ops.
 
435
                {"x<", true, "x", lessThan},
 
436
                {"x <", true, "x", lessThan},
 
437
                {"x  <", true, "x", lessThan},
 
438
                {"   x   <  ", true, "x", lessThan},
 
439
                {"x <=", true, "x", lessEq},
 
440
                {"x =", true, "x", equal},
 
441
                {"x >=", true, "x", greaterEq},
 
442
                {"x >", true, "x", greaterThan},
 
443
                {"in >", true, "in", greaterThan},
 
444
                {"in>", true, "in", greaterThan},
 
445
                // Valid but (currently) unsupported ops.
 
446
                {"x!=", false, "", 0},
 
447
                {"x !=", false, "", 0},
 
448
                {" x  !=  ", false, "", 0},
 
449
                {"x IN", false, "", 0},
 
450
                {"x in", false, "", 0},
 
451
                // Invalid ops.
 
452
                {"x EQ", false, "", 0},
 
453
                {"x lt", false, "", 0},
 
454
                {"x <>", false, "", 0},
 
455
                {"x >>", false, "", 0},
 
456
                {"x ==", false, "", 0},
 
457
                {"x =<", false, "", 0},
 
458
                {"x =>", false, "", 0},
 
459
                {"x !", false, "", 0},
 
460
                {"x ", false, "", 0},
 
461
                {"x", false, "", 0},
 
462
                // Quoted and interesting field names.
 
463
                {"x > y =", true, "x > y", equal},
 
464
                {"` x ` =", true, " x ", equal},
 
465
                {`" x " =`, true, " x ", equal},
 
466
                {`" \"x " =`, true, ` "x `, equal},
 
467
                {`" x =`, false, "", 0},
 
468
                {`" x ="`, false, "", 0},
 
469
                {"` x \" =", false, "", 0},
 
470
        }
 
471
        for _, tc := range testCases {
 
472
                q := NewQuery("foo").Filter(tc.filterStr, 42)
 
473
                if ok := q.err == nil; ok != tc.wantOK {
 
474
                        t.Errorf("%q: ok=%t, want %t", tc.filterStr, ok, tc.wantOK)
 
475
                        continue
 
476
                }
 
477
                if !tc.wantOK {
 
478
                        continue
 
479
                }
 
480
                if len(q.filter) != 1 {
 
481
                        t.Errorf("%q: len=%d, want %d", tc.filterStr, len(q.filter), 1)
 
482
                        continue
 
483
                }
 
484
                got, want := q.filter[0], filter{tc.wantFieldName, tc.wantOp, 42}
 
485
                if got != want {
 
486
                        t.Errorf("%q: got %v, want %v", tc.filterStr, got, want)
 
487
                        continue
 
488
                }
 
489
        }
 
490
}