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

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

Issue 1152383003: Simple memory testing for gae/wrapper (Closed) Base URL: https://chromium.googlesource.com/infra/infra.git@better_context_lite
Patch Set: fix capitalization Created 5 years, 6 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 "testing"
12
13 . "github.com/smartystreets/goconvey/convey"
14 "golang.org/x/net/context"
15
16 "appengine/datastore"
17 )
18
19 func TestDatastoreKinder(t *testing.T) {
20 Convey("Datastore kinds and keys", t, func() {
21 c := Use(Enable(context.Background()))
22 ds := wrapper.GetDS(c)
23 So(ds, ShouldNotBeNil)
24
25 Convey("implements DSKinder", func() {
26 type Foo struct{}
27 So(ds.Kind(&Foo{}), ShouldEqual, "Foo")
28
29 Convey("which can be tweaked by DSKindSetter", func() {
30 ds.SetKindNameResolver(func(interface{}) string { return "spam" })
31 So(ds.Kind(&Foo{}), ShouldEqual, "spam")
32
33 Convey("and it retains the function so you can s tack them", func() {
34 cur := ds.KindNameResolver()
35 ds.SetKindNameResolver(func(o interface{ }) string { return "wat" + cur(o) })
36 So(ds.Kind(&Foo{}), ShouldEqual, "watspa m")
37 })
38 })
39 })
40
41 Convey("implements DSNewKeyer", func() {
42 Convey("NewKey", func() {
43 key := ds.NewKey("nerd", "stringID", 0, nil)
44 So(key, ShouldNotBeNil)
45 So(key.Kind(), ShouldEqual, "nerd")
46 So(key.StringID(), ShouldEqual, "stringID")
47 So(key.IntID(), ShouldEqual, 0)
48 So(key.Parent(), ShouldBeNil)
49 So(key.AppID(), ShouldEqual, "dev~my~app")
50 So(key.Namespace(), ShouldEqual, "")
51 So(key.String(), ShouldEqual, "/nerd,stringID")
52 So(key.Incomplete(), ShouldBeFalse)
53 So(KeyValid("", key, UserKeyOnly), ShouldBeTrue)
54
55 chkey := ds.NewKey("wat", "", 100, key)
56 So(chkey, ShouldNotBeNil)
57 So(chkey.Kind(), ShouldEqual, "wat")
58 So(chkey.StringID(), ShouldEqual, "")
59 So(chkey.IntID(), ShouldEqual, 100)
60 So(chkey.Parent(), ShouldEqual, key)
61 So(chkey.AppID(), ShouldEqual, "dev~my~app")
62 So(chkey.Namespace(), ShouldEqual, "")
63 So(chkey.String(), ShouldEqual, "/nerd,stringID/ wat,100")
64 So(key.Incomplete(), ShouldBeFalse)
65 So(KeyValid("", chkey, UserKeyOnly), ShouldBeTru e)
66
67 incompl := ds.NewKey("sup", "", 0, key)
68 So(incompl, ShouldNotBeNil)
69 So(incompl.Incomplete(), ShouldBeTrue)
70 So(KeyValid("", incompl, UserKeyOnly), ShouldBeT rue)
71 So(incompl.String(), ShouldEqual, "/nerd,stringI D/sup,0")
72
73 bad := ds.NewKey("nooo", "", 10, incompl)
74 So(bad, ShouldNotBeNil)
75 So(bad.Incomplete(), ShouldBeFalse)
76 So(KeyValid("", bad, UserKeyOnly), ShouldBeFalse )
77 So(bad.String(), ShouldEqual, "/nerd,stringID/su p,0/nooo,10")
78
79 So(rootKey(bad), ShouldEqual, key)
80
81 Convey("other key validation", func() {
82 So(KeyValid("", nil, UserKeyOnly), Shoul dBeFalse)
83
84 key := ds.NewKey("", "", 0, nil)
85 So(key, ShouldNotBeNil)
86
87 So(KeyValid("", key, UserKeyOnly), Shoul dBeFalse)
88
89 key = ds.NewKey("noop", "power level", 9 000, nil)
90 So(key, ShouldNotBeNil)
91
92 So(KeyValid("", key, UserKeyOnly), Shoul dBeFalse)
93 })
94 })
95
96 Convey("NewKeyObj", func() {
97 type Foo struct {
98 _knd string `goon:"kind,coool" `
99 ID int64 `goon:"id"`
100 Parent *datastore.Key `goon:"parent"`
101 }
102 f := &Foo{ID: 100}
103 k := ds.NewKeyObj(f)
104 So(k.String(), ShouldEqual, "/coool,100")
105
106 f.Parent = k
107 f._knd = "weevils"
108 f.ID = 19
109 k = ds.NewKeyObj(f)
110 So(k.String(), ShouldEqual, "/coool,100/weevils, 19")
111
112 Convey("panics when you do a dumb thing", func() {
113 type Foo struct {
114 ID []byte `goon:"id"`
115 }
116 So(func() { ds.NewKeyObj(&Foo{}) }, Shou ldPanic)
117 })
118 })
119
120 Convey("NewKeyObjError", func() {
121 type Foo struct {
122 ID []byte `goon:"id"`
123 }
124 _, err := ds.NewKeyObjError(&Foo{})
125 So(err.Error(), ShouldContainSubstring, "must be int64 or string")
126 })
127 })
128
129 })
130 }
131
132 func TestDatastoreSingleReadWriter(t *testing.T) {
133 Convey("Datastore single reads and writes", t, func() {
134 c := Use(Enable(context.Background()))
135 ds := wrapper.GetDS(c)
136 So(ds, ShouldNotBeNil)
137
138 Convey("implements DSSingleReadWriter", func() {
139 type Foo struct {
140 ID int64 `goon:"id" datastore:"-"`
141 Parent *datastore.Key `goon:"parent" datastore:" -"`
142 Val int
143 }
144
145 Convey("invalid keys break", func() {
146 k := ds.NewKeyObj(&Foo{})
147 f := &Foo{Parent: k}
148 So(ds.Get(f), ShouldEqual, datastore.ErrInvalidK ey)
149
150 _, err := ds.Put(f)
151 So(err, ShouldEqual, datastore.ErrInvalidKey)
152 })
153
154 Convey("getting objects that DNE is an error", func() {
155 So(ds.Get(&Foo{ID: 1}), ShouldEqual, datastore.E rrNoSuchEntity)
156 })
157
158 Convey("Can Put stuff", func() {
159 // with an incomplete key!
160 f := &Foo{Val: 10}
161 k, err := ds.Put(f)
162 So(err, ShouldBeNil)
163 So(k.String(), ShouldEqual, "/Foo,1")
164 So(ds.NewKeyObj(f), ShouldResemble, k)
165
166 Convey("and Get it back", func() {
167 newFoo := &Foo{ID: 1}
168 err := ds.Get(newFoo)
169 So(err, ShouldBeNil)
170 So(newFoo, ShouldResemble, f)
171
172 Convey("and we can Delete it", func() {
173 err := ds.Delete(ds.NewKey("Foo" , "", 1, nil))
174 So(err, ShouldBeNil)
175
176 err = ds.Get(newFoo)
177 So(err, ShouldEqual, datastore.E rrNoSuchEntity)
178 })
179 })
180 Convey("Deleteing with a bogus key is bad", func () {
181 err := ds.Delete(ds.NewKey("Foo", "wat", 100, nil))
182 So(err, ShouldEqual, datastore.ErrInvali dKey)
183 })
184 Convey("Deleteing a DNE entity is fine", func() {
185 err := ds.Delete(ds.NewKey("Foo", "wat", 0, nil))
186 So(err, ShouldBeNil)
187 })
188
189 Convey("serialization breaks in the normal ways" , func() {
190 type BadFoo struct {
191 _kind string `goon:"kind,Foo"`
192 ID int64 `goon:"id" datastor e:"-"`
193 Val uint8
194 }
195 _, err := ds.Put(&BadFoo{})
196 So(err.Error(), ShouldContainSubstring,
197 "unsupported struct field type: uint8")
198
199 err = ds.Get(&BadFoo{ID: 1})
200 So(err.Error(), ShouldContainSubstring,
201 "type mismatch: int versus uint8 ")
202 })
203
204 Convey("check that metadata works", func() {
205 val, _ := meta.GetEntityGroupVersion(c, k)
206 So(val, ShouldEqual, 1)
207
208 for i := 0; i < 10; i++ {
209 _, err = ds.Put(&Foo{Val: 10, Pa rent: k})
210 So(err, ShouldBeNil)
211 }
212 val, _ = meta.GetEntityGroupVersion(c, k )
213 So(val, ShouldEqual, 11)
214
215 Convey("ensure that group versions persi st across deletes", func() {
216 So(ds.Delete(k), ShouldBeNil)
217 for i := int64(1); i < 11; i++ {
218 So(ds.Delete(ds.NewKey(" Foo", "", i, k)), ShouldBeNil)
219 }
220 // TODO(riannucci): replace with a Count query instead of this cast
221 ents := ds.(*dsImpl).data.store. GetCollection("ents:")
222 num, _ := ents.GetTotals()
223 // /__entity_root_ids__,Foo
224 // /Foo,1/__entity_group__,1
225 // /Foo,1/__entity_group_ids__,1
226 So(num, ShouldEqual, 3)
227
228 version, err := curVersion(ents, groupMetaKey(k))
229 So(err, ShouldBeNil)
230 So(version, ShouldEqual, 22)
231
232 k, err := ds.Put(f)
233 So(err, ShouldBeNil)
234 val, _ := meta.GetEntityGroupVer sion(c, k)
235 So(val, ShouldEqual, 23)
236 })
237 })
238 })
239 })
240
241 Convey("implements DSTransactioner", func() {
242 type Foo struct {
243 ID int64 `goon:"id" datastore:"-"`
244 Parent *datastore.Key `goon:"parent" datastore:" -"`
245 Val int
246 }
247 Convey("Put", func() {
248 f := &Foo{Val: 10}
249 k, err := ds.Put(f)
250 So(err, ShouldBeNil)
251 So(k.String(), ShouldEqual, "/Foo,1")
252 So(ds.NewKeyObj(f), ShouldResemble, k)
253
254 Convey("can Put new entity groups", func() {
255 err := ds.RunInTransaction(func(c contex t.Context) error {
256 ds := wrapper.GetDS(c)
257 So(ds, ShouldNotBeNil)
258
259 f1 := &Foo{Val: 100}
260 k, err := ds.Put(f1)
261 So(err, ShouldBeNil)
262 So(k.String(), ShouldEqual, "/Fo o,2")
263
264 f2 := &Foo{Val: 200}
265 k, err = ds.Put(f2)
266 So(err, ShouldBeNil)
267 So(k.String(), ShouldEqual, "/Fo o,3")
268
269 return nil
270 }, &datastore.TransactionOptions{XG: tru e})
271 So(err, ShouldBeNil)
272
273 f := &Foo{ID: 2}
274 So(ds.Get(f), ShouldBeNil)
275 So(f.Val, ShouldEqual, 100)
276
277 f = &Foo{ID: 3}
278 So(ds.Get(f), ShouldBeNil)
279 So(f.Val, ShouldEqual, 200)
280 })
281
282 Convey("can Put new entities in a current group" , func() {
283 err := ds.RunInTransaction(func(c contex t.Context) error {
284 ds := wrapper.GetDS(c)
285 So(ds, ShouldNotBeNil)
286
287 f1 := &Foo{Val: 100, Parent: ds. NewKeyObj(f)}
288 k, err := ds.Put(f1)
289 So(err, ShouldBeNil)
290 So(k.String(), ShouldEqual, "/Fo o,1/Foo,1")
291
292 f2 := &Foo{Val: 200, Parent: ds. NewKeyObj(f)}
293 k, err = ds.Put(f2)
294 So(err, ShouldBeNil)
295 So(k.String(), ShouldEqual, "/Fo o,1/Foo,2")
296
297 return nil
298 }, nil)
299 So(err, ShouldBeNil)
300
301 f1 := &Foo{ID: 1, Parent: ds.NewKeyObj(& Foo{ID: 1})}
302 So(ds.Get(f1), ShouldBeNil)
303 So(f1.Val, ShouldEqual, 100)
304
305 f2 := &Foo{ID: 2, Parent: f1.Parent}
306 So(ds.Get(f2), ShouldBeNil)
307 So(f2.Val, ShouldEqual, 200)
308 })
309
310 Convey("Deletes work too", func() {
311 err := ds.RunInTransaction(func(c contex t.Context) error {
312 ds := wrapper.GetDS(c)
313 So(ds, ShouldNotBeNil)
314 So(ds.Delete(ds.NewKeyObj(f)), S houldBeNil)
315 return nil
316 }, nil)
317 So(err, ShouldBeNil)
318 So(ds.Get(f), ShouldEqual, datastore.Err NoSuchEntity)
319 })
320
321 Convey("A Get counts against your group count", func() {
322 err := ds.RunInTransaction(func(c contex t.Context) error {
323 ds := wrapper.GetDS(c)
324 f := &Foo{ID: 20}
325 So(ds.Get(f), ShouldEqual, datas tore.ErrNoSuchEntity)
326
327 f.ID = 1
328 So(ds.Get(f).Error(), ShouldCont ainSubstring, "cross-group")
329 return nil
330 }, nil)
331 So(err, ShouldBeNil)
332 })
333
334 Convey("Get takes a snapshot", func() {
335 err := ds.RunInTransaction(func(c contex t.Context) error {
336 txnDS := wrapper.GetDS(c)
337 So(txnDS, ShouldNotBeNil)
338
339 f := &Foo{ID: 1}
340 So(txnDS.Get(f), ShouldBeNil)
341 So(f.Val, ShouldEqual, 10)
342
343 // Don't ever do this in a real program unless you want to guarantee
344 // a failed transaction :)
345 f.Val = 11
346 _, err := ds.Put(f)
347 So(err, ShouldBeNil)
348
349 So(txnDS.Get(f), ShouldBeNil)
350 So(f.Val, ShouldEqual, 10)
351
352 return nil
353 }, nil)
354 So(err, ShouldBeNil)
355
356 f := &Foo{ID: 1}
357 So(ds.Get(f), ShouldBeNil)
358 So(f.Val, ShouldEqual, 11)
359
360 })
361
362 Convey("and snapshots are consistent even after Puts", func() {
363 err := ds.RunInTransaction(func(c contex t.Context) error {
364 txnDS := wrapper.GetDS(c)
365 So(txnDS, ShouldNotBeNil)
366
367 f := &Foo{ID: 1}
368 So(txnDS.Get(f), ShouldBeNil)
369 So(f.Val, ShouldEqual, 10)
370
371 // Don't ever do this in a real program unless you want to guarantee
372 // a failed transaction :)
373 f.Val = 11
374 _, err := ds.Put(f)
375 So(err, ShouldBeNil)
376
377 So(txnDS.Get(f), ShouldBeNil)
378 So(f.Val, ShouldEqual, 10)
379
380 f.Val = 20
381 _, err = txnDS.Put(f)
382 So(err, ShouldBeNil)
383
384 So(txnDS.Get(f), ShouldBeNil)
385 So(f.Val, ShouldEqual, 10) // st ill gets 10
386
387 return nil
388 }, nil)
389 So(err.Error(), ShouldContainSubstring, "concurrent")
390
391 f := &Foo{ID: 1}
392 So(ds.Get(f), ShouldBeNil)
393 So(f.Val, ShouldEqual, 11)
394 })
395
396 Convey("Reusing a transaction context is bad new s", func() {
397 var txnDS wrapper.Datastore
398 err := ds.RunInTransaction(func(c contex t.Context) error {
399 txnDS = wrapper.GetDS(c)
400 So(txnDS.Get(&Foo{ID: 1}), Shoul dBeNil)
401 return nil
402 }, nil)
403 So(err, ShouldBeNil)
404 So(txnDS.Get(&Foo{ID: 1}).Error(), Shoul dContainSubstring, "expired")
405 })
406
407 Convey("Nested transactions are rejected", func( ) {
408 err := ds.RunInTransaction(func(c contex t.Context) error {
409 err := wrapper.GetDS(c).RunInTra nsaction(func(c context.Context) error {
410 panic("noooo")
411 }, nil)
412 So(err.Error(), ShouldContainSub string, "nested transactions")
413 return nil
414 }, nil)
415 So(err, ShouldBeNil)
416 })
417
418 Convey("Concurrent transactions only accept one set of changes", func() {
419 // Note: I think this implementation is actually /slightly/ wrong.
420 // Accorting to my read of the docs for appengine, when you open a
421 // transaction it actually (essentially) holds a reference to the
422 // entire datastore. Our implementation takes a snapshot of the
423 // entity group as soon as something obs erves/affects it.
424 //
425 // That said... I'm not sure if there's really a semantic difference.
426 err := ds.RunInTransaction(func(c contex t.Context) error {
427 txnDS := wrapper.GetDS(c)
428 f := &Foo{ID: 1, Val: 21}
429 _, err = txnDS.Put(f)
430 So(err, ShouldBeNil)
431
432 err := ds.RunInTransaction(func( c context.Context) error {
433 txnDS := wrapper.GetDS(c )
434 f := &Foo{ID: 1, Val: 27 }
435 _, err := txnDS.Put(f)
436 So(err, ShouldBeNil)
437 return nil
438 }, nil)
439 So(err, ShouldBeNil)
440
441 return nil
442 }, nil)
443 So(err.Error(), ShouldContainSubstring, "concurrent")
444
445 f := &Foo{ID: 1}
446 So(ds.Get(f), ShouldBeNil)
447 So(f.Val, ShouldEqual, 27)
448 })
449
450 Convey("XG", func() {
451 Convey("Modifying two groups with XG=fal se is invalid", func() {
452 err := ds.RunInTransaction(func( c context.Context) error {
453 ds := wrapper.GetDS(c)
454 f := &Foo{ID: 1, Val: 20 0}
455 _, err := ds.Put(f)
456 So(err, ShouldBeNil)
457
458 f.ID = 2
459 _, err = ds.Put(f)
460 So(err.Error(), ShouldCo ntainSubstring, "cross-group")
461 return err
462 }, nil)
463 So(err.Error(), ShouldContainSub string, "cross-group")
464 })
465
466 Convey("Modifying >25 groups with XG=tru e is invald", func() {
467 err := ds.RunInTransaction(func( c context.Context) error {
468 ds := wrapper.GetDS(c)
469 for i := int64(1); i < 2 6; i++ {
470 f := &Foo{ID: i, Val: 200}
471 _, err := ds.Put (f)
472 So(err, ShouldBe Nil)
473 }
474 f := &Foo{ID: 27, Val: 2 00}
475 _, err := ds.Put(f)
476 So(err.Error(), ShouldCo ntainSubstring, "too many entity groups")
477 return err
478 }, &datastore.TransactionOptions {XG: true})
479 So(err.Error(), ShouldContainSub string, "too many entity groups")
480 })
481 })
482
483 Convey("Errors and panics", func() {
484 Convey("returning an error aborts", func () {
485 err := ds.RunInTransaction(func( c context.Context) error {
486 ds := wrapper.GetDS(c)
487 f := &Foo{ID: 1, Val: 20 0}
488 _, err := ds.Put(f)
489 So(err, ShouldBeNil)
490
491 return fmt.Errorf("thing y")
492 }, nil)
493 So(err.Error(), ShouldEqual, "th ingy")
494
495 f := &Foo{ID: 1}
496 So(ds.Get(f), ShouldBeNil)
497 So(f.Val, ShouldEqual, 10)
498 })
499
500 Convey("panicing aborts", func() {
501 So(func() {
502 ds.RunInTransaction(func (c context.Context) error {
503 ds := wrapper.Ge tDS(c)
504 f := &Foo{ID: 1, Val: 200}
505 _, err := ds.Put (f)
506 So(err, ShouldBe Nil)
507 panic("wheeeeee" )
508 }, nil)
509 }, ShouldPanic)
510
511 f := &Foo{ID: 1}
512 So(ds.Get(f), ShouldBeNil)
513 So(f.Val, ShouldEqual, 10)
514 })
515 })
516 })
517 })
518
519 })
520 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698