Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(109)

Side by Side Diff: go/src/infra/gae/libs/wrapper/memory/datastore_test.go

Issue 1222903002: Refactor current GAE abstraction library to be free of the SDK* (Closed) Base URL: https://chromium.googlesource.com/infra/infra.git@master
Patch Set: more fixes Created 5 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 package memory
6
7 import (
8 "fmt"
9 "infra/gae/libs/meta"
10 "infra/gae/libs/wrapper"
11 "math"
12 "testing"
13
14 . "github.com/smartystreets/goconvey/convey"
15 "golang.org/x/net/context"
16
17 "appengine/datastore"
18 )
19
20 func TestDatastoreKinder(t *testing.T) {
21 t.Parallel()
22
23 Convey("Datastore kinds and keys", t, func() {
24 c := Use(context.Background())
25 ds := wrapper.GetDS(c)
26 So(ds, ShouldNotBeNil)
27
28 Convey("implements DSKinder", func() {
29 type Foo struct{}
30 So(ds.Kind(&Foo{}), ShouldEqual, "Foo")
31
32 Convey("which can be tweaked by DSKindSetter", func() {
33 ds.SetKindNameResolver(func(interface{}) string { return "spam" })
34 So(ds.Kind(&Foo{}), ShouldEqual, "spam")
35
36 Convey("and it retains the function so you can s tack them", func() {
37 cur := ds.KindNameResolver()
38 ds.SetKindNameResolver(func(o interface{ }) string { return "wat" + cur(o) })
39 So(ds.Kind(&Foo{}), ShouldEqual, "watspa m")
40 })
41 })
42 })
43
44 Convey("implements DSNewKeyer", func() {
45 Convey("NewKey", func() {
46 key := ds.NewKey("nerd", "stringID", 0, nil)
47 So(key, ShouldNotBeNil)
48 So(key.Kind(), ShouldEqual, "nerd")
49 So(key.StringID(), ShouldEqual, "stringID")
50 So(key.IntID(), ShouldEqual, 0)
51 So(key.Parent(), ShouldBeNil)
52 So(key.AppID(), ShouldEqual, "dev~my~app")
53 So(key.Namespace(), ShouldEqual, "")
54 So(key.String(), ShouldEqual, "/nerd,stringID")
55 So(key.Incomplete(), ShouldBeFalse)
56 So(keyValid("", key, userKeyOnly), ShouldBeTrue)
57
58 chkey := ds.NewKey("wat", "", 100, key)
59 So(chkey, ShouldNotBeNil)
60 So(chkey.Kind(), ShouldEqual, "wat")
61 So(chkey.StringID(), ShouldEqual, "")
62 So(chkey.IntID(), ShouldEqual, 100)
63 So(chkey.Parent(), ShouldEqual, key)
64 So(chkey.AppID(), ShouldEqual, "dev~my~app")
65 So(chkey.Namespace(), ShouldEqual, "")
66 So(chkey.String(), ShouldEqual, "/nerd,stringID/ wat,100")
67 So(key.Incomplete(), ShouldBeFalse)
68 So(keyValid("", chkey, userKeyOnly), ShouldBeTru e)
69
70 incompl := ds.NewKey("sup", "", 0, key)
71 So(incompl, ShouldNotBeNil)
72 So(incompl.Incomplete(), ShouldBeTrue)
73 So(keyValid("", incompl, userKeyOnly), ShouldBeT rue)
74 So(incompl.String(), ShouldEqual, "/nerd,stringI D/sup,0")
75
76 bad := ds.NewKey("nooo", "", 10, incompl)
77 So(bad, ShouldNotBeNil)
78 So(bad.Incomplete(), ShouldBeFalse)
79 So(keyValid("", bad, userKeyOnly), ShouldBeFalse )
80 So(bad.String(), ShouldEqual, "/nerd,stringID/su p,0/nooo,10")
81
82 So(rootKey(bad), ShouldEqual, key)
83
84 Convey("other key validation", func() {
85 So(keyValid("", nil, userKeyOnly), Shoul dBeFalse)
86
87 key := ds.NewKey("", "", 0, nil)
88 So(key, ShouldNotBeNil)
89
90 So(keyValid("", key, userKeyOnly), Shoul dBeFalse)
91
92 key = ds.NewKey("noop", "power level", 9 000, nil)
93 So(key, ShouldNotBeNil)
94
95 So(keyValid("", key, userKeyOnly), Shoul dBeFalse)
96 })
97 })
98
99 Convey("NewKeyObj", func() {
100 type Foo struct {
101 _knd string `goon:"kind,coool" `
102 ID int64 `goon:"id"`
103 Parent *datastore.Key `goon:"parent"`
104 }
105 f := &Foo{ID: 100}
106 k := ds.NewKeyObj(f)
107 So(k.String(), ShouldEqual, "/coool,100")
108
109 f.Parent = k
110 f._knd = "weevils"
111 f.ID = 19
112 k = ds.NewKeyObj(f)
113 So(k.String(), ShouldEqual, "/coool,100/weevils, 19")
114
115 Convey("panics when you do a dumb thing", func() {
116 type Foo struct {
117 ID []byte `goon:"id"`
118 }
119 So(func() { ds.NewKeyObj(&Foo{}) }, Shou ldPanic)
120 })
121 })
122
123 Convey("NewKeyObjError", func() {
124 type Foo struct {
125 ID []byte `goon:"id"`
126 }
127 _, err := ds.NewKeyObjError(&Foo{})
128 So(err.Error(), ShouldContainSubstring, "must be int64 or string")
129 })
130 })
131
132 })
133 }
134
135 func TestDatastoreSingleReadWriter(t *testing.T) {
136 t.Parallel()
137
138 Convey("Datastore single reads and writes", t, func() {
139 c := Use(context.Background())
140 ds := wrapper.GetDS(c)
141 So(ds, ShouldNotBeNil)
142
143 Convey("implements DSSingleReadWriter", func() {
144 type Foo struct {
145 ID int64 `goon:"id" datastore:"-"`
146 Parent *datastore.Key `goon:"parent" datastore:" -"`
147 Val int
148 }
149
150 Convey("invalid keys break", func() {
151 k := ds.NewKeyObj(&Foo{})
152 f := &Foo{Parent: k}
153 So(ds.Get(f), ShouldEqual, datastore.ErrInvalidK ey)
154
155 _, err := ds.Put(f)
156 So(err, ShouldEqual, datastore.ErrInvalidKey)
157 })
158
159 Convey("getting objects that DNE is an error", func() {
160 So(ds.Get(&Foo{ID: 1}), ShouldEqual, datastore.E rrNoSuchEntity)
161 })
162
163 Convey("Can Put stuff", func() {
164 // with an incomplete key!
165 f := &Foo{Val: 10}
166 k, err := ds.Put(f)
167 So(err, ShouldBeNil)
168 So(k.String(), ShouldEqual, "/Foo,1")
169 So(ds.NewKeyObj(f), ShouldResemble, k)
170
171 Convey("and Get it back", func() {
172 newFoo := &Foo{ID: 1}
173 err := ds.Get(newFoo)
174 So(err, ShouldBeNil)
175 So(newFoo, ShouldResemble, f)
176
177 Convey("and we can Delete it", func() {
178 err := ds.Delete(ds.NewKey("Foo" , "", 1, nil))
179 So(err, ShouldBeNil)
180
181 err = ds.Get(newFoo)
182 So(err, ShouldEqual, datastore.E rrNoSuchEntity)
183 })
184 })
185 Convey("Deleteing with a bogus key is bad", func () {
186 err := ds.Delete(ds.NewKey("Foo", "wat", 100, nil))
187 So(err, ShouldEqual, datastore.ErrInvali dKey)
188 })
189 Convey("Deleteing a DNE entity is fine", func() {
190 err := ds.Delete(ds.NewKey("Foo", "wat", 0, nil))
191 So(err, ShouldBeNil)
192 })
193
194 Convey("serialization breaks in the normal ways" , func() {
195 type BadFoo struct {
196 _kind string `goon:"kind,Foo"`
197 ID int64 `goon:"id" datastor e:"-"`
198 Val uint8
199 }
200 _, err := ds.Put(&BadFoo{})
201 So(err.Error(), ShouldContainSubstring,
202 "unsupported struct field type: uint8")
203
204 err = ds.Get(&BadFoo{ID: 1})
205 So(err.Error(), ShouldContainSubstring,
206 "type mismatch: int versus uint8 ")
207 })
208
209 Convey("check that metadata works", func() {
210 val, _ := meta.GetEntityGroupVersion(c, k)
211 So(val, ShouldEqual, 1)
212
213 for i := 0; i < 10; i++ {
214 _, err = ds.Put(&Foo{Val: 10, Pa rent: k})
215 So(err, ShouldBeNil)
216 }
217 val, _ = meta.GetEntityGroupVersion(c, k )
218 So(val, ShouldEqual, 11)
219
220 Convey("ensure that group versions persi st across deletes", func() {
221 So(ds.Delete(k), ShouldBeNil)
222 for i := int64(1); i < 11; i++ {
223 So(ds.Delete(ds.NewKey(" Foo", "", i, k)), ShouldBeNil)
224 }
225 // TODO(riannucci): replace with a Count query instead of this cast
226 ents := ds.(*dsImpl).data.store. GetCollection("ents:")
227 num, _ := ents.GetTotals()
228 // /__entity_root_ids__,Foo
229 // /Foo,1/__entity_group__,1
230 // /Foo,1/__entity_group_ids__,1
231 So(num, ShouldEqual, 3)
232
233 version, err := curVersion(ents, groupMetaKey(k))
234 So(err, ShouldBeNil)
235 So(version, ShouldEqual, 22)
236
237 k, err := ds.Put(f)
238 So(err, ShouldBeNil)
239 val, _ := meta.GetEntityGroupVer sion(c, k)
240 So(val, ShouldEqual, 23)
241 })
242 })
243 })
244 })
245
246 Convey("implements DSTransactioner", func() {
247 type Foo struct {
248 ID int64 `goon:"id" datastore:"-"`
249 Parent *datastore.Key `goon:"parent" datastore:" -"`
250 Val int
251 }
252 Convey("Put", func() {
253 f := &Foo{Val: 10}
254 k, err := ds.Put(f)
255 So(err, ShouldBeNil)
256 So(k.String(), ShouldEqual, "/Foo,1")
257 So(ds.NewKeyObj(f), ShouldResemble, k)
258
259 Convey("can Put new entity groups", func() {
260 err := ds.RunInTransaction(func(c contex t.Context) error {
261 ds := wrapper.GetDS(c)
262 So(ds, ShouldNotBeNil)
263
264 f1 := &Foo{Val: 100}
265 k, err := ds.Put(f1)
266 So(err, ShouldBeNil)
267 So(k.String(), ShouldEqual, "/Fo o,2")
268
269 f2 := &Foo{Val: 200}
270 k, err = ds.Put(f2)
271 So(err, ShouldBeNil)
272 So(k.String(), ShouldEqual, "/Fo o,3")
273
274 return nil
275 }, &datastore.TransactionOptions{XG: tru e})
276 So(err, ShouldBeNil)
277
278 f := &Foo{ID: 2}
279 So(ds.Get(f), ShouldBeNil)
280 So(f.Val, ShouldEqual, 100)
281
282 f = &Foo{ID: 3}
283 So(ds.Get(f), ShouldBeNil)
284 So(f.Val, ShouldEqual, 200)
285 })
286
287 Convey("can Put new entities in a current group" , func() {
288 err := ds.RunInTransaction(func(c contex t.Context) error {
289 ds := wrapper.GetDS(c)
290 So(ds, ShouldNotBeNil)
291
292 f1 := &Foo{Val: 100, Parent: ds. NewKeyObj(f)}
293 k, err := ds.Put(f1)
294 So(err, ShouldBeNil)
295 So(k.String(), ShouldEqual, "/Fo o,1/Foo,1")
296
297 f2 := &Foo{Val: 200, Parent: ds. NewKeyObj(f)}
298 k, err = ds.Put(f2)
299 So(err, ShouldBeNil)
300 So(k.String(), ShouldEqual, "/Fo o,1/Foo,2")
301
302 return nil
303 }, nil)
304 So(err, ShouldBeNil)
305
306 f1 := &Foo{ID: 1, Parent: ds.NewKeyObj(& Foo{ID: 1})}
307 So(ds.Get(f1), ShouldBeNil)
308 So(f1.Val, ShouldEqual, 100)
309
310 f2 := &Foo{ID: 2, Parent: f1.Parent}
311 So(ds.Get(f2), ShouldBeNil)
312 So(f2.Val, ShouldEqual, 200)
313 })
314
315 Convey("Deletes work too", func() {
316 err := ds.RunInTransaction(func(c contex t.Context) error {
317 ds := wrapper.GetDS(c)
318 So(ds, ShouldNotBeNil)
319 So(ds.Delete(ds.NewKeyObj(f)), S houldBeNil)
320 return nil
321 }, nil)
322 So(err, ShouldBeNil)
323 So(ds.Get(f), ShouldEqual, datastore.Err NoSuchEntity)
324 })
325
326 Convey("A Get counts against your group count", func() {
327 err := ds.RunInTransaction(func(c contex t.Context) error {
328 ds := wrapper.GetDS(c)
329 f := &Foo{ID: 20}
330 So(ds.Get(f), ShouldEqual, datas tore.ErrNoSuchEntity)
331
332 f.ID = 1
333 So(ds.Get(f).Error(), ShouldCont ainSubstring, "cross-group")
334 return nil
335 }, nil)
336 So(err, ShouldBeNil)
337 })
338
339 Convey("Get takes a snapshot", func() {
340 err := ds.RunInTransaction(func(c contex t.Context) error {
341 txnDS := wrapper.GetDS(c)
342 So(txnDS, ShouldNotBeNil)
343
344 f := &Foo{ID: 1}
345 So(txnDS.Get(f), ShouldBeNil)
346 So(f.Val, ShouldEqual, 10)
347
348 // Don't ever do this in a real program unless you want to guarantee
349 // a failed transaction :)
350 f.Val = 11
351 _, err := ds.Put(f)
352 So(err, ShouldBeNil)
353
354 So(txnDS.Get(f), ShouldBeNil)
355 So(f.Val, ShouldEqual, 10)
356
357 return nil
358 }, nil)
359 So(err, ShouldBeNil)
360
361 f := &Foo{ID: 1}
362 So(ds.Get(f), ShouldBeNil)
363 So(f.Val, ShouldEqual, 11)
364
365 })
366
367 Convey("and snapshots are consistent even after Puts", func() {
368 err := ds.RunInTransaction(func(c contex t.Context) error {
369 txnDS := wrapper.GetDS(c)
370 So(txnDS, ShouldNotBeNil)
371
372 f := &Foo{ID: 1}
373 So(txnDS.Get(f), ShouldBeNil)
374 So(f.Val, ShouldEqual, 10)
375
376 // Don't ever do this in a real program unless you want to guarantee
377 // a failed transaction :)
378 f.Val = 11
379 _, err := ds.Put(f)
380 So(err, ShouldBeNil)
381
382 So(txnDS.Get(f), ShouldBeNil)
383 So(f.Val, ShouldEqual, 10)
384
385 f.Val = 20
386 _, err = txnDS.Put(f)
387 So(err, ShouldBeNil)
388
389 So(txnDS.Get(f), ShouldBeNil)
390 So(f.Val, ShouldEqual, 10) // st ill gets 10
391
392 return nil
393 }, nil)
394 So(err.Error(), ShouldContainSubstring, "concurrent")
395
396 f := &Foo{ID: 1}
397 So(ds.Get(f), ShouldBeNil)
398 So(f.Val, ShouldEqual, 11)
399 })
400
401 Convey("Reusing a transaction context is bad new s", func() {
402 txnDS := wrapper.Datastore(nil)
403 err := ds.RunInTransaction(func(c contex t.Context) error {
404 txnDS = wrapper.GetDS(c)
405 So(txnDS.Get(&Foo{ID: 1}), Shoul dBeNil)
406 return nil
407 }, nil)
408 So(err, ShouldBeNil)
409 So(txnDS.Get(&Foo{ID: 1}).Error(), Shoul dContainSubstring, "expired")
410 })
411
412 Convey("Nested transactions are rejected", func( ) {
413 err := ds.RunInTransaction(func(c contex t.Context) error {
414 err := wrapper.GetDS(c).RunInTra nsaction(func(c context.Context) error {
415 panic("noooo")
416 }, nil)
417 So(err.Error(), ShouldContainSub string, "nested transactions")
418 return nil
419 }, nil)
420 So(err, ShouldBeNil)
421 })
422
423 Convey("Concurrent transactions only accept one set of changes", func() {
424 // Note: I think this implementation is actually /slightly/ wrong.
425 // Accorting to my read of the docs for appengine, when you open a
426 // transaction it actually (essentially) holds a reference to the
427 // entire datastore. Our implementation takes a snapshot of the
428 // entity group as soon as something obs erves/affects it.
429 //
430 // That said... I'm not sure if there's really a semantic difference.
431 err := ds.RunInTransaction(func(c contex t.Context) error {
432 txnDS := wrapper.GetDS(c)
433 f := &Foo{ID: 1, Val: 21}
434 _, err = txnDS.Put(f)
435 So(err, ShouldBeNil)
436
437 err := ds.RunInTransaction(func( c context.Context) error {
438 txnDS := wrapper.GetDS(c )
439 f := &Foo{ID: 1, Val: 27 }
440 _, err := txnDS.Put(f)
441 So(err, ShouldBeNil)
442 return nil
443 }, nil)
444 So(err, ShouldBeNil)
445
446 return nil
447 }, nil)
448 So(err.Error(), ShouldContainSubstring, "concurrent")
449
450 f := &Foo{ID: 1}
451 So(ds.Get(f), ShouldBeNil)
452 So(f.Val, ShouldEqual, 27)
453 })
454
455 Convey("XG", func() {
456 Convey("Modifying two groups with XG=fal se is invalid", func() {
457 err := ds.RunInTransaction(func( c context.Context) error {
458 ds := wrapper.GetDS(c)
459 f := &Foo{ID: 1, Val: 20 0}
460 _, err := ds.Put(f)
461 So(err, ShouldBeNil)
462
463 f.ID = 2
464 _, err = ds.Put(f)
465 So(err.Error(), ShouldCo ntainSubstring, "cross-group")
466 return err
467 }, nil)
468 So(err.Error(), ShouldContainSub string, "cross-group")
469 })
470
471 Convey("Modifying >25 groups with XG=tru e is invald", func() {
472 err := ds.RunInTransaction(func( c context.Context) error {
473 ds := wrapper.GetDS(c)
474 for i := int64(1); i < 2 6; i++ {
475 f := &Foo{ID: i, Val: 200}
476 _, err := ds.Put (f)
477 So(err, ShouldBe Nil)
478 }
479 f := &Foo{ID: 27, Val: 2 00}
480 _, err := ds.Put(f)
481 So(err.Error(), ShouldCo ntainSubstring, "too many entity groups")
482 return err
483 }, &datastore.TransactionOptions {XG: true})
484 So(err.Error(), ShouldContainSub string, "too many entity groups")
485 })
486 })
487
488 Convey("Errors and panics", func() {
489 Convey("returning an error aborts", func () {
490 err := ds.RunInTransaction(func( c context.Context) error {
491 ds := wrapper.GetDS(c)
492 f := &Foo{ID: 1, Val: 20 0}
493 _, err := ds.Put(f)
494 So(err, ShouldBeNil)
495
496 return fmt.Errorf("thing y")
497 }, nil)
498 So(err.Error(), ShouldEqual, "th ingy")
499
500 f := &Foo{ID: 1}
501 So(ds.Get(f), ShouldBeNil)
502 So(f.Val, ShouldEqual, 10)
503 })
504
505 Convey("panicing aborts", func() {
506 So(func() {
507 ds.RunInTransaction(func (c context.Context) error {
508 ds := wrapper.Ge tDS(c)
509 f := &Foo{ID: 1, Val: 200}
510 _, err := ds.Put (f)
511 So(err, ShouldBe Nil)
512 panic("wheeeeee" )
513 }, nil)
514 }, ShouldPanic)
515
516 f := &Foo{ID: 1}
517 So(ds.Get(f), ShouldBeNil)
518 So(f.Val, ShouldEqual, 10)
519 })
520 })
521 })
522 })
523
524 })
525 }
526
527 const MaxUint = ^uint(0)
528 const MaxInt = int(MaxUint >> 1)
529 const IntIs32Bits = int64(MaxInt) < math.MaxInt64
530
531 func TestDatastoreQueryer(t *testing.T) {
532 Convey("Datastore Query suport", t, func() {
533 c := Use(context.Background())
534 ds := wrapper.GetDS(c)
535 So(ds, ShouldNotBeNil)
536
537 Convey("can create good queries", func() {
538 q := ds.NewQuery("Foo").KeysOnly().Limit(10).Offset(39)
539 q = q.Start(queryCursor("kosmik")).End(queryCursor("krab s"))
540 So(q, ShouldNotBeNil)
541 So(q.(*queryImpl).err, ShouldBeNil)
542 qi := q.(*queryImpl).checkCorrectness("", false)
543 So(qi.err, ShouldBeNil)
544 })
545
546 Convey("normalize ensures orders make sense", func() {
547 q := ds.NewQuery("Cool")
548 q = q.Filter("cat =", 19).Filter("bob =", 10).Order("bob ").Order("bob")
549
550 Convey("removes dups and equality orders", func() {
551 q = q.Order("wat")
552 qi := q.(*queryImpl).normalize().checkCorrectnes s("", false)
553 So(qi.err, ShouldBeNil)
554 So(qi.order, ShouldResemble, []queryOrder{{"wat" , qASC}})
555 })
556
557 Convey("keeps inequality orders", func() {
558 q = q.Order("wat")
559 q := q.Filter("bob >", 10).Filter("wat <", 29)
560 qi := q.(*queryImpl).normalize().checkCorrectnes s("", false)
561 So(qi.order, ShouldResemble, []queryOrder{{"bob" , qASC}, {"wat", qASC}})
562 So(qi.err.Error(), ShouldContainSubstring, "Only one inequality")
563 })
564
565 Convey("if we equality-filter on __key__, order is ditch ed", func() {
566 q = q.Order("wat")
567 q := q.Filter("__key__ =", ds.NewKey("Foo", "wat ", 0, nil))
568 qi := q.(*queryImpl).normalize().checkCorrectnes s("", false)
569 So(qi.order, ShouldResemble, []queryOrder(nil))
570 So(qi.err, ShouldBeNil)
571 })
572
573 Convey("if we order by key and something else, key domin ates", func() {
574 q := q.Order("__key__").Order("wat")
575 qi := q.(*queryImpl).normalize().checkCorrectnes s("", false)
576 So(qi.order, ShouldResemble, []queryOrder{{"__ke y__", qASC}})
577 So(qi.err, ShouldBeNil)
578 })
579 })
580
581 Convey("can create bad queries", func() {
582 q := ds.NewQuery("Foo")
583
584 Convey("bad filter ops", func() {
585 q := q.Filter("Bob !", "value")
586 So(q.(*queryImpl).err.Error(), ShouldContainSubs tring, "invalid operator \"!\"")
587 })
588 Convey("bad filter", func() {
589 q := q.Filter("Bob", "value")
590 So(q.(*queryImpl).err.Error(), ShouldContainSubs tring, "invalid filter")
591 })
592 Convey("bad order", func() {
593 q := q.Order("+Bob")
594 So(q.(*queryImpl).err.Error(), ShouldContainSubs tring, "invalid order")
595 })
596 Convey("empty", func() {
597 q := q.Order("")
598 So(q.(*queryImpl).err.Error(), ShouldContainSubs tring, "empty order")
599 })
600 Convey("OOB limit", func() {
601 // this is supremely stupid. The SDK uses 'int' which measn we have to
602 // use it too, but then THEY BOUNDS CHECK IT FOR 32 BITS... *sigh*
603 if !IntIs32Bits {
604 q := q.Limit(MaxInt)
605 So(q.(*queryImpl).err.Error(), ShouldCon tainSubstring, "query limit overflow")
606 }
607 })
608 Convey("underflow offset", func() {
609 q := q.Offset(-29)
610 So(q.(*queryImpl).err.Error(), ShouldContainSubs tring, "negative query offset")
611 })
612 Convey("OOB offset", func() {
613 if !IntIs32Bits {
614 q := q.Offset(MaxInt)
615 So(q.(*queryImpl).err.Error(), ShouldCon tainSubstring, "query offset overflow")
616 }
617 })
618 Convey("Bad cursors", func() {
619 q := q.Start(queryCursor("")).End(queryCursor("" ))
620 So(q.(*queryImpl).err.Error(), ShouldContainSubs tring, "invalid cursor")
621 })
622 Convey("Bad ancestors", func() {
623 q := q.Ancestor(ds.NewKey("Goop", "wat", 10, nil ))
624 So(q, ShouldNotBeNil)
625 qi := q.(*queryImpl).checkCorrectness("", false)
626 So(qi.err, ShouldEqual, datastore.ErrInvalidKey)
627 })
628 Convey("nil ancestors", func() {
629 qi := q.Ancestor(nil).(*queryImpl).checkCorrectn ess("", false)
630 So(qi.err.Error(), ShouldContainSubstring, "nil query ancestor")
631 })
632 Convey("Bad key filters", func() {
633 q := q.Filter("__key__ =", ds.NewKey("Goop", "wa t", 10, nil))
634 qi := q.(*queryImpl).checkCorrectness("", false)
635 So(qi.err, ShouldEqual, datastore.ErrInvalidKey)
636 })
637 Convey("non-ancestor queries in a transaction", func() {
638 qi := q.(*queryImpl).checkCorrectness("", true)
639 So(qi.err.Error(), ShouldContainSubstring, "Only ancestor queries")
640 })
641 Convey("absurd numbers of filters are prohibited", func( ) {
642 q := q.Ancestor(ds.NewKey("thing", "wat", 0, nil ))
643 for i := 0; i < 100; i++ {
644 q = q.Filter("something =", 10)
645 }
646 qi := q.(*queryImpl).checkCorrectness("", false)
647 So(qi.err.Error(), ShouldContainSubstring, "quer y is too large")
648 })
649 Convey("filters for __key__ that aren't keys", func() {
650 q := q.Filter("__key__ = ", 10)
651 qi := q.(*queryImpl).checkCorrectness("", false)
652 So(qi.err.Error(), ShouldContainSubstring, "must be a Key")
653 })
654 Convey("multiple inequalities", func() {
655 q := q.Filter("bob > ", 19).Filter("charlie < ", 20)
656 qi := q.(*queryImpl).checkCorrectness("", false)
657 So(qi.err.Error(), ShouldContainSubstring, "one inequality filter")
658 })
659 Convey("bad sort orders", func() {
660 q := q.Filter("bob > ", 19).Order("-charlie")
661 qi := q.(*queryImpl).checkCorrectness("", false)
662 So(qi.err.Error(), ShouldContainSubstring, "firs t sort property")
663 })
664 Convey("kindless with non-__key__ filters", func() {
665 q := ds.NewQuery("").Filter("face <", 25.3)
666 qi := q.(*queryImpl).checkCorrectness("", false)
667 So(qi.err.Error(), ShouldContainSubstring, "kind is required for non-__key__")
668 })
669 Convey("kindless with non-__key__ orders", func() {
670 q := ds.NewQuery("").Order("face")
671 qi := q.(*queryImpl).checkCorrectness("", false)
672 So(qi.err.Error(), ShouldContainSubstring, "kind is required for all orders")
673 })
674 Convey("kindless with decending-__key__ orders", func() {
675 q := ds.NewQuery("").Order("-__key__")
676 qi := q.(*queryImpl).checkCorrectness("", false)
677 So(qi.err.Error(), ShouldContainSubstring, "kind is required for all orders")
678 })
679 })
680
681 })
682 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698