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

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

Powered by Google App Engine
This is Rietveld 408576698