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

Side by Side Diff: memory/raw_datastore_test.go

Issue 1243323002: Refactor a bit. (Closed) Base URL: https://github.com/luci/gae.git@master
Patch Set: fix golint 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
« no previous file with comments | « memory/raw_datastore_query.go ('k') | memory/taskqueue.go » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 "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 }
OLDNEW
« no previous file with comments | « memory/raw_datastore_query.go ('k') | memory/taskqueue.go » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698