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

Side by Side Diff: filter/txnBuf/txnbuf_test.go

Issue 1309803004: Add transaction buffer filter. (Closed) Base URL: https://github.com/luci/gae.git@add_query_support
Patch Set: one more test Created 5 years, 2 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 txnBuf
6
7 import (
8 "bytes"
9 "fmt"
10 "math/rand"
11 "testing"
12
13 "github.com/luci/gae/filter/count"
14 "github.com/luci/gae/impl/memory"
15 "github.com/luci/gae/service/datastore"
16 "github.com/luci/luci-go/common/cmpbin"
17 "github.com/luci/luci-go/common/errors"
18 . "github.com/luci/luci-go/common/testing/assertions"
19 . "github.com/smartystreets/goconvey/convey"
20 "golang.org/x/net/context"
21 )
22
23 type Foo struct {
24 ID int64 `gae:"$id"`
25 Parent *datastore.Key `gae:"$parent"`
26
27 Value []int64
28 ValueNI []byte `gae:",noindex"`
29 Sort []string
30 }
31
32 func toIntSlice(stuff []interface{}) []int64 {
33 vals, ok := stuff[0].([]int64)
34 if !ok {
35 vals = make([]int64, len(stuff))
36 for i := range vals {
37 vals[i] = int64(stuff[i].(int))
38 }
39 }
40 return vals
41 }
42
43 func toInt64(thing interface{}) int64 {
44 switch x := thing.(type) {
45 case int:
46 return int64(x)
47 case int64:
48 return x
49 default:
50 panic(fmt.Errorf("wat r it? %v", x))
51 }
52 }
53
54 func fooShouldHave(ds datastore.Interface) func(interface{}, ...interface{}) str ing {
55 return func(id interface{}, values ...interface{}) string {
56 f := &Foo{ID: toInt64(id)}
57 err := ds.Get(f)
58 if len(values) == 0 {
59 return ShouldEqual(err, datastore.ErrNoSuchEntity)
60 }
61
62 ret := ShouldBeNil(err)
63 if ret == "" {
64 if data, ok := values[0].([]byte); ok {
65 ret = ShouldResemble(f.ValueNI, data)
66 } else {
67 ret = ShouldResemble(f.Value, toIntSlice(values) )
68 }
69 }
70 return ret
71 }
72 }
73
74 func fooSetTo(ds datastore.Interface) func(interface{}, ...interface{}) string {
75 return func(id interface{}, values ...interface{}) string {
76 f := &Foo{ID: toInt64(id)}
77 if len(values) == 0 {
78 return ShouldBeNil(ds.Delete(ds.KeyForObj(f)))
79 }
80 if data, ok := values[0].([]byte); ok {
81 f.ValueNI = data
82 } else {
83 f.Value = toIntSlice(values)
84 }
85 return ShouldBeNil(ds.Put(f))
86 }
87 }
88
89 func TestTransactionBuffers(t *testing.T) {
90 t.Parallel()
91
92 cb := func(i int64) string {
93 buf := &bytes.Buffer{}
94 cmpbin.WriteInt(buf, i)
95 return buf.String()
96 }
97
98 rs := rand.NewSource(0)
99 dataMultiRoot := make([]*Foo, 20)
100 dataSingleRoot := make([]*Foo, 20)
101 root := datastore.MakeKey("dev~app", "", "Parent", 1)
102 nums := make([]string, 20)
103 for i := range dataMultiRoot {
104 id := int64(i + 1)
105 nums[i] = cb(id)
106
107 val := make([]int64, rs.Int63()%20)
108 for j := range val {
109 r := rs.Int63()
110 val[j] = r
111 }
112
113 dataMultiRoot[i] = &Foo{ID: id, Value: val}
114 dataSingleRoot[i] = &Foo{ID: id, Parent: root, Value: val}
115 }
116
117 hugeField := make([]byte, DefaultSizeBudget/8)
118 for i := range hugeField {
119 hugeField[i] = byte(i)
120 }
121
122 hugeData := make([]*Foo, 11)
123 for i := range hugeData {
124 hugeData[i] = &Foo{ID: int64(i + 1), ValueNI: hugeField}
125 }
126
127 mkds := func(data []*Foo) (under, over *count.DSCounter, ds datastore.In terface) {
128 c := memory.Use(context.Background())
129 ds = datastore.Get(c)
130 _, err := ds.AllocateIDs(ds.KeyForObj(data[0]), 100)
131 if err != nil {
132 panic(err)
133 }
134 if err := ds.PutMulti(data); err != nil {
135 panic(err)
136 }
137
138 c, under = count.FilterRDS(c)
139 c = FilterRDS(c)
140 c, over = count.FilterRDS(c)
141 ds = datastore.Get(c)
142 return
143 }
144
145 Convey("Get/Put/Delete", t, func() {
146 under, over, ds := mkds(dataMultiRoot)
147
148 So(under.PutMulti.Total(), ShouldEqual, 0)
149 So(over.PutMulti.Total(), ShouldEqual, 0)
150
151 Convey("Good", func() {
152 Convey("read-only", func() {
153 So(ds.RunInTransaction(func(c context.Context) e rror {
154 ds := datastore.Get(c)
155
156 So(4, fooShouldHave(ds), dataMultiRoot[3 ].Value)
157 return nil
158 }, nil), ShouldBeNil)
159 })
160
161 Convey("single-level read/write", func() {
162 So(ds.RunInTransaction(func(c context.Context) e rror {
163 ds := datastore.Get(c)
164
165 So(4, fooShouldHave(ds), dataMultiRoot[3 ].Value)
166
167 So(4, fooSetTo(ds), 1, 2, 3, 4)
168
169 So(3, fooSetTo(ds), 1, 2, 3, 4)
170
171 // look! it remembers :)
172 So(4, fooShouldHave(ds), 1, 2, 3, 4)
173 return nil
174 }, &datastore.TransactionOptions{XG: true}), Sho uldBeNil)
175
176 So(under.PutMulti.Total(), ShouldEqual, 1)
177
178 So(3, fooShouldHave(ds), 1, 2, 3, 4)
179 So(4, fooShouldHave(ds), 1, 2, 3, 4)
180 })
181
182 Convey("multi-level read/write", func() {
183 So(ds.RunInTransaction(func(c context.Context) e rror {
184 ds := datastore.Get(c)
185
186 So(3, fooShouldHave(ds), dataMultiRoot[2 ].Value)
187
188 So(3, fooSetTo(ds), 1, 2, 3, 4)
189 So(7, fooSetTo(ds))
190
191 // inner, failing, transaction
192 So(ds.RunInTransaction(func(c context.Co ntext) error {
193 ds := datastore.Get(c)
194
195 // we can see stuff written in t he outer txn
196 So(7, fooShouldHave(ds))
197 So(3, fooShouldHave(ds), 1, 2, 3 , 4)
198
199 So(3, fooSetTo(ds), 10, 20, 30, 40)
200
201 // disaster strikes!
202 return errors.New("whaaaa")
203 }, nil), ShouldErrLike, "whaaaa")
204
205 So(3, fooShouldHave(ds), 1, 2, 3, 4)
206
207 // inner, successful, transaction
208 So(ds.RunInTransaction(func(c context.Co ntext) error {
209 ds := datastore.Get(c)
210 So(3, fooShouldHave(ds), 1, 2, 3 , 4)
211 So(3, fooSetTo(ds), 10, 20, 30, 40)
212 return nil
213 }, nil), ShouldBeNil)
214
215 // now we see it
216 So(3, fooShouldHave(ds), 10, 20, 30, 40)
217 return nil
218 }, &datastore.TransactionOptions{XG: true}), Sho uldBeNil)
219
220 So(under.PutMulti.Total(), ShouldEqual, 1)
221 So(under.DeleteMulti.Total(), ShouldEqual, 1)
222
223 // 'over' Put operations are amplified because t he inner transaction
224 // commits go through the 'over' filter on the o uter transaction. So it's
225 // # Puts + # inner txns.
226 So(over.PutMulti.Total(), ShouldEqual, 5)
227
228 So(7, fooShouldHave(ds))
229 So(3, fooShouldHave(ds), 10, 20, 30, 40)
230 })
231
232 Convey("can allocate IDs from an inner transaction", fun c() {
233 nums := []int64{4, 8, 15, 16, 23, 42}
234 k := (*datastore.Key)(nil)
235 So(ds.RunInTransaction(func(c context.Context) e rror {
236 ds := datastore.Get(c)
237
238 So(ds.RunInTransaction(func(c context.Co ntext) error {
239 ds := datastore.Get(c)
240 f := &Foo{Value: nums}
241 So(ds.Put(f), ShouldBeNil)
242 k = ds.KeyForObj(f)
243 return nil
244 }, nil), ShouldBeNil)
245
246 So(k.IntID(), fooShouldHave(ds), nums)
247
248 return nil
249 }, nil), ShouldBeNil)
250
251 So(k.IntID(), fooShouldHave(ds), nums)
252 })
253
254 Convey("inner txn too big allows outer txn", func() {
255 So(ds.RunInTransaction(func(c context.Context) e rror {
256 ds := datastore.Get(c)
257
258 So(18, fooSetTo(ds), hugeField)
259
260 So(ds.RunInTransaction(func(c context.Co ntext) error {
261 ds := datastore.Get(c)
262 So(ds.PutMulti(hugeData), Should BeNil)
263 return nil
264 }, nil), ShouldErrLike, ErrTransactionTo oLarge)
265
266 return nil
267 }, &datastore.TransactionOptions{XG: true}), Sho uldBeNil)
268
269 So(18, fooShouldHave(ds), hugeField)
270 })
271
272 Convey("outer txn too big prevents inner txn", func() {
273 So(ds.RunInTransaction(func(c context.Context) e rror {
274 ds := datastore.Get(c)
275
276 So(ds.PutMulti(hugeData), ShouldBeNil)
277
278 So(ds.RunInTransaction(func(c context.Co ntext) error {
279 panic("never!")
280 }, nil), ShouldErrLike, ErrTransactionTo oLarge)
281
282 return nil
283 }, &datastore.TransactionOptions{XG: true}), Sho uldBeNil)
284
285 So(1, fooShouldHave(ds), hugeField)
286 })
287
288 })
289
290 Convey("Bad", func() {
291
292 Convey("too many roots", func() {
293 So(ds.RunInTransaction(func(c context.Context) e rror {
294 ds := datastore.Get(c)
295
296 f := &Foo{ID: 7}
297 So(ds.Get(f), ShouldBeNil)
298 So(f, ShouldResemble, dataMultiRoot[6])
299
300 So(ds.RunInTransaction(func(c context.Co ntext) error {
301 return datastore.Get(c).Get(&Foo {ID: 6})
302 }, nil), ShouldErrLike, "too many entity groups")
303
304 f.Value = []int64{9}
305 So(ds.Put(f), ShouldBeNil)
306
307 return nil
308 }, nil), ShouldBeNil)
309
310 f := &Foo{ID: 7}
311 So(ds.Get(f), ShouldBeNil)
312 So(f.Value, ShouldResemble, []int64{9})
313 })
314
315 Convey("buffered errors never reach the datastore", func () {
316 So(ds.RunInTransaction(func(c context.Context) e rror {
317 ds := datastore.Get(c)
318
319 So(ds.Put(&Foo{ID: 1, Value: []int64{1, 2, 3, 4}}), ShouldBeNil)
320 return errors.New("boop")
321 }, nil), ShouldErrLike, "boop")
322 So(under.PutMulti.Total(), ShouldEqual, 0)
323 So(over.PutMulti.Successes(), ShouldEqual, 1)
324 })
325
326 })
327
328 })
329
330 Convey("Queries", t, func() {
331 Convey("Good", func() {
332 q := datastore.NewQuery("Foo").Ancestor(root)
333
334 Convey("normal", func() {
335 _, _, ds := mkds(dataSingleRoot)
336 ds.Testable().AddIndexes(&datastore.IndexDefinit ion{
337 Kind: "Foo",
338 Ancestor: true,
339 SortBy: []datastore.IndexColumn{
340 {Property: "Value"},
341 },
342 })
343
344 So(ds.RunInTransaction(func(c context.Context) e rror {
345 ds := datastore.Get(c)
346
347 q = q.Lt("Value", 400000000000000000)
348
349 vals := []*Foo{}
350 So(ds.GetAll(q, &vals), ShouldBeNil)
351 So(len(vals), ShouldEqual, 8)
352
353 f := &Foo{ID: 1, Parent: root}
354 So(ds.Get(f), ShouldBeNil)
355 f.Value = append(f.Value, 100)
356 So(ds.Put(f), ShouldBeNil)
357
358 // Wowee, zowee, merged queries!
359 vals2 := []*Foo{}
360 So(ds.GetAll(q, &vals2), ShouldBeNil)
361 So(len(vals2), ShouldEqual, 9)
362 So(vals2[0], ShouldResemble, f)
363
364 vals2 = []*Foo{}
365 So(ds.GetAll(q.Limit(2).Offset(1), &vals 2), ShouldBeNil)
366 So(len(vals2), ShouldEqual, 2)
367 So(vals2, ShouldResemble, vals[:2])
368
369 return nil
370 }, nil), ShouldBeNil)
371 })
372
373 Convey("keysOnly", func() {
374 _, _, ds := mkds([]*Foo{
375 {ID: 2, Parent: root, Value: []int64{1, 2, 3, 4, 5, 6, 7}},
376 {ID: 3, Parent: root, Value: []int64{3, 4, 5, 6, 7, 8, 9}},
377 {ID: 4, Parent: root, Value: []int64{3, 5, 7, 9, 11, 100, 1}},
378 {ID: 5, Parent: root, Value: []int64{1, 70, 101}},
379 })
380
381 So(ds.RunInTransaction(func(c context.Context) e rror {
382 ds := datastore.Get(c)
383
384 q = q.Eq("Value", 1).KeysOnly(true)
385 vals := []*datastore.Key{}
386 So(ds.GetAll(q, &vals), ShouldBeNil)
387 So(len(vals), ShouldEqual, 3)
388 So(vals[2], ShouldResemble, ds.MakeKey(" Parent", 1, "Foo", 5))
389
390 // can remove keys
391 So(ds.Delete(ds.MakeKey("Parent", 1, "Fo o", 2)), ShouldBeNil)
392 vals = []*datastore.Key{}
393 So(ds.GetAll(q, &vals), ShouldBeNil)
394 So(len(vals), ShouldEqual, 2)
395
396 // and add new ones
397 So(ds.Put(&Foo{ID: 1, Parent: root, Valu e: []int64{1, 7, 100}}), ShouldBeNil)
398 So(ds.Put(&Foo{ID: 7, Parent: root, Valu e: []int64{20, 1}}), ShouldBeNil)
399 vals = []*datastore.Key{}
400 So(ds.GetAll(q, &vals), ShouldBeNil)
401 So(len(vals), ShouldEqual, 4)
402
403 So(vals[0].IntID(), ShouldEqual, 1)
404 So(vals[1].IntID(), ShouldEqual, 4)
405 So(vals[2].IntID(), ShouldEqual, 5)
406 So(vals[3].IntID(), ShouldEqual, 7)
407
408 return nil
409 }, nil), ShouldBeNil)
410 })
411
412 Convey("project", func() {
413 _, _, ds := mkds([]*Foo{
414 {ID: 2, Parent: root, Value: []int64{1, 2, 3, 4, 5, 6, 7}},
415 {ID: 3, Parent: root, Value: []int64{3, 4, 5, 6, 7, 8, 9}},
416 {ID: 4, Parent: root, Value: []int64{3, 5, 7, 9, 11, 100, 1}},
417 {ID: 5, Parent: root, Value: []int64{1, 70, 101}},
418 })
419
420 ds.Testable().AddIndexes(&datastore.IndexDefinit ion{
421 Kind: "Foo",
422 Ancestor: true,
423 SortBy: []datastore.IndexColumn{
424 {Property: "Value"},
425 },
426 })
427
428 So(ds.RunInTransaction(func(c context.Context) e rror {
429 ds := datastore.Get(c)
430
431 q = q.Project("Value").Offset(4).Limit(1 0)
432
433 vals := []datastore.PropertyMap{}
434 So(ds.GetAll(q, &vals), ShouldBeNil)
435 So(len(vals), ShouldEqual, 10)
436
437 expect := []struct {
438 id int64
439 val int64
440 }{
441 {2, 3},
442 {3, 3},
443 {4, 3},
444 {2, 4},
445 {3, 4},
446 {2, 5},
447 {3, 5},
448 {4, 5},
449 {2, 6},
450 {3, 6},
451 }
452
453 for i, pm := range vals {
454 So(pm.GetMetaDefault("key", nil) , ShouldResemble, ds.MakeKey("Parent", 1, "Foo", expect[i].id))
455 So(pm["Value"][0].Value(), Shoul dEqual, expect[i].val)
456 }
457
458 // should remove 4 entries, but there ar e plenty more to fill
459 So(ds.Delete(ds.MakeKey("Parent", 1, "Fo o", 2)), ShouldBeNil)
460
461 vals = []datastore.PropertyMap{}
462 So(ds.GetAll(q, &vals), ShouldBeNil)
463 So(len(vals), ShouldEqual, 10)
464
465 expect = []struct {
466 id int64
467 val int64
468 }{
469 // note (3, 3) and (4, 3) are co rrectly missing because deleting
470 // 2 removed two entries which a re hidden by the Offset(4).
471 {3, 4},
472 {3, 5},
473 {4, 5},
474 {3, 6},
475 {3, 7},
476 {4, 7},
477 {3, 8},
478 {3, 9},
479 {4, 9},
480 {4, 11},
481 }
482
483 for i, pm := range vals {
484 So(pm.GetMetaDefault("key", nil) , ShouldResemble, ds.MakeKey("Parent", 1, "Foo", expect[i].id))
485 So(pm["Value"][0].Value(), Shoul dEqual, expect[i].val)
486 }
487
488 So(ds.Put(&Foo{ID: 1, Parent: root, Valu e: []int64{3, 9}}), ShouldBeNil)
489
490 vals = []datastore.PropertyMap{}
491 So(ds.GetAll(q, &vals), ShouldBeNil)
492 So(len(vals), ShouldEqual, 10)
493
494 expect = []struct {
495 id int64
496 val int64
497 }{
498 // 'invisible' {1, 3} entry bump s the {4, 3} into view.
499 {4, 3},
500 {3, 4},
501 {3, 5},
502 {4, 5},
503 {3, 6},
504 {3, 7},
505 {4, 7},
506 {3, 8},
507 {1, 9},
508 {3, 9},
509 {4, 9},
510 }
511
512 for i, pm := range vals {
513 So(pm.GetMetaDefault("key", nil) , ShouldResemble, ds.MakeKey("Parent", 1, "Foo", expect[i].id))
514 So(pm["Value"][0].Value(), Shoul dEqual, expect[i].val)
515 }
516
517 return nil
518 }, nil), ShouldBeNil)
519
520 })
521
522 Convey("project+distinct", func() {
523 _, _, ds := mkds([]*Foo{
524 {ID: 2, Parent: root, Value: []int64{1, 2, 3, 4, 5, 6, 7}},
525 {ID: 3, Parent: root, Value: []int64{3, 4, 5, 6, 7, 8, 9}},
526 {ID: 4, Parent: root, Value: []int64{3, 5, 7, 9, 11, 100, 1}},
527 {ID: 5, Parent: root, Value: []int64{1, 70, 101}},
528 })
529
530 ds.Testable().AddIndexes(&datastore.IndexDefinit ion{
531 Kind: "Foo",
532 Ancestor: true,
533 SortBy: []datastore.IndexColumn{
534 {Property: "Value"},
535 },
536 })
537
538 So(ds.RunInTransaction(func(c context.Context) e rror {
539 ds := datastore.Get(c)
540
541 q = q.Project("Value").Distinct(true)
542
543 vals := []datastore.PropertyMap{}
544 So(ds.GetAll(q, &vals), ShouldBeNil)
545 So(len(vals), ShouldEqual, 13)
546
547 expect := []struct {
548 id int64
549 val int64
550 }{
551 {2, 1},
552 {2, 2},
553 {2, 3},
554 {2, 4},
555 {2, 5},
556 {2, 6},
557 {2, 7},
558 {3, 8},
559 {3, 9},
560 {4, 11},
561 {5, 70},
562 {4, 100},
563 {5, 101},
564 }
565
566 for i, pm := range vals {
567 So(pm["Value"][0].Value(), Shoul dEqual, expect[i].val)
568 So(pm.GetMetaDefault("key", nil) , ShouldResemble, ds.MakeKey("Parent", 1, "Foo", expect[i].id))
569 }
570
571 return nil
572 }, nil), ShouldBeNil)
573 })
574
575 Convey("overwrite", func() {
576 data := []*Foo{
577 {ID: 2, Parent: root, Value: []int64{1, 2, 3, 4, 5, 6, 7}},
578 {ID: 3, Parent: root, Value: []int64{3, 4, 5, 6, 7, 8, 9}},
579 {ID: 4, Parent: root, Value: []int64{3, 5, 7, 9, 11, 100, 1, 2}},
580 {ID: 5, Parent: root, Value: []int64{1, 70, 101}},
581 }
582
583 _, _, ds := mkds(data)
584
585 q = q.Eq("Value", 2, 3)
586
587 So(ds.RunInTransaction(func(c context.Context) e rror {
588 ds := datastore.Get(c)
589
590 vals := []*Foo{}
591 So(ds.GetAll(q, &vals), ShouldBeNil)
592 So(len(vals), ShouldEqual, 2)
593
594 So(vals[0], ShouldResemble, data[0])
595 So(vals[1], ShouldResemble, data[2])
596
597 foo2 := &Foo{ID: 2, Parent: root, Value: []int64{2, 3}}
598 So(ds.Put(foo2), ShouldBeNil)
599
600 vals = []*Foo{}
601 So(ds.GetAll(q, &vals), ShouldBeNil)
602 So(len(vals), ShouldEqual, 2)
603
604 So(vals[0], ShouldResemble, foo2)
605 So(vals[1], ShouldResemble, data[2])
606
607 foo1 := &Foo{ID: 1, Parent: root, Value: []int64{2, 3}}
608 So(ds.Put(foo1), ShouldBeNil)
609
610 vals = []*Foo{}
611 So(ds.GetAll(q, &vals), ShouldBeNil)
612 So(len(vals), ShouldEqual, 3)
613
614 So(vals[0], ShouldResemble, foo1)
615 So(vals[1], ShouldResemble, foo2)
616 So(vals[2], ShouldResemble, data[2])
617
618 return nil
619 }, nil), ShouldBeNil)
620 })
621
622 projectData := []*Foo{
623 {ID: 2, Parent: root, Value: []int64{1, 2, 3, 4, 5, 6, 7}, Sort: []string{"x", "z"}},
624 {ID: 3, Parent: root, Value: []int64{3, 4, 5, 6, 7, 8, 9}, Sort: []string{"b"}},
625 {ID: 4, Parent: root, Value: []int64{3, 5, 7, 9, 11, 100, 1, 2}, Sort: []string{"aa", "a"}},
626 {ID: 5, Parent: root, Value: []int64{1, 70, 101} , Sort: []string{"c"}},
627 }
628
629 Convey("project+extra orders", func() {
630
631 _, _, ds := mkds(projectData)
632 ds.Testable().AddIndexes(&datastore.IndexDefinit ion{
633 Kind: "Foo",
634 Ancestor: true,
635 SortBy: []datastore.IndexColumn{
636 {Property: "Sort", Descending: t rue},
637 {Property: "Value", Descending: true},
638 },
639 })
640
641 q = q.Project("Value").Order("-Sort", "-Value"). Distinct(true)
642 So(ds.RunInTransaction(func(c context.Context) e rror {
643 ds = datastore.Get(c)
644
645 So(ds.Put(&Foo{
646 ID: 1, Parent: root, Value: []in t64{0, 1, 1000},
647 Sort: []string{"zz"}}), ShouldBe Nil)
648
649 vals := []datastore.PropertyMap{}
650 So(ds.GetAll(q, &vals), ShouldBeNil)
651
652 expect := []struct {
653 id int64
654 val int64
655 }{
656 {1, 1000},
657 {1, 1},
658 {1, 0},
659 {2, 7},
660 {2, 6},
661 {2, 5},
662 {2, 4},
663 {2, 3},
664 {2, 2},
665 {5, 101},
666 {5, 70},
667 {3, 9},
668 {3, 8},
669 {4, 100},
670 {4, 11},
671 }
672
673 for i, pm := range vals {
674 So(pm["Value"][0].Value(), Shoul dEqual, expect[i].val)
675 So(pm.GetMetaDefault("key", nil) , ShouldResemble, ds.MakeKey("Parent", 1, "Foo", expect[i].id))
676 }
677
678 return nil
679 }, nil), ShouldBeNil)
680 })
681
682 Convey("buffered entity sorts before ineq, but after fir st parent entity", func() {
683 // If we got this wrong, we'd see Foo,3 come bef ore Foo,2. This might
684 // happen because we calculate the comparison st ring for each entity
685 // based on the whole entity, but we forgot to l imit the comparison
686 // string generation by the inequality criteria.
687 data := []*Foo{
688 {ID: 2, Parent: root, Value: []int64{2, 3, 5, 6}, Sort: []string{"z"}},
689 }
690
691 _, _, ds := mkds(data)
692 ds.Testable().AddIndexes(&datastore.IndexDefinit ion{
693 Kind: "Foo",
694 Ancestor: true,
695 SortBy: []datastore.IndexColumn{
696 {Property: "Value"},
697 },
698 })
699
700 q = q.Gt("Value", 2).Limit(2)
701
702 So(ds.RunInTransaction(func(c context.Context) e rror {
703 ds = datastore.Get(c)
704
705 foo1 := &Foo{ID: 3, Parent: root, Value: []int64{0, 2, 3, 4}}
706 So(ds.Put(foo1), ShouldBeNil)
707
708 vals := []*Foo{}
709 So(ds.GetAll(q, &vals), ShouldBeNil)
710 So(len(vals), ShouldEqual, 2)
711
712 So(vals[0], ShouldResemble, data[0])
713 So(vals[1], ShouldResemble, foo1)
714
715 return nil
716 }, nil), ShouldBeNil)
717 })
718
719 Convey("keysOnly+extra orders", func() {
720 _, _, ds := mkds(projectData)
721 ds.Testable().AddIndexes(&datastore.IndexDefinit ion{
722 Kind: "Foo",
723 Ancestor: true,
724 SortBy: []datastore.IndexColumn{
725 {Property: "Sort"},
726 },
727 })
728
729 q = q.Order("Sort").KeysOnly(true)
730
731 So(ds.RunInTransaction(func(c context.Context) e rror {
732 ds = datastore.Get(c)
733
734 So(ds.Put(&Foo{
735 ID: 1, Parent: root, Value: []in t64{0, 1, 1000},
736 Sort: []string{"x", "zz"}}), Sho uldBeNil)
737
738 So(ds.Put(&Foo{
739 ID: 2, Parent: root, Value: []in t64{0, 1, 1000},
740 Sort: []string{"zz", "zzz", "zzz z"}}), ShouldBeNil)
741
742 vals := []*datastore.Key{}
743 So(ds.GetAll(q, &vals), ShouldBeNil)
744 So(len(vals), ShouldEqual, 5)
745
746 So(vals, ShouldResemble, []*datastore.Ke y{
747 ds.MakeKey("Parent", 1, "Foo", 4 ),
748 ds.MakeKey("Parent", 1, "Foo", 3 ),
749 ds.MakeKey("Parent", 1, "Foo", 5 ),
750 ds.MakeKey("Parent", 1, "Foo", 1 ),
751 ds.MakeKey("Parent", 1, "Foo", 2 ),
752 })
753
754 return nil
755 }, nil), ShouldBeNil)
756 })
757
758 Convey("query accross nested transactions", func() {
759 _, _, ds := mkds(projectData)
760 q = q.Eq("Value", 2, 3)
761
762 foo1 := &Foo{ID: 1, Parent: root, Value: []int64 {2, 3}}
763 foo7 := &Foo{ID: 7, Parent: root, Value: []int64 {2, 3}}
764
765 So(ds.RunInTransaction(func(c context.Context) e rror {
766 ds := datastore.Get(c)
767
768 So(ds.Put(foo1), ShouldBeNil)
769
770 vals := []*Foo{}
771 So(ds.GetAll(q, &vals), ShouldBeNil)
772 So(vals, ShouldResemble, []*Foo{foo1, pr ojectData[0], projectData[2]})
773
774 So(ds.RunInTransaction(func(c context.Co ntext) error {
775 ds := datastore.Get(c)
776
777 vals := []*Foo{}
778 So(ds.GetAll(q, &vals), ShouldBe Nil)
779 So(vals, ShouldResemble, []*Foo{ foo1, projectData[0], projectData[2]})
780
781 So(ds.Delete(ds.MakeKey("Parent" , 1, "Foo", 4)), ShouldBeNil)
782 So(ds.Put(foo7), ShouldBeNil)
783
784 vals = []*Foo{}
785 So(ds.GetAll(q, &vals), ShouldBe Nil)
786 So(vals, ShouldResemble, []*Foo{ foo1, projectData[0], foo7})
787
788 return nil
789 }, nil), ShouldBeNil)
790
791 vals = []*Foo{}
792 So(ds.GetAll(q, &vals), ShouldBeNil)
793 So(vals, ShouldResemble, []*Foo{foo1, pr ojectData[0], foo7})
794
795 return nil
796 }, nil), ShouldBeNil)
797
798 vals := []*Foo{}
799 So(ds.GetAll(q, &vals), ShouldBeNil)
800 So(vals, ShouldResemble, []*Foo{foo1, projectDat a[0], foo7})
801
802 })
803
804 })
805
806 })
807
808 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698