1
// Copyright 2014 Google Inc. All Rights Reserved.
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
7
// http://www.apache.org/licenses/LICENSE-2.0
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.
25
"github.com/golang/protobuf/proto"
26
"google.golang.org/cloud"
27
pb "google.golang.org/cloud/internal/datastore"
32
PathElement: []*pb.Key_PathElement{
34
Kind: proto.String("Gopher"),
40
PathElement: []*pb.Key_PathElement{
42
Kind: proto.String("Gopher"),
46
Kind: proto.String("Gopher"),
53
type fakeTransport struct {
54
Handler func(req proto.Message, resp proto.Message) (err error)
57
func (t *fakeTransport) RoundTrip(req *http.Request) (*http.Response, error) {
59
body, err := ioutil.ReadAll(req.Body)
61
var in pb.RunQueryRequest
62
var resp http.Response
63
if err = proto.Unmarshal(body, &in); err != nil {
66
StatusCode: http.StatusBadRequest,
69
// Run our fake query and serialize the response
70
var out pb.RunQueryResponse
71
err := t.Handler(&in, &out)
75
StatusCode: http.StatusBadRequest,
78
payload, err := proto.Marshal(&out)
81
StatusCode: http.StatusBadRequest,
85
StatusCode: http.StatusOK,
86
Body: ioutil.NopCloser(bytes.NewBuffer(payload)),
93
// Set common response fields
94
resp.Proto = "HTTP/1.0"
101
func fakeRunQuery(in *pb.RunQueryRequest, out *pb.RunQueryResponse) error {
102
expectedIn := &pb.RunQueryRequest{
104
Kind: []*pb.KindExpression{&pb.KindExpression{Name: proto.String("Gopher")}},
107
if !proto.Equal(in, expectedIn) {
108
return fmt.Errorf("unsupported argument: got %v want %v", in, expectedIn)
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{
118
Property: []*pb.Property{
120
Name: proto.String("Name"),
121
Value: &pb.Value{StringValue: proto.String("George")},
124
Name: proto.String("Height"),
126
IntegerValue: proto.Int64(32),
135
Property: []*pb.Property{
137
Name: proto.String("Name"),
138
Value: &pb.Value{StringValue: proto.String("Rufus")},
140
// No height for Rufus.
150
type StructThatImplementsPLS struct{}
152
func (StructThatImplementsPLS) Load(p []Property) error { return nil }
153
func (StructThatImplementsPLS) Save() ([]Property, error) { return nil, nil }
155
var _ PropertyLoadSaver = StructThatImplementsPLS{}
157
type StructPtrThatImplementsPLS struct{}
159
func (*StructPtrThatImplementsPLS) Load(p []Property) error { return nil }
160
func (*StructPtrThatImplementsPLS) Save() ([]Property, error) { return nil, nil }
162
var _ PropertyLoadSaver = &StructPtrThatImplementsPLS{}
164
type PropertyMap map[string]Property
166
func (m PropertyMap) Load(props []Property) error {
167
for _, p := range props {
173
func (m PropertyMap) Save() ([]Property, error) {
174
props := make([]Property, 0, len(m))
175
for _, p := range m {
176
props = append(props, p)
181
var _ PropertyLoadSaver = PropertyMap{}
188
// typeOfEmptyInterface is the type of interface{}, but we can't use
189
// reflect.TypeOf((interface{})(nil)) directly because TypeOf takes an
191
var typeOfEmptyInterface = reflect.TypeOf((*interface{})(nil)).Elem()
193
func TestCheckMultiArg(t *testing.T) {
194
testCases := []struct {
197
elemType reflect.Type
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},
212
multiArgTypePropertyLoadSaver,
213
reflect.TypeOf(PropertyList{}),
217
multiArgTypePropertyLoadSaver,
218
reflect.TypeOf(PropertyMap{}),
221
[]StructThatImplementsPLS(nil),
222
multiArgTypePropertyLoadSaver,
223
reflect.TypeOf(StructThatImplementsPLS{}),
226
[]StructPtrThatImplementsPLS(nil),
227
multiArgTypePropertyLoadSaver,
228
reflect.TypeOf(StructPtrThatImplementsPLS{}),
233
reflect.TypeOf(Gopher{}),
237
multiArgTypeStructPtr,
238
reflect.TypeOf(Gopher{}),
242
multiArgTypeInterface,
243
typeOfEmptyInterface,
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)
255
func TestSimpleQuery(t *testing.T) {
256
struct1 := Gopher{Name: "George", Height: 32}
257
struct2 := Gopher{Name: "Rufus"}
258
pList1 := PropertyList{
268
pList2 := PropertyList{
274
pMap1 := PropertyMap{
284
pMap2 := PropertyMap{
291
testCases := []struct {
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}},
302
// Any other destination type is invalid.
305
{PropertyList{}, nil},
306
{PropertyMap{}, nil},
309
{[]PropertyList{}, nil},
312
{new(PropertyList), nil}, // This is a special case.
313
{new(PropertyMap), nil},
315
{new([]map[int]int), nil},
316
{new([]map[string]Property), nil},
317
{new([]map[string]interface{}), 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},
326
for _, tc := range testCases {
328
ctx := cloud.NewContext("queryTest", &http.Client{
329
Transport: &fakeTransport{Handler: func(in proto.Message, out proto.Message) error {
331
return fakeRunQuery(in.(*pb.RunQueryRequest), out.(*pb.RunQueryResponse))
339
expectedErr = ErrInvalidEntityType
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)
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)
356
key1 := NewKey(ctx, "Gopher", "", 6, nil)
357
expectedKeys := []*Key{
359
NewKey(ctx, "Gopher", "", 8, key1),
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)
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])
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)
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() {
385
a, b = a.Parent(), b.Parent()
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")
395
if !reflect.DeepEqual(q0, q1) {
396
t.Errorf("q0 and q1 were not equal")
398
if reflect.DeepEqual(q1, q2) {
399
t.Errorf("q1 and q2 were equal")
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.
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))
419
if !reflect.DeepEqual(q3, q5) {
420
t.Errorf("q3 and q5 were not equal")
422
if reflect.DeepEqual(q5, q6) {
423
t.Errorf("q5 and q6 were equal")
427
func TestFilterParser(t *testing.T) {
428
testCases := []struct {
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},
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},
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},
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)
480
if len(q.filter) != 1 {
481
t.Errorf("%q: len=%d, want %d", tc.filterStr, len(q.filter), 1)
484
got, want := q.filter[0], filter{tc.wantFieldName, tc.wantOp, 42}
486
t.Errorf("%q: got %v, want %v", tc.filterStr, got, want)