OLD | NEW |
| (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 "math" | |
10 "testing" | |
11 | |
12 "github.com/luci/gae" | |
13 "github.com/luci/gae/helper" | |
14 . "github.com/smartystreets/goconvey/convey" | |
15 "golang.org/x/net/context" | |
16 ) | |
17 | |
18 func TestDatastoreKinder(t *testing.T) { | |
19 t.Parallel() | |
20 | |
21 Convey("Datastore keys", t, func() { | |
22 c := Use(context.Background()) | |
23 rds := gae.GetRDS(c) | |
24 So(rds, ShouldNotBeNil) | |
25 | |
26 Convey("implements DSNewKeyer", func() { | |
27 Convey("NewKey", func() { | |
28 key := rds.NewKey("nerd", "stringID", 0, nil) | |
29 So(key, ShouldNotBeNil) | |
30 So(key.Kind(), ShouldEqual, "nerd") | |
31 So(key.StringID(), ShouldEqual, "stringID") | |
32 So(key.IntID(), ShouldEqual, 0) | |
33 So(key.Parent(), ShouldBeNil) | |
34 So(key.AppID(), ShouldEqual, "dev~app") | |
35 So(key.Namespace(), ShouldEqual, "") | |
36 So(key.String(), ShouldEqual, "/nerd,stringID") | |
37 So(helper.DSKeyIncomplete(key), ShouldBeFalse) | |
38 So(helper.DSKeyValid(key, "", false), ShouldBeTr
ue) | |
39 }) | |
40 }) | |
41 | |
42 }) | |
43 } | |
44 | |
45 func testGetMeta(c context.Context, k gae.DSKey) int64 { | |
46 rds := gae.GetRDS(c) | |
47 k = rds.NewKey("__entity_group__", "", 1, helper.DSKeyRoot(k)) | |
48 pmap := gae.DSPropertyMap{} | |
49 rds.Get(k, pmap) | |
50 return pmap["__version__"][0].Value().(int64) | |
51 } | |
52 | |
53 var pls = helper.GetPLS | |
54 | |
55 func TestDatastoreSingleReadWriter(t *testing.T) { | |
56 t.Parallel() | |
57 | |
58 Convey("Datastore single reads and writes", t, func() { | |
59 c := Use(context.Background()) | |
60 rds := gae.GetRDS(c) | |
61 So(rds, ShouldNotBeNil) | |
62 | |
63 Convey("implements DSSingleReadWriter", func() { | |
64 type Foo struct { | |
65 Val int | |
66 } | |
67 | |
68 Convey("invalid keys break", func() { | |
69 k := rds.NewKey("Foo", "", 0, nil) | |
70 So(rds.Get(k, nil), ShouldEqual, gae.ErrDSInvali
dKey) | |
71 | |
72 _, err := rds.Put(rds.NewKey("Foo", "", 0, k), p
ls(&Foo{})) | |
73 So(err, ShouldEqual, gae.ErrDSInvalidKey) | |
74 }) | |
75 | |
76 Convey("getting objects that DNE is an error", func() { | |
77 k := rds.NewKey("Foo", "", 1, nil) | |
78 So(rds.Get(k, nil), ShouldEqual, gae.ErrDSNoSuch
Entity) | |
79 }) | |
80 | |
81 Convey("Can Put stuff", func() { | |
82 // with an incomplete key! | |
83 k := rds.NewKey("Foo", "", 0, nil) | |
84 f := &Foo{Val: 10} | |
85 k, err := rds.Put(k, pls(f)) | |
86 So(err, ShouldBeNil) | |
87 So(k.String(), ShouldEqual, "/Foo,1") | |
88 | |
89 Convey("and Get it back", func() { | |
90 newFoo := &Foo{} | |
91 err := rds.Get(k, pls(newFoo)) | |
92 So(err, ShouldBeNil) | |
93 So(newFoo, ShouldResemble, f) | |
94 | |
95 Convey("and we can Delete it", func() { | |
96 err := rds.Delete(k) | |
97 So(err, ShouldBeNil) | |
98 | |
99 err = rds.Get(k, pls(newFoo)) | |
100 So(err, ShouldEqual, gae.ErrDSNo
SuchEntity) | |
101 }) | |
102 }) | |
103 Convey("Deleteing with a bogus key is bad", func
() { | |
104 err := rds.Delete(rds.NewKey("Foo", "wat
", 100, nil)) | |
105 So(err, ShouldEqual, gae.ErrDSInvalidKey
) | |
106 }) | |
107 Convey("Deleteing a DNE entity is fine", func()
{ | |
108 err := rds.Delete(rds.NewKey("Foo", "wat
", 0, nil)) | |
109 So(err, ShouldBeNil) | |
110 }) | |
111 | |
112 Convey("with multiple puts", func() { | |
113 So(testGetMeta(c, k), ShouldEqual, 1) | |
114 | |
115 keys := []gae.DSKey{} | |
116 plss := []gae.DSPropertyLoadSaver{} | |
117 | |
118 pkey := k | |
119 for i := 0; i < 10; i++ { | |
120 keys = append(keys, rds.NewKey("
Foo", "", 0, pkey)) | |
121 plss = append(plss, pls(&Foo{Val
: 10})) | |
122 } | |
123 keys, err := rds.PutMulti(keys, plss) | |
124 So(err, ShouldBeNil) | |
125 So(testGetMeta(c, k), ShouldEqual, 11) | |
126 | |
127 Convey("ensure that group versions persi
st across deletes", func() { | |
128 So(rds.Delete(k), ShouldBeNil) | |
129 for i := int64(1); i < 11; i++ { | |
130 So(rds.Delete(rds.NewKey
("Foo", "", i, k)), ShouldBeNil) | |
131 } | |
132 // TODO(riannucci): replace with
a Count query instead of this cast | |
133 ents := rds.(*dsImpl).data.store
.GetCollection("ents:") | |
134 num, _ := ents.GetTotals() | |
135 // /__entity_root_ids__,Foo | |
136 // /Foo,1/__entity_group__,1 | |
137 // /Foo,1/__entity_group_ids__,1 | |
138 So(num, ShouldEqual, 3) | |
139 | |
140 So(curVersion(ents, groupMetaKey
(k)), ShouldEqual, 22) | |
141 | |
142 k, err := rds.Put(k, pls(f)) | |
143 So(err, ShouldBeNil) | |
144 So(testGetMeta(c, k), ShouldEqua
l, 23) | |
145 }) | |
146 | |
147 Convey("can GetMulti", func() { | |
148 plss := make([]gae.DSPropertyLoa
dSaver, len(keys)) | |
149 for i := range plss { | |
150 plss[i] = gae.DSProperty
Map{} | |
151 } | |
152 err := rds.GetMulti(keys, plss) | |
153 So(err, ShouldBeNil) | |
154 for _, pls := range plss { | |
155 So(pls.(gae.DSPropertyMa
p), ShouldResemble, gae.DSPropertyMap{ | |
156 "Val": {gae.MkDS
Property(10)}, | |
157 }) | |
158 } | |
159 }) | |
160 }) | |
161 }) | |
162 }) | |
163 | |
164 Convey("implements DSTransactioner", func() { | |
165 type Foo struct { | |
166 Val int | |
167 } | |
168 Convey("Put", func() { | |
169 k := rds.NewKey("Foo", "", 0, nil) | |
170 f := &Foo{Val: 10} | |
171 k, err := rds.Put(k, pls(f)) | |
172 So(err, ShouldBeNil) | |
173 So(k.String(), ShouldEqual, "/Foo,1") | |
174 | |
175 Convey("can Put new entity groups", func() { | |
176 err := rds.RunInTransaction(func(c conte
xt.Context) error { | |
177 rds := gae.GetRDS(c) | |
178 So(rds, ShouldNotBeNil) | |
179 | |
180 f1 := &Foo{Val: 100} | |
181 k, err := rds.Put(rds.NewKey("Fo
o", "", 0, nil), pls(f1)) | |
182 So(err, ShouldBeNil) | |
183 So(k.String(), ShouldEqual, "/Fo
o,2") | |
184 | |
185 f2 := &Foo{Val: 200} | |
186 k, err = rds.Put(rds.NewKey("Foo
", "", 0, nil), pls(f2)) | |
187 So(err, ShouldBeNil) | |
188 So(k.String(), ShouldEqual, "/Fo
o,3") | |
189 | |
190 return nil | |
191 }, &gae.DSTransactionOptions{XG: true}) | |
192 So(err, ShouldBeNil) | |
193 | |
194 f := &Foo{} | |
195 So(rds.Get(rds.NewKey("Foo", "", 2, nil)
, pls(f)), ShouldBeNil) | |
196 So(f.Val, ShouldEqual, 100) | |
197 | |
198 f = &Foo{} | |
199 So(rds.Get(rds.NewKey("Foo", "", 3, nil)
, pls(f)), ShouldBeNil) | |
200 So(f.Val, ShouldEqual, 200) | |
201 }) | |
202 | |
203 Convey("can Put new entities in a current group"
, func() { | |
204 err := rds.RunInTransaction(func(c conte
xt.Context) error { | |
205 rds := gae.GetRDS(c) | |
206 So(rds, ShouldNotBeNil) | |
207 | |
208 par := k | |
209 | |
210 f1 := &Foo{Val: 100} | |
211 k, err := rds.Put(rds.NewKey("Fo
o", "", 0, par), pls(f1)) | |
212 So(err, ShouldBeNil) | |
213 So(k.String(), ShouldEqual, "/Fo
o,1/Foo,1") | |
214 | |
215 f2 := &Foo{Val: 200} | |
216 k, err = rds.Put(rds.NewKey("Foo
", "", 0, par), pls(f2)) | |
217 So(err, ShouldBeNil) | |
218 So(k.String(), ShouldEqual, "/Fo
o,1/Foo,2") | |
219 | |
220 return nil | |
221 }, nil) | |
222 So(err, ShouldBeNil) | |
223 | |
224 f1 := &Foo{} | |
225 So(rds.Get(rds.NewKey("Foo", "", 1, k),
pls(f1)), ShouldBeNil) | |
226 So(f1.Val, ShouldEqual, 100) | |
227 | |
228 f2 := &Foo{} | |
229 So(rds.Get(rds.NewKey("Foo", "", 2, k),
pls(f2)), ShouldBeNil) | |
230 So(f2.Val, ShouldEqual, 200) | |
231 }) | |
232 | |
233 Convey("Deletes work too", func() { | |
234 err := rds.RunInTransaction(func(c conte
xt.Context) error { | |
235 rds := gae.GetRDS(c) | |
236 So(rds, ShouldNotBeNil) | |
237 So(rds.Delete(k), ShouldBeNil) | |
238 return nil | |
239 }, nil) | |
240 So(err, ShouldBeNil) | |
241 So(rds.Get(k, nil), ShouldEqual, gae.Err
DSNoSuchEntity) | |
242 }) | |
243 | |
244 Convey("A Get counts against your group count",
func() { | |
245 err := rds.RunInTransaction(func(c conte
xt.Context) error { | |
246 rds := gae.GetRDS(c) | |
247 So(rds.Get(rds.NewKey("Foo", "",
20, nil), nil), ShouldEqual, gae.ErrDSNoSuchEntity) | |
248 So(rds.Get(k, nil).Error(), Shou
ldContainSubstring, "cross-group") | |
249 return nil | |
250 }, nil) | |
251 So(err, ShouldBeNil) | |
252 }) | |
253 | |
254 Convey("Get takes a snapshot", func() { | |
255 err := rds.RunInTransaction(func(c conte
xt.Context) error { | |
256 txnDS := gae.GetRDS(c) | |
257 So(txnDS, ShouldNotBeNil) | |
258 | |
259 So(txnDS.Get(k, pls(f)), ShouldB
eNil) | |
260 So(f.Val, ShouldEqual, 10) | |
261 | |
262 // Don't ever do this in a real
program unless you want to guarantee | |
263 // a failed transaction :) | |
264 f.Val = 11 | |
265 _, err := rds.Put(k, pls(f)) | |
266 So(err, ShouldBeNil) | |
267 | |
268 So(txnDS.Get(k, pls(f)), ShouldB
eNil) | |
269 So(f.Val, ShouldEqual, 10) | |
270 | |
271 return nil | |
272 }, nil) | |
273 So(err, ShouldBeNil) | |
274 | |
275 f := &Foo{} | |
276 So(rds.Get(k, pls(f)), ShouldBeNil) | |
277 So(f.Val, ShouldEqual, 11) | |
278 }) | |
279 | |
280 Convey("and snapshots are consistent even after
Puts", func() { | |
281 err := rds.RunInTransaction(func(c conte
xt.Context) error { | |
282 txnDS := gae.GetRDS(c) | |
283 So(txnDS, ShouldNotBeNil) | |
284 | |
285 f := &Foo{} | |
286 So(txnDS.Get(k, pls(f)), ShouldB
eNil) | |
287 So(f.Val, ShouldEqual, 10) | |
288 | |
289 // Don't ever do this in a real
program unless you want to guarantee | |
290 // a failed transaction :) | |
291 f.Val = 11 | |
292 | |
293 _, err := rds.Put(k, pls(f)) | |
294 So(err, ShouldBeNil) | |
295 | |
296 So(txnDS.Get(k, pls(f)), ShouldB
eNil) | |
297 So(f.Val, ShouldEqual, 10) | |
298 | |
299 f.Val = 20 | |
300 _, err = txnDS.Put(k, pls(f)) | |
301 So(err, ShouldBeNil) | |
302 | |
303 So(txnDS.Get(k, pls(f)), ShouldB
eNil) | |
304 So(f.Val, ShouldEqual, 10) // st
ill gets 10 | |
305 | |
306 return nil | |
307 }, nil) | |
308 So(err.Error(), ShouldContainSubstring,
"concurrent") | |
309 | |
310 f := &Foo{} | |
311 So(rds.Get(k, pls(f)), ShouldBeNil) | |
312 So(f.Val, ShouldEqual, 11) | |
313 }) | |
314 | |
315 Convey("Reusing a transaction context is bad new
s", func() { | |
316 k := rds.NewKey("Foo", "", 1, nil) | |
317 txnDS := gae.RawDatastore(nil) | |
318 err := rds.RunInTransaction(func(c conte
xt.Context) error { | |
319 txnDS = gae.GetRDS(c) | |
320 So(txnDS.Get(k, gae.DSPropertyMa
p{}), ShouldBeNil) | |
321 return nil | |
322 }, nil) | |
323 So(err, ShouldBeNil) | |
324 So(txnDS.Get(k, gae.DSPropertyMap{}).Err
or(), ShouldContainSubstring, "expired") | |
325 }) | |
326 | |
327 Convey("Nested transactions are rejected", func(
) { | |
328 err := rds.RunInTransaction(func(c conte
xt.Context) error { | |
329 err := gae.GetRDS(c).RunInTransa
ction(func(c context.Context) error { | |
330 panic("noooo") | |
331 }, nil) | |
332 So(err.Error(), ShouldContainSub
string, "nested transactions") | |
333 return nil | |
334 }, nil) | |
335 So(err, ShouldBeNil) | |
336 }) | |
337 | |
338 Convey("Concurrent transactions only accept one
set of changes", func() { | |
339 // Note: I think this implementation is
actually /slightly/ wrong. | |
340 // Accorting to my read of the docs for
appengine, when you open a | |
341 // transaction it actually (essentially)
holds a reference to the | |
342 // entire datastore. Our implementation
takes a snapshot of the | |
343 // entity group as soon as something obs
erves/affects it. | |
344 // | |
345 // That said... I'm not sure if there's
really a semantic difference. | |
346 err := rds.RunInTransaction(func(c conte
xt.Context) error { | |
347 txnDS := gae.GetRDS(c) | |
348 f := &Foo{Val: 21} | |
349 _, err = txnDS.Put(k, pls(f)) | |
350 So(err, ShouldBeNil) | |
351 | |
352 err := rds.RunInTransaction(func
(c context.Context) error { | |
353 txnDS := gae.GetRDS(c) | |
354 f := &Foo{Val: 27} | |
355 _, err := txnDS.Put(k, p
ls(f)) | |
356 So(err, ShouldBeNil) | |
357 return nil | |
358 }, nil) | |
359 So(err, ShouldBeNil) | |
360 | |
361 return nil | |
362 }, nil) | |
363 So(err.Error(), ShouldContainSubstring,
"concurrent") | |
364 | |
365 f := &Foo{} | |
366 So(rds.Get(k, pls(f)), ShouldBeNil) | |
367 So(f.Val, ShouldEqual, 27) | |
368 }) | |
369 | |
370 Convey("XG", func() { | |
371 Convey("Modifying two groups with XG=fal
se is invalid", func() { | |
372 err := rds.RunInTransaction(func
(c context.Context) error { | |
373 rds := gae.GetRDS(c) | |
374 f := &Foo{Val: 200} | |
375 _, err := rds.Put(k, pls
(f)) | |
376 So(err, ShouldBeNil) | |
377 | |
378 _, err = rds.Put(rds.New
Key("Foo", "", 2, nil), pls(f)) | |
379 So(err.Error(), ShouldCo
ntainSubstring, "cross-group") | |
380 return err | |
381 }, nil) | |
382 So(err.Error(), ShouldContainSub
string, "cross-group") | |
383 }) | |
384 | |
385 Convey("Modifying >25 groups with XG=tru
e is invald", func() { | |
386 err := rds.RunInTransaction(func
(c context.Context) error { | |
387 rds := gae.GetRDS(c) | |
388 for i := int64(1); i < 2
6; i++ { | |
389 k := rds.NewKey(
"Foo", "", i, nil) | |
390 f := &Foo{Val: 2
00} | |
391 _, err := rds.Pu
t(k, pls(f)) | |
392 So(err, ShouldBe
Nil) | |
393 } | |
394 f := &Foo{Val: 200} | |
395 _, err := rds.Put(rds.Ne
wKey("Foo", "", 27, nil), pls(f)) | |
396 So(err.Error(), ShouldCo
ntainSubstring, "too many entity groups") | |
397 return err | |
398 }, &gae.DSTransactionOptions{XG:
true}) | |
399 So(err.Error(), ShouldContainSub
string, "too many entity groups") | |
400 }) | |
401 }) | |
402 | |
403 Convey("Errors and panics", func() { | |
404 Convey("returning an error aborts", func
() { | |
405 err := rds.RunInTransaction(func
(c context.Context) error { | |
406 rds := gae.GetRDS(c) | |
407 f := &Foo{Val: 200} | |
408 _, err := rds.Put(k, pls
(f)) | |
409 So(err, ShouldBeNil) | |
410 | |
411 return fmt.Errorf("thing
y") | |
412 }, nil) | |
413 So(err.Error(), ShouldEqual, "th
ingy") | |
414 | |
415 f := &Foo{} | |
416 So(rds.Get(k, pls(f)), ShouldBeN
il) | |
417 So(f.Val, ShouldEqual, 10) | |
418 }) | |
419 | |
420 Convey("panicing aborts", func() { | |
421 So(func() { | |
422 rds.RunInTransaction(fun
c(c context.Context) error { | |
423 rds := gae.GetRD
S(c) | |
424 f := &Foo{Val: 2
00} | |
425 _, err := rds.Pu
t(k, pls(f)) | |
426 So(err, ShouldBe
Nil) | |
427 panic("wheeeeee"
) | |
428 }, nil) | |
429 }, ShouldPanic) | |
430 | |
431 f := &Foo{} | |
432 So(rds.Get(k, pls(f)), ShouldBeN
il) | |
433 So(f.Val, ShouldEqual, 10) | |
434 }) | |
435 }) | |
436 }) | |
437 }) | |
438 | |
439 }) | |
440 } | |
441 | |
442 const MaxUint = ^uint(0) | |
443 const MaxInt = int(MaxUint >> 1) | |
444 const IntIs32Bits = int64(MaxInt) < math.MaxInt64 | |
445 | |
446 func TestDatastoreQueryer(t *testing.T) { | |
447 Convey("Datastore Query suport", t, func() { | |
448 c := Use(context.Background()) | |
449 rds := gae.GetRDS(c) | |
450 So(rds, ShouldNotBeNil) | |
451 | |
452 Convey("can create good queries", func() { | |
453 q := rds.NewQuery("Foo").KeysOnly().Limit(10).Offset(39) | |
454 q = q.Start(queryCursor("kosmik")).End(queryCursor("krab
s")) | |
455 So(q, ShouldNotBeNil) | |
456 So(q.(*queryImpl).err, ShouldBeNil) | |
457 qi := q.(*queryImpl).checkCorrectness("", false) | |
458 So(qi.err, ShouldBeNil) | |
459 }) | |
460 | |
461 Convey("normalize ensures orders make sense", func() { | |
462 q := rds.NewQuery("Cool") | |
463 q = q.Filter("cat =", 19).Filter("bob =", 10).Order("bob
").Order("bob") | |
464 | |
465 Convey("removes dups and equality orders", func() { | |
466 q = q.Order("wat") | |
467 qi := q.(*queryImpl).normalize().checkCorrectnes
s("", false) | |
468 So(qi.err, ShouldBeNil) | |
469 So(qi.order, ShouldResemble, []queryOrder{{"wat"
, qASC}}) | |
470 }) | |
471 | |
472 Convey("keeps inequality orders", func() { | |
473 q = q.Order("wat") | |
474 q := q.Filter("bob >", 10).Filter("wat <", 29) | |
475 qi := q.(*queryImpl).normalize().checkCorrectnes
s("", false) | |
476 So(qi.order, ShouldResemble, []queryOrder{{"bob"
, qASC}, {"wat", qASC}}) | |
477 So(qi.err.Error(), ShouldContainSubstring, "Only
one inequality") | |
478 }) | |
479 | |
480 Convey("if we equality-filter on __key__, order is ditch
ed", func() { | |
481 q = q.Order("wat") | |
482 q := q.Filter("__key__ =", rds.NewKey("Foo", "wa
t", 0, nil)) | |
483 qi := q.(*queryImpl).normalize().checkCorrectnes
s("", false) | |
484 So(qi.order, ShouldResemble, []queryOrder(nil)) | |
485 So(qi.err, ShouldBeNil) | |
486 }) | |
487 | |
488 Convey("if we order by key and something else, key domin
ates", func() { | |
489 q := q.Order("__key__").Order("wat") | |
490 qi := q.(*queryImpl).normalize().checkCorrectnes
s("", false) | |
491 So(qi.order, ShouldResemble, []queryOrder{{"__ke
y__", qASC}}) | |
492 So(qi.err, ShouldBeNil) | |
493 }) | |
494 }) | |
495 | |
496 Convey("can create bad queries", func() { | |
497 q := rds.NewQuery("Foo") | |
498 | |
499 Convey("bad filter ops", func() { | |
500 q := q.Filter("Bob !", "value") | |
501 So(q.(*queryImpl).err.Error(), ShouldContainSubs
tring, "invalid operator \"!\"") | |
502 }) | |
503 Convey("bad filter", func() { | |
504 q := q.Filter("Bob", "value") | |
505 So(q.(*queryImpl).err.Error(), ShouldContainSubs
tring, "invalid filter") | |
506 }) | |
507 Convey("bad order", func() { | |
508 q := q.Order("+Bob") | |
509 So(q.(*queryImpl).err.Error(), ShouldContainSubs
tring, "invalid order") | |
510 }) | |
511 Convey("empty", func() { | |
512 q := q.Order("") | |
513 So(q.(*queryImpl).err.Error(), ShouldContainSubs
tring, "empty order") | |
514 }) | |
515 Convey("OOB limit", func() { | |
516 // this is supremely stupid. The SDK uses 'int'
which measn we have to | |
517 // use it too, but then THEY BOUNDS CHECK IT FOR
32 BITS... *sigh* | |
518 if !IntIs32Bits { | |
519 q := q.Limit(MaxInt) | |
520 So(q.(*queryImpl).err.Error(), ShouldCon
tainSubstring, "query limit overflow") | |
521 } | |
522 }) | |
523 Convey("underflow offset", func() { | |
524 q := q.Offset(-29) | |
525 So(q.(*queryImpl).err.Error(), ShouldContainSubs
tring, "negative query offset") | |
526 }) | |
527 Convey("OOB offset", func() { | |
528 if !IntIs32Bits { | |
529 q := q.Offset(MaxInt) | |
530 So(q.(*queryImpl).err.Error(), ShouldCon
tainSubstring, "query offset overflow") | |
531 } | |
532 }) | |
533 Convey("Bad cursors", func() { | |
534 q := q.Start(queryCursor("")).End(queryCursor(""
)) | |
535 So(q.(*queryImpl).err.Error(), ShouldContainSubs
tring, "invalid cursor") | |
536 }) | |
537 Convey("Bad ancestors", func() { | |
538 q := q.Ancestor(rds.NewKey("Goop", "wat", 10, ni
l)) | |
539 So(q, ShouldNotBeNil) | |
540 qi := q.(*queryImpl).checkCorrectness("", false) | |
541 So(qi.err, ShouldEqual, gae.ErrDSInvalidKey) | |
542 }) | |
543 Convey("nil ancestors", func() { | |
544 qi := q.Ancestor(nil).(*queryImpl).checkCorrectn
ess("", false) | |
545 So(qi.err.Error(), ShouldContainSubstring, "nil
query ancestor") | |
546 }) | |
547 Convey("Bad key filters", func() { | |
548 q := q.Filter("__key__ =", rds.NewKey("Goop", "w
at", 10, nil)) | |
549 qi := q.(*queryImpl).checkCorrectness("", false) | |
550 So(qi.err, ShouldEqual, gae.ErrDSInvalidKey) | |
551 }) | |
552 Convey("non-ancestor queries in a transaction", func() { | |
553 qi := q.(*queryImpl).checkCorrectness("", true) | |
554 So(qi.err.Error(), ShouldContainSubstring, "Only
ancestor queries") | |
555 }) | |
556 Convey("absurd numbers of filters are prohibited", func(
) { | |
557 q := q.Ancestor(rds.NewKey("thing", "wat", 0, ni
l)) | |
558 for i := 0; i < 100; i++ { | |
559 q = q.Filter("something =", 10) | |
560 } | |
561 qi := q.(*queryImpl).checkCorrectness("", false) | |
562 So(qi.err.Error(), ShouldContainSubstring, "quer
y is too large") | |
563 }) | |
564 Convey("filters for __key__ that aren't keys", func() { | |
565 q := q.Filter("__key__ = ", 10) | |
566 qi := q.(*queryImpl).checkCorrectness("", false) | |
567 So(qi.err.Error(), ShouldContainSubstring, "must
be a Key") | |
568 }) | |
569 Convey("multiple inequalities", func() { | |
570 q := q.Filter("bob > ", 19).Filter("charlie < ",
20) | |
571 qi := q.(*queryImpl).checkCorrectness("", false) | |
572 So(qi.err.Error(), ShouldContainSubstring, "one
inequality filter") | |
573 }) | |
574 Convey("bad sort orders", func() { | |
575 q := q.Filter("bob > ", 19).Order("-charlie") | |
576 qi := q.(*queryImpl).checkCorrectness("", false) | |
577 So(qi.err.Error(), ShouldContainSubstring, "firs
t sort property") | |
578 }) | |
579 Convey("kindless with non-__key__ filters", func() { | |
580 q := rds.NewQuery("").Filter("face <", 25.3) | |
581 qi := q.(*queryImpl).checkCorrectness("", false) | |
582 So(qi.err.Error(), ShouldContainSubstring, "kind
is required for non-__key__") | |
583 }) | |
584 Convey("kindless with non-__key__ orders", func() { | |
585 q := rds.NewQuery("").Order("face") | |
586 qi := q.(*queryImpl).checkCorrectness("", false) | |
587 So(qi.err.Error(), ShouldContainSubstring, "kind
is required for all orders") | |
588 }) | |
589 Convey("kindless with decending-__key__ orders", func()
{ | |
590 q := rds.NewQuery("").Order("-__key__") | |
591 qi := q.(*queryImpl).checkCorrectness("", false) | |
592 So(qi.err.Error(), ShouldContainSubstring, "kind
is required for all orders") | |
593 }) | |
594 }) | |
595 | |
596 }) | |
597 } | |
OLD | NEW |