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

Side by Side Diff: impl/memory/datastore_test.go

Issue 2302743002: Interface update, per-method Contexts. (Closed)
Patch Set: Created 4 years, 3 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
1 // Copyright 2015 The LUCI Authors. All rights reserved. 1 // Copyright 2015 The LUCI Authors. All rights reserved.
2 // Use of this source code is governed under the Apache License, Version 2.0 2 // Use of this source code is governed under the Apache License, Version 2.0
3 // that can be found in the LICENSE file. 3 // that can be found in the LICENSE file.
4 4
5 package memory 5 package memory
6 6
7 import ( 7 import (
8 "errors"
8 "fmt" 9 "fmt"
9 "testing" 10 "testing"
10 "time" 11 "time"
11 12
12 » dsS "github.com/luci/gae/service/datastore" 13 » ds "github.com/luci/gae/service/datastore"
13 "github.com/luci/gae/service/datastore/serialize" 14 "github.com/luci/gae/service/datastore/serialize"
14 infoS "github.com/luci/gae/service/info" 15 infoS "github.com/luci/gae/service/info"
16
17 "golang.org/x/net/context"
18
15 . "github.com/luci/luci-go/common/testing/assertions" 19 . "github.com/luci/luci-go/common/testing/assertions"
16 . "github.com/smartystreets/goconvey/convey" 20 . "github.com/smartystreets/goconvey/convey"
17 "golang.org/x/net/context"
18 ) 21 )
19 22
20 type MetaGroup struct { 23 type MetaGroup struct {
21 » _id int64 `gae:"$id,1"` 24 » _id int64 `gae:"$id,1"`
22 » _kind string `gae:"$kind,__entity_group__"` 25 » _kind string `gae:"$kind,__entity_group__"`
23 » Parent *dsS.Key `gae:"$parent"` 26 » Parent *ds.Key `gae:"$parent"`
24 27
25 Version int64 `gae:"__version__"` 28 Version int64 `gae:"__version__"`
26 } 29 }
27 30
28 func testGetMeta(c context.Context, k *dsS.Key) int64 { 31 func testGetMeta(c context.Context, k *ds.Key) int64 {
29 » ds := dsS.Get(c)
30 mg := &MetaGroup{Parent: k.Root()} 32 mg := &MetaGroup{Parent: k.Root()}
31 » if err := ds.Get(mg); err != nil { 33 » if err := ds.Get(c, mg); err != nil {
32 panic(err) 34 panic(err)
33 } 35 }
34 return mg.Version 36 return mg.Version
35 } 37 }
36 38
37 var pls = dsS.GetPLS 39 var pls = ds.GetPLS
38 40
39 type Foo struct { 41 type Foo struct {
40 » ID int64 `gae:"$id"` 42 » ID int64 `gae:"$id"`
41 » Parent *dsS.Key `gae:"$parent"` 43 » Parent *ds.Key `gae:"$parent"`
42 44
43 Val int 45 Val int
44 Name string 46 Name string
45 } 47 }
46 48
47 func TestDatastoreSingleReadWriter(t *testing.T) { 49 func TestDatastoreSingleReadWriter(t *testing.T) {
48 t.Parallel() 50 t.Parallel()
49 51
50 Convey("Datastore single reads and writes", t, func() { 52 Convey("Datastore single reads and writes", t, func() {
51 c := Use(context.Background()) 53 c := Use(context.Background())
52 » » ds := dsS.Get(c) 54 » » So(ds.Raw(c), ShouldNotBeNil)
53 » » So(ds, ShouldNotBeNil)
54 55
55 Convey("getting objects that DNE is an error", func() { 56 Convey("getting objects that DNE is an error", func() {
56 » » » So(ds.Get(&Foo{ID: 1}), ShouldEqual, dsS.ErrNoSuchEntity ) 57 » » » So(ds.Get(c, &Foo{ID: 1}), ShouldEqual, ds.ErrNoSuchEnti ty)
57 }) 58 })
58 59
59 Convey("bad namespaces fail", func() { 60 Convey("bad namespaces fail", func() {
60 » » » _, err := infoS.Get(c).Namespace("$$blzyall") 61 » » » _, err := infoS.Namespace(c, "$$blzyall")
61 So(err.Error(), ShouldContainSubstring, "namespace \"$$b lzyall\" does not match") 62 So(err.Error(), ShouldContainSubstring, "namespace \"$$b lzyall\" does not match")
62 }) 63 })
63 64
64 Convey("Can Put stuff", func() { 65 Convey("Can Put stuff", func() {
65 // with an incomplete key! 66 // with an incomplete key!
66 f := &Foo{Val: 10} 67 f := &Foo{Val: 10}
67 » » » So(ds.Put(f), ShouldBeNil) 68 » » » So(ds.Put(c, f), ShouldBeNil)
68 » » » k := ds.KeyForObj(f) 69 » » » k := ds.KeyForObj(c, f)
69 So(k.String(), ShouldEqual, "dev~app::/Foo,1") 70 So(k.String(), ShouldEqual, "dev~app::/Foo,1")
70 71
71 Convey("and Get it back", func() { 72 Convey("and Get it back", func() {
72 newFoo := &Foo{ID: 1} 73 newFoo := &Foo{ID: 1}
73 » » » » So(ds.Get(newFoo), ShouldBeNil) 74 » » » » So(ds.Get(c, newFoo), ShouldBeNil)
74 So(newFoo, ShouldResemble, f) 75 So(newFoo, ShouldResemble, f)
75 76
76 Convey("but it's hidden from a different namespa ce", func() { 77 Convey("but it's hidden from a different namespa ce", func() {
77 » » » » » c, err := infoS.Get(c).Namespace("whomba t") 78 » » » » » c, err := infoS.Namespace(c, "whombat")
78 So(err, ShouldBeNil) 79 So(err, ShouldBeNil)
79 » » » » » ds = dsS.Get(c) 80 » » » » » So(ds.Get(c, f), ShouldEqual, ds.ErrNoSu chEntity)
80 » » » » » So(ds.Get(f), ShouldEqual, dsS.ErrNoSuch Entity)
81 }) 81 })
82 82
83 Convey("and we can Delete it", func() { 83 Convey("and we can Delete it", func() {
84 » » » » » So(ds.Delete(k), ShouldBeNil) 84 » » » » » So(ds.Delete(c, k), ShouldBeNil)
85 » » » » » So(ds.Get(newFoo), ShouldEqual, dsS.ErrN oSuchEntity) 85 » » » » » So(ds.Get(c, newFoo), ShouldEqual, ds.Er rNoSuchEntity)
86 }) 86 })
87 87
88 }) 88 })
89 Convey("Deleteing with a bogus key is bad", func() { 89 Convey("Deleteing with a bogus key is bad", func() {
90 » » » » So(ds.Delete(ds.NewKey("Foo", "wat", 100, nil)), ShouldEqual, dsS.ErrInvalidKey) 90 » » » » So(ds.Delete(c, ds.NewKey(c, "Foo", "wat", 100, nil)), ShouldEqual, ds.ErrInvalidKey)
91 }) 91 })
92 Convey("Deleteing a DNE entity is fine", func() { 92 Convey("Deleteing a DNE entity is fine", func() {
93 » » » » So(ds.Delete(ds.NewKey("Foo", "wat", 0, nil)), S houldBeNil) 93 » » » » So(ds.Delete(c, ds.NewKey(c, "Foo", "wat", 0, ni l)), ShouldBeNil)
94 }) 94 })
95 95
96 Convey("Deleting entities from a nonexistant namespace w orks", func() { 96 Convey("Deleting entities from a nonexistant namespace w orks", func() {
97 » » » » aid := infoS.Get(c).FullyQualifiedAppID() 97 » » » » c := infoS.MustNamespace(c, "noexist")
98 » » » » keys := make([]*dsS.Key, 10) 98 » » » » keys := make([]*ds.Key, 10)
99 for i := range keys { 99 for i := range keys {
100 » » » » » keys[i] = ds.MakeKey(aid, "noexist", "Ki nd", i+1) 100 » » » » » keys[i] = ds.MakeKey(c, "Kind", i+1)
101 } 101 }
102 » » » » So(ds.DeleteMulti(keys), ShouldBeNil) 102 » » » » So(ds.Delete(c, keys), ShouldBeNil)
103 count := 0 103 count := 0
104 » » » » So(ds.Raw().DeleteMulti(keys, func(err error) er ror { 104 » » » » So(ds.Raw(c).DeleteMulti(keys, func(err error) e rror {
105 count++ 105 count++
106 So(err, ShouldBeNil) 106 So(err, ShouldBeNil)
107 return nil 107 return nil
108 }), ShouldBeNil) 108 }), ShouldBeNil)
109 So(count, ShouldEqual, len(keys)) 109 So(count, ShouldEqual, len(keys))
110 }) 110 })
111 111
112 Convey("with multiple puts", func() { 112 Convey("with multiple puts", func() {
113 So(testGetMeta(c, k), ShouldEqual, 1) 113 So(testGetMeta(c, k), ShouldEqual, 1)
114 114
115 foos := make([]Foo, 10) 115 foos := make([]Foo, 10)
116 for i := range foos { 116 for i := range foos {
117 foos[i].Val = 10 117 foos[i].Val = 10
118 foos[i].Parent = k 118 foos[i].Parent = k
119 } 119 }
120 » » » » So(ds.PutMulti(foos), ShouldBeNil) 120 » » » » So(ds.Put(c, foos), ShouldBeNil)
121 So(testGetMeta(c, k), ShouldEqual, 11) 121 So(testGetMeta(c, k), ShouldEqual, 11)
122 122
123 » » » » keys := make([]*dsS.Key, len(foos)) 123 » » » » keys := make([]*ds.Key, len(foos))
124 for i, f := range foos { 124 for i, f := range foos {
125 » » » » » keys[i] = ds.KeyForObj(&f) 125 » » » » » keys[i] = ds.KeyForObj(c, &f)
126 } 126 }
127 127
128 Convey("ensure that group versions persist acros s deletes", func() { 128 Convey("ensure that group versions persist acros s deletes", func() {
129 » » » » » So(ds.DeleteMulti(append(keys, k)), Shou ldBeNil) 129 » » » » » So(ds.Delete(c, append(keys, k)), Should BeNil)
130 130
131 » » » » » ds.Testable().CatchupIndexes() 131 » » » » » ds.GetTestable(c).CatchupIndexes()
132 132
133 count := 0 133 count := 0
134 » » » » » So(ds.Run(dsS.NewQuery(""), func(_ *dsS. Key) { 134 » » » » » So(ds.Run(c, ds.NewQuery(""), func(_ *ds .Key) {
135 count++ 135 count++
136 }), ShouldBeNil) 136 }), ShouldBeNil)
137 So(count, ShouldEqual, 3) 137 So(count, ShouldEqual, 3)
138 138
139 So(testGetMeta(c, k), ShouldEqual, 22) 139 So(testGetMeta(c, k), ShouldEqual, 22)
140 140
141 » » » » » So(ds.Put(&Foo{ID: 1}), ShouldBeNil) 141 » » » » » So(ds.Put(c, &Foo{ID: 1}), ShouldBeNil)
142 So(testGetMeta(c, k), ShouldEqual, 23) 142 So(testGetMeta(c, k), ShouldEqual, 23)
143 }) 143 })
144 144
145 Convey("can Get", func() { 145 Convey("can Get", func() {
146 » » » » » vals := make([]dsS.PropertyMap, len(keys )) 146 » » » » » vals := make([]ds.PropertyMap, len(keys) )
147 for i := range vals { 147 for i := range vals {
148 » » » » » » vals[i] = dsS.PropertyMap{} 148 » » » » » » vals[i] = ds.PropertyMap{}
149 So(vals[i].SetMeta("key", keys[i ]), ShouldBeTrue) 149 So(vals[i].SetMeta("key", keys[i ]), ShouldBeTrue)
150 } 150 }
151 » » » » » So(ds.GetMulti(vals), ShouldBeNil) 151 » » » » » So(ds.Get(c, vals), ShouldBeNil)
152 152
153 for i, val := range vals { 153 for i, val := range vals {
154 » » » » » » So(val, ShouldResemble, dsS.Prop ertyMap{ 154 » » » » » » So(val, ShouldResemble, ds.Prope rtyMap{
155 » » » » » » » "Val": {dsS.MkProperty( 10)}, 155 » » » » » » » "Val": {ds.MkProperty(1 0)},
156 » » » » » » » "Name": {dsS.MkProperty( "")}, 156 » » » » » » » "Name": {ds.MkProperty(" ")},
157 » » » » » » » "$key": {dsS.MkPropertyN I(keys[i])}, 157 » » » » » » » "$key": {ds.MkPropertyNI (keys[i])},
158 }) 158 })
159 } 159 }
160 }) 160 })
161 161
162 }) 162 })
163 163
164 Convey("allocating ids prevents their use", func() { 164 Convey("allocating ids prevents their use", func() {
165 » » » » keys := ds.NewIncompleteKeys(100, "Foo", nil) 165 » » » » keys := ds.NewIncompleteKeys(c, 100, "Foo", nil)
166 » » » » So(ds.AllocateIDs(keys), ShouldBeNil) 166 » » » » So(ds.AllocateIDs(c, keys), ShouldBeNil)
167 So(len(keys), ShouldEqual, 100) 167 So(len(keys), ShouldEqual, 100)
168 168
169 // Assert that none of our keys share the same I D. 169 // Assert that none of our keys share the same I D.
170 ids := make(map[int64]struct{}) 170 ids := make(map[int64]struct{})
171 for _, k := range keys { 171 for _, k := range keys {
172 ids[k.IntID()] = struct{}{} 172 ids[k.IntID()] = struct{}{}
173 } 173 }
174 So(len(ids), ShouldEqual, len(keys)) 174 So(len(ids), ShouldEqual, len(keys))
175 175
176 // Put a new object and ensure that it is alloca ted an unused ID. 176 // Put a new object and ensure that it is alloca ted an unused ID.
177 f := &Foo{Val: 10} 177 f := &Foo{Val: 10}
178 » » » » So(ds.Put(f), ShouldBeNil) 178 » » » » So(ds.Put(c, f), ShouldBeNil)
179 » » » » k := ds.KeyForObj(f) 179 » » » » k := ds.KeyForObj(c, f)
180 So(k.String(), ShouldEqual, "dev~app::/Foo,102") 180 So(k.String(), ShouldEqual, "dev~app::/Foo,102")
181 181
182 _, ok := ids[k.IntID()] 182 _, ok := ids[k.IntID()]
183 So(ok, ShouldBeFalse) 183 So(ok, ShouldBeFalse)
184 }) 184 })
185 }) 185 })
186 186
187 Convey("implements DSTransactioner", func() { 187 Convey("implements DSTransactioner", func() {
188 Convey("Put", func() { 188 Convey("Put", func() {
189 f := &Foo{Val: 10} 189 f := &Foo{Val: 10}
190 » » » » So(ds.Put(f), ShouldBeNil) 190 » » » » So(ds.Put(c, f), ShouldBeNil)
191 » » » » k := ds.KeyForObj(f) 191 » » » » k := ds.KeyForObj(c, f)
192 So(k.String(), ShouldEqual, "dev~app::/Foo,1") 192 So(k.String(), ShouldEqual, "dev~app::/Foo,1")
193 193
194 Convey("can describe its transaction state", fun c() {
195 So(ds.CurrentTransaction(c), ShouldBeNil )
dnj 2016/09/01 15:25:40 This test makes me happy.
iannucci 2016/09/16 01:01:13 :D
196
197 err := ds.RunInTransaction(c, func(c con text.Context) error {
198 So(ds.CurrentTransaction(c), Sho uldNotBeNil)
199
200 // Can reset to nil.
201 nc := ds.WithTransaction(c, nil)
202 So(ds.CurrentTransaction(nc), Sh ouldBeNil)
203 return nil
204 }, nil)
205 So(err, ShouldBeNil)
206 })
207
194 Convey("can Put new entity groups", func() { 208 Convey("can Put new entity groups", func() {
195 » » » » » err := ds.RunInTransaction(func(c contex t.Context) error { 209 » » » » » err := ds.RunInTransaction(c, func(c con text.Context) error {
196 » » » » » » ds := dsS.Get(c)
197
198 f := &Foo{Val: 100} 210 f := &Foo{Val: 100}
199 » » » » » » So(ds.Put(f), ShouldBeNil) 211 » » » » » » So(ds.Put(c, f), ShouldBeNil)
200 So(f.ID, ShouldEqual, 2) 212 So(f.ID, ShouldEqual, 2)
201 213
202 f.ID = 0 214 f.ID = 0
203 f.Val = 200 215 f.Val = 200
204 » » » » » » So(ds.Put(f), ShouldBeNil) 216 » » » » » » So(ds.Put(c, f), ShouldBeNil)
205 So(f.ID, ShouldEqual, 3) 217 So(f.ID, ShouldEqual, 3)
206 218
207 return nil 219 return nil
208 » » » » » }, &dsS.TransactionOptions{XG: true}) 220 » » » » » }, &ds.TransactionOptions{XG: true})
209 So(err, ShouldBeNil) 221 So(err, ShouldBeNil)
210 222
211 f := &Foo{ID: 2} 223 f := &Foo{ID: 2}
212 » » » » » So(ds.Get(f), ShouldBeNil) 224 » » » » » So(ds.Get(c, f), ShouldBeNil)
213 So(f.Val, ShouldEqual, 100) 225 So(f.Val, ShouldEqual, 100)
214 226
215 f.ID = 3 227 f.ID = 3
216 » » » » » So(ds.Get(f), ShouldBeNil) 228 » » » » » So(ds.Get(c, f), ShouldBeNil)
217 So(f.Val, ShouldEqual, 200) 229 So(f.Val, ShouldEqual, 200)
218 }) 230 })
219 231
220 Convey("can Put new entities in a current group" , func() { 232 Convey("can Put new entities in a current group" , func() {
221 » » » » » err := ds.RunInTransaction(func(c contex t.Context) error { 233 » » » » » err := ds.RunInTransaction(c, func(c con text.Context) error {
222 » » » » » » ds := dsS.Get(c)
223
224 f := &Foo{Val: 100, Parent: k} 234 f := &Foo{Val: 100, Parent: k}
225 » » » » » » So(ds.Put(f), ShouldBeNil) 235 » » » » » » So(ds.Put(c, f), ShouldBeNil)
226 » » » » » » So(ds.KeyForObj(f).String(), Sho uldEqual, "dev~app::/Foo,1/Foo,1") 236 » » » » » » So(ds.KeyForObj(c, f).String(), ShouldEqual, "dev~app::/Foo,1/Foo,1")
227 237
228 f.ID = 0 238 f.ID = 0
229 f.Val = 200 239 f.Val = 200
230 » » » » » » So(ds.Put(f), ShouldBeNil) 240 » » » » » » So(ds.Put(c, f), ShouldBeNil)
231 » » » » » » So(ds.KeyForObj(f).String(), Sho uldEqual, "dev~app::/Foo,1/Foo,2") 241 » » » » » » So(ds.KeyForObj(c, f).String(), ShouldEqual, "dev~app::/Foo,1/Foo,2")
232 242
233 return nil 243 return nil
234 }, nil) 244 }, nil)
235 So(err, ShouldBeNil) 245 So(err, ShouldBeNil)
236 246
237 f := &Foo{ID: 1, Parent: k} 247 f := &Foo{ID: 1, Parent: k}
238 » » » » » So(ds.Get(f), ShouldBeNil) 248 » » » » » So(ds.Get(c, f), ShouldBeNil)
239 So(f.Val, ShouldEqual, 100) 249 So(f.Val, ShouldEqual, 100)
240 250
241 f.ID = 2 251 f.ID = 2
242 » » » » » So(ds.Get(f), ShouldBeNil) 252 » » » » » So(ds.Get(c, f), ShouldBeNil)
243 So(f.Val, ShouldEqual, 200) 253 So(f.Val, ShouldEqual, 200)
244 }) 254 })
245 255
246 Convey("Deletes work too", func() { 256 Convey("Deletes work too", func() {
247 » » » » » err := ds.RunInTransaction(func(c contex t.Context) error { 257 » » » » » err := ds.RunInTransaction(c, func(c con text.Context) error {
248 » » » » » » return dsS.Get(c).Delete(k) 258 » » » » » » return ds.Delete(c, k)
249 }, nil) 259 }, nil)
250 So(err, ShouldBeNil) 260 So(err, ShouldBeNil)
251 » » » » » So(ds.Get(&Foo{ID: 1}), ShouldEqual, dsS .ErrNoSuchEntity) 261 » » » » » So(ds.Get(c, &Foo{ID: 1}), ShouldEqual, ds.ErrNoSuchEntity)
252 }) 262 })
253 263
254 Convey("A Get counts against your group count", func() { 264 Convey("A Get counts against your group count", func() {
255 » » » » » err := ds.RunInTransaction(func(c contex t.Context) error { 265 » » » » » err := ds.RunInTransaction(c, func(c con text.Context) error {
256 » » » » » » ds := dsS.Get(c) 266 » » » » » » pm := ds.PropertyMap{}
257 267 » » » » » » So(pm.SetMeta("key", ds.NewKey(c , "Foo", "", 20, nil)), ShouldBeTrue)
258 » » » » » » pm := dsS.PropertyMap{} 268 » » » » » » So(ds.Get(c, pm), ShouldEqual, d s.ErrNoSuchEntity)
259 » » » » » » So(pm.SetMeta("key", ds.NewKey(" Foo", "", 20, nil)), ShouldBeTrue)
260 » » » » » » So(ds.Get(pm), ShouldEqual, dsS. ErrNoSuchEntity)
261 269
262 So(pm.SetMeta("key", k), ShouldB eTrue) 270 So(pm.SetMeta("key", k), ShouldB eTrue)
263 » » » » » » So(ds.Get(pm).Error(), ShouldCon tainSubstring, "cross-group") 271 » » » » » » So(ds.Get(c, pm).Error(), Should ContainSubstring, "cross-group")
264 return nil 272 return nil
265 }, nil) 273 }, nil)
266 So(err, ShouldBeNil) 274 So(err, ShouldBeNil)
267 }) 275 })
268 276
269 Convey("Get takes a snapshot", func() { 277 Convey("Get takes a snapshot", func() {
270 » » » » » err := ds.RunInTransaction(func(c contex t.Context) error { 278 » » » » » err := ds.RunInTransaction(c, func(c con text.Context) error {
271 » » » » » » ds := dsS.Get(c) 279 » » » » » » So(ds.Get(c, f), ShouldBeNil)
272
273 » » » » » » So(ds.Get(f), ShouldBeNil)
274 So(f.Val, ShouldEqual, 10) 280 So(f.Val, ShouldEqual, 10)
275 281
276 // Don't ever do this in a real program unless you want to guarantee 282 // Don't ever do this in a real program unless you want to guarantee
277 // a failed transaction :) 283 // a failed transaction :)
278 f.Val = 11 284 f.Val = 11
279 » » » » » » So(dsS.GetNoTxn(c).Put(f), Shoul dBeNil) 285 » » » » » » So(ds.Put(ds.WithTransaction(c, nil), f), ShouldBeNil)
280 286
281 » » » » » » So(ds.Get(f), ShouldBeNil) 287 » » » » » » So(ds.Get(c, f), ShouldBeNil)
282 So(f.Val, ShouldEqual, 10) 288 So(f.Val, ShouldEqual, 10)
283 289
284 return nil 290 return nil
285 }, nil) 291 }, nil)
286 So(err, ShouldBeNil) 292 So(err, ShouldBeNil)
287 293
288 f := &Foo{ID: 1} 294 f := &Foo{ID: 1}
289 » » » » » So(ds.Get(f), ShouldBeNil) 295 » » » » » So(ds.Get(c, f), ShouldBeNil)
290 So(f.Val, ShouldEqual, 11) 296 So(f.Val, ShouldEqual, 11)
291 }) 297 })
292 298
293 Convey("and snapshots are consistent even after Puts", func() { 299 Convey("and snapshots are consistent even after Puts", func() {
294 » » » » » err := ds.RunInTransaction(func(c contex t.Context) error { 300 » » » » » err := ds.RunInTransaction(c, func(c con text.Context) error {
295 » » » » » » ds := dsS.Get(c)
296
297 f := &Foo{ID: 1} 301 f := &Foo{ID: 1}
298 » » » » » » So(ds.Get(f), ShouldBeNil) 302 » » » » » » So(ds.Get(c, f), ShouldBeNil)
299 So(f.Val, ShouldEqual, 10) 303 So(f.Val, ShouldEqual, 10)
300 304
301 // Don't ever do this in a real program unless you want to guarantee 305 // Don't ever do this in a real program unless you want to guarantee
302 // a failed transaction :) 306 // a failed transaction :)
303 f.Val = 11 307 f.Val = 11
304 » » » » » » So(dsS.GetNoTxn(c).Put(f), Shoul dBeNil) 308 » » » » » » So(ds.Put(ds.WithTransaction(c, nil), f), ShouldBeNil)
305 309
306 » » » » » » So(ds.Get(f), ShouldBeNil) 310 » » » » » » So(ds.Get(c, f), ShouldBeNil)
307 So(f.Val, ShouldEqual, 10) 311 So(f.Val, ShouldEqual, 10)
308 312
309 f.Val = 20 313 f.Val = 20
310 » » » » » » So(ds.Put(f), ShouldBeNil) 314 » » » » » » So(ds.Put(c, f), ShouldBeNil)
311 315
312 » » » » » » So(ds.Get(f), ShouldBeNil) 316 » » » » » » So(ds.Get(c, f), ShouldBeNil)
313 So(f.Val, ShouldEqual, 10) // st ill gets 10 317 So(f.Val, ShouldEqual, 10) // st ill gets 10
314 318
315 return nil 319 return nil
316 » » » » » }, &dsS.TransactionOptions{Attempts: 1}) 320 » » » » » }, &ds.TransactionOptions{Attempts: 1})
317 So(err.Error(), ShouldContainSubstring, "concurrent") 321 So(err.Error(), ShouldContainSubstring, "concurrent")
318 322
319 f := &Foo{ID: 1} 323 f := &Foo{ID: 1}
320 » » » » » So(ds.Get(f), ShouldBeNil) 324 » » » » » So(ds.Get(c, f), ShouldBeNil)
321 So(f.Val, ShouldEqual, 11) 325 So(f.Val, ShouldEqual, 11)
322 }) 326 })
323 327
324 Convey("Reusing a transaction context is bad new s", func() { 328 Convey("Reusing a transaction context is bad new s", func() {
325 » » » » » txnDS := dsS.Interface(nil) 329 » » » » » var txnCtx context.Context
326 » » » » » err := ds.RunInTransaction(func(c contex t.Context) error { 330 » » » » » err := ds.RunInTransaction(c, func(c con text.Context) error {
327 » » » » » » txnDS = dsS.Get(c) 331 » » » » » » txnCtx = c
328 » » » » » » So(txnDS.Get(f), ShouldBeNil) 332 » » » » » » So(ds.Get(c, f), ShouldBeNil)
329 return nil 333 return nil
330 }, nil) 334 }, nil)
331 So(err, ShouldBeNil) 335 So(err, ShouldBeNil)
332 » » » » » So(txnDS.Get(f).Error(), ShouldContainSu bstring, "expired") 336 » » » » » So(ds.Get(txnCtx, f).Error(), ShouldCont ainSubstring, "expired")
333 }) 337 })
334 338
335 Convey("Nested transactions are rejected", func( ) { 339 Convey("Nested transactions are rejected", func( ) {
336 » » » » » err := ds.RunInTransaction(func(c contex t.Context) error { 340 » » » » » err := ds.RunInTransaction(c, func(c con text.Context) error {
337 » » » » » » err := dsS.Get(c).RunInTransacti on(func(c context.Context) error { 341 » » » » » » err := ds.RunInTransaction(c, fu nc(c context.Context) error {
338 panic("noooo") 342 panic("noooo")
339 }, nil) 343 }, nil)
340 So(err.Error(), ShouldContainSub string, "nested transactions") 344 So(err.Error(), ShouldContainSub string, "nested transactions")
341 return nil 345 return nil
342 }, nil) 346 }, nil)
343 So(err, ShouldBeNil) 347 So(err, ShouldBeNil)
344 }) 348 })
345 349
350 Convey("Transactions can be escaped.", func() {
351 testError := errors.New("test error")
352 noTxnPM := ds.PropertyMap{
353 "$kind": []ds.Property{ds.MkProp erty("Test")},
354 "$id": []ds.Property{ds.MkProp erty("no txn")}}
355
356 err := ds.RunInTransaction(c, func(c con text.Context) error {
357 So(ds.CurrentTransaction(c), Sho uldNotBeNil)
358
359 pmap := ds.PropertyMap{
360 "$kind": []ds.Property{d s.MkProperty("Test")},
361 "$id": []ds.Property{d s.MkProperty("quux")}}
362 if err := ds.Put(c, pmap); err ! = nil {
363 return err
364 }
365
366 // Put an entity outside of the transaction so we can confirm that
367 // it was added even when the tr ansaction fails.
368 if err := ds.Put(ds.WithTransact ion(c, nil), noTxnPM); err != nil {
369 return err
370 }
371 return testError
372 }, nil)
373 So(err, ShouldEqual, testError)
374
375 // Confirm that noTxnPM was added.
376 So(ds.CurrentTransaction(c), ShouldBeNil )
377 So(ds.Get(c, noTxnPM), ShouldBeNil)
378 })
379
346 Convey("Concurrent transactions only accept one set of changes", func() { 380 Convey("Concurrent transactions only accept one set of changes", func() {
347 // Note: I think this implementation is actually /slightly/ wrong. 381 // Note: I think this implementation is actually /slightly/ wrong.
348 // According to my read of the docs for appengine, when you open a 382 // According to my read of the docs for appengine, when you open a
349 // transaction it actually (essentially) holds a reference to the 383 // transaction it actually (essentially) holds a reference to the
350 // entire datastore. Our implementation takes a snapshot of the 384 // entire datastore. Our implementation takes a snapshot of the
351 // entity group as soon as something obs erves/affects it. 385 // entity group as soon as something obs erves/affects it.
352 // 386 //
353 // That said... I'm not sure if there's really a semantic difference. 387 // That said... I'm not sure if there's really a semantic difference.
354 » » » » » err := ds.RunInTransaction(func(c contex t.Context) error { 388 » » » » » err := ds.RunInTransaction(c, func(c con text.Context) error {
355 » » » » » » So(dsS.Get(c).Put(&Foo{ID: 1, Va l: 21}), ShouldBeNil) 389 » » » » » » So(ds.Put(c, &Foo{ID: 1, Val: 21 }), ShouldBeNil)
356 390
357 » » » » » » err := dsS.GetNoTxn(c).RunInTran saction(func(c context.Context) error { 391 » » » » » » err := ds.RunInTransaction(ds.Wi thTransaction(c, nil), func(c context.Context) error {
358 » » » » » » » So(dsS.Get(c).Put(&Foo{I D: 1, Val: 27}), ShouldBeNil) 392 » » » » » » » So(ds.Put(c, &Foo{ID: 1, Val: 27}), ShouldBeNil)
359 return nil 393 return nil
360 }, nil) 394 }, nil)
361 So(err, ShouldBeNil) 395 So(err, ShouldBeNil)
362 396
363 return nil 397 return nil
364 }, nil) 398 }, nil)
365 So(err.Error(), ShouldContainSubstring, "concurrent") 399 So(err.Error(), ShouldContainSubstring, "concurrent")
366 400
367 f := &Foo{ID: 1} 401 f := &Foo{ID: 1}
368 » » » » » So(ds.Get(f), ShouldBeNil) 402 » » » » » So(ds.Get(c, f), ShouldBeNil)
369 So(f.Val, ShouldEqual, 27) 403 So(f.Val, ShouldEqual, 27)
370 }) 404 })
371 405
372 Convey("XG", func() { 406 Convey("XG", func() {
373 Convey("Modifying two groups with XG=fal se is invalid", func() { 407 Convey("Modifying two groups with XG=fal se is invalid", func() {
374 » » » » » » err := ds.RunInTransaction(func( c context.Context) error { 408 » » » » » » err := ds.RunInTransaction(c, fu nc(c context.Context) error {
375 » » » » » » » ds := dsS.Get(c)
376 f := &Foo{ID: 1, Val: 20 0} 409 f := &Foo{ID: 1, Val: 20 0}
377 » » » » » » » So(ds.Put(f), ShouldBeNi l) 410 » » » » » » » So(ds.Put(c, f), ShouldB eNil)
378 411
379 f.ID = 2 412 f.ID = 2
380 » » » » » » » err := ds.Put(f) 413 » » » » » » » err := ds.Put(c, f)
381 So(err.Error(), ShouldCo ntainSubstring, "cross-group") 414 So(err.Error(), ShouldCo ntainSubstring, "cross-group")
382 return err 415 return err
383 }, nil) 416 }, nil)
384 So(err.Error(), ShouldContainSub string, "cross-group") 417 So(err.Error(), ShouldContainSub string, "cross-group")
385 }) 418 })
386 419
387 Convey("Modifying >25 groups with XG=tru e is invald", func() { 420 Convey("Modifying >25 groups with XG=tru e is invald", func() {
388 » » » » » » err := ds.RunInTransaction(func( c context.Context) error { 421 » » » » » » err := ds.RunInTransaction(c, fu nc(c context.Context) error {
389 » » » » » » » ds := dsS.Get(c)
390 foos := make([]Foo, 25) 422 foos := make([]Foo, 25)
391 for i := int64(1); i < 2 6; i++ { 423 for i := int64(1); i < 2 6; i++ {
392 foos[i-1].ID = i 424 foos[i-1].ID = i
393 foos[i-1].Val = 200 425 foos[i-1].Val = 200
394 } 426 }
395 » » » » » » » So(ds.PutMulti(foos), Sh ouldBeNil) 427 » » » » » » » So(ds.Put(c, foos), Shou ldBeNil)
396 » » » » » » » err := ds.Put(&Foo{ID: 2 6}) 428 » » » » » » » err := ds.Put(c, &Foo{ID : 26})
397 So(err.Error(), ShouldCo ntainSubstring, "too many entity groups") 429 So(err.Error(), ShouldCo ntainSubstring, "too many entity groups")
398 return err 430 return err
399 » » » » » » }, &dsS.TransactionOptions{XG: t rue}) 431 » » » » » » }, &ds.TransactionOptions{XG: tr ue})
400 So(err.Error(), ShouldContainSub string, "too many entity groups") 432 So(err.Error(), ShouldContainSub string, "too many entity groups")
401 }) 433 })
402 }) 434 })
403 435
404 Convey("Errors and panics", func() { 436 Convey("Errors and panics", func() {
405 Convey("returning an error aborts", func () { 437 Convey("returning an error aborts", func () {
406 » » » » » » err := ds.RunInTransaction(func( c context.Context) error { 438 » » » » » » err := ds.RunInTransaction(c, fu nc(c context.Context) error {
407 » » » » » » » ds := dsS.Get(c) 439 » » » » » » » So(ds.Put(c, &Foo{ID: 1, Val: 200}), ShouldBeNil)
408 » » » » » » » So(ds.Put(&Foo{ID: 1, Va l: 200}), ShouldBeNil)
409 return fmt.Errorf("thing y") 440 return fmt.Errorf("thing y")
410 }, nil) 441 }, nil)
411 So(err.Error(), ShouldEqual, "th ingy") 442 So(err.Error(), ShouldEqual, "th ingy")
412 443
413 f := &Foo{ID: 1} 444 f := &Foo{ID: 1}
414 » » » » » » So(ds.Get(f), ShouldBeNil) 445 » » » » » » So(ds.Get(c, f), ShouldBeNil)
415 So(f.Val, ShouldEqual, 10) 446 So(f.Val, ShouldEqual, 10)
416 }) 447 })
417 448
418 Convey("panicing aborts", func() { 449 Convey("panicing aborts", func() {
419 So(func() { 450 So(func() {
420 » » » » » » » So(ds.RunInTransaction(f unc(c context.Context) error { 451 » » » » » » » So(ds.RunInTransaction(c , func(c context.Context) error {
421 » » » » » » » » ds := dsS.Get(c) 452 » » » » » » » » So(ds.Put(c, &Fo o{Val: 200}), ShouldBeNil)
422 » » » » » » » » So(ds.Put(&Foo{V al: 200}), ShouldBeNil)
423 panic("wheeeeee" ) 453 panic("wheeeeee" )
424 }, nil), ShouldBeNil) 454 }, nil), ShouldBeNil)
425 }, ShouldPanic) 455 }, ShouldPanic)
426 456
427 f := &Foo{ID: 1} 457 f := &Foo{ID: 1}
428 » » » » » » So(ds.Get(f), ShouldBeNil) 458 » » » » » » So(ds.Get(c, f), ShouldBeNil)
429 So(f.Val, ShouldEqual, 10) 459 So(f.Val, ShouldEqual, 10)
430 }) 460 })
431 }) 461 })
432 462
433 Convey("Transaction retries", func() { 463 Convey("Transaction retries", func() {
434 » » » » » tst := ds.Testable() 464 » » » » » tst := ds.GetTestable(c)
435 Reset(func() { tst.SetTransactionRetryCo unt(0) }) 465 Reset(func() { tst.SetTransactionRetryCo unt(0) })
436 466
437 Convey("SetTransactionRetryCount set to zero", func() { 467 Convey("SetTransactionRetryCount set to zero", func() {
438 tst.SetTransactionRetryCount(0) 468 tst.SetTransactionRetryCount(0)
439 calls := 0 469 calls := 0
440 » » » » » » So(ds.RunInTransaction(func(c co ntext.Context) error { 470 » » » » » » So(ds.RunInTransaction(c, func(c context.Context) error {
441 calls++ 471 calls++
442 return nil 472 return nil
443 }, nil), ShouldBeNil) 473 }, nil), ShouldBeNil)
444 So(calls, ShouldEqual, 1) 474 So(calls, ShouldEqual, 1)
445 }) 475 })
446 476
447 Convey("default TransactionOptions is 3 attempts", func() { 477 Convey("default TransactionOptions is 3 attempts", func() {
448 tst.SetTransactionRetryCount(100 ) // more than 3 478 tst.SetTransactionRetryCount(100 ) // more than 3
449 calls := 0 479 calls := 0
450 » » » » » » So(ds.RunInTransaction(func(c co ntext.Context) error { 480 » » » » » » So(ds.RunInTransaction(c, func(c context.Context) error {
451 calls++ 481 calls++
452 return nil 482 return nil
453 » » » » » » }, nil), ShouldEqual, dsS.ErrCon currentTransaction) 483 » » » » » » }, nil), ShouldEqual, ds.ErrConc urrentTransaction)
454 So(calls, ShouldEqual, 3) 484 So(calls, ShouldEqual, 3)
455 }) 485 })
456 486
457 Convey("non-default TransactionOptions " , func() { 487 Convey("non-default TransactionOptions " , func() {
458 tst.SetTransactionRetryCount(100 ) // more than 20 488 tst.SetTransactionRetryCount(100 ) // more than 20
459 calls := 0 489 calls := 0
460 » » » » » » So(ds.RunInTransaction(func(c co ntext.Context) error { 490 » » » » » » So(ds.RunInTransaction(c, func(c context.Context) error {
461 calls++ 491 calls++
462 return nil 492 return nil
463 » » » » » » }, &dsS.TransactionOptions{Attem pts: 20}), ShouldEqual, dsS.ErrConcurrentTransaction) 493 » » » » » » }, &ds.TransactionOptions{Attemp ts: 20}), ShouldEqual, ds.ErrConcurrentTransaction)
464 So(calls, ShouldEqual, 20) 494 So(calls, ShouldEqual, 20)
465 }) 495 })
466 496
467 Convey("SetTransactionRetryCount is resp ected", func() { 497 Convey("SetTransactionRetryCount is resp ected", func() {
468 tst.SetTransactionRetryCount(1) // less than 3 498 tst.SetTransactionRetryCount(1) // less than 3
469 calls := 0 499 calls := 0
470 » » » » » » So(ds.RunInTransaction(func(c co ntext.Context) error { 500 » » » » » » So(ds.RunInTransaction(c, func(c context.Context) error {
471 calls++ 501 calls++
472 return nil 502 return nil
473 }, nil), ShouldBeNil) 503 }, nil), ShouldBeNil)
474 So(calls, ShouldEqual, 2) 504 So(calls, ShouldEqual, 2)
475 }) 505 })
476 506
477 Convey("fatal errors are not retried", f unc() { 507 Convey("fatal errors are not retried", f unc() {
478 tst.SetTransactionRetryCount(1) 508 tst.SetTransactionRetryCount(1)
479 calls := 0 509 calls := 0
480 » » » » » » So(ds.RunInTransaction(func(c co ntext.Context) error { 510 » » » » » » So(ds.RunInTransaction(c, func(c context.Context) error {
481 calls++ 511 calls++
482 return fmt.Errorf("omg") 512 return fmt.Errorf("omg")
483 }, nil).Error(), ShouldEqual, "o mg") 513 }, nil).Error(), ShouldEqual, "o mg")
484 So(calls, ShouldEqual, 1) 514 So(calls, ShouldEqual, 1)
485 }) 515 })
486 }) 516 })
487 }) 517 })
488 }) 518 })
489 519
490 Convey("Testable.Consistent", func() { 520 Convey("Testable.Consistent", func() {
491 Convey("false", func() { 521 Convey("false", func() {
492 » » » » ds.Testable().Consistent(false) // the default 522 » » » » ds.GetTestable(c).Consistent(false) // the defau lt
493 for i := 0; i < 10; i++ { 523 for i := 0; i < 10; i++ {
494 » » » » » So(ds.Put(&Foo{ID: int64(i + 1), Val: i + 1}), ShouldBeNil) 524 » » » » » So(ds.Put(c, &Foo{ID: int64(i + 1), Val: i + 1}), ShouldBeNil)
495 } 525 }
496 » » » » q := dsS.NewQuery("Foo").Gt("Val", 3) 526 » » » » q := ds.NewQuery("Foo").Gt("Val", 3)
497 » » » » count, err := ds.Count(q) 527 » » » » count, err := ds.Count(c, q)
498 So(err, ShouldBeNil) 528 So(err, ShouldBeNil)
499 So(count, ShouldEqual, 0) 529 So(count, ShouldEqual, 0)
500 530
501 » » » » So(ds.Delete(ds.MakeKey("Foo", 4)), ShouldBeNil) 531 » » » » So(ds.Delete(c, ds.MakeKey(c, "Foo", 4)), Should BeNil)
502 532
503 » » » » count, err = ds.Count(q) 533 » » » » count, err = ds.Count(c, q)
504 So(err, ShouldBeNil) 534 So(err, ShouldBeNil)
505 So(count, ShouldEqual, 0) 535 So(count, ShouldEqual, 0)
506 536
507 » » » » ds.Testable().Consistent(true) 537 » » » » ds.GetTestable(c).Consistent(true)
508 » » » » count, err = ds.Count(q) 538 » » » » count, err = ds.Count(c, q)
509 So(err, ShouldBeNil) 539 So(err, ShouldBeNil)
510 So(count, ShouldEqual, 6) 540 So(count, ShouldEqual, 6)
511 }) 541 })
512 542
513 Convey("true", func() { 543 Convey("true", func() {
514 » » » » ds.Testable().Consistent(true) 544 » » » » ds.GetTestable(c).Consistent(true)
515 for i := 0; i < 10; i++ { 545 for i := 0; i < 10; i++ {
516 » » » » » So(ds.Put(&Foo{ID: int64(i + 1), Val: i + 1}), ShouldBeNil) 546 » » » » » So(ds.Put(c, &Foo{ID: int64(i + 1), Val: i + 1}), ShouldBeNil)
517 } 547 }
518 » » » » q := dsS.NewQuery("Foo").Gt("Val", 3) 548 » » » » q := ds.NewQuery("Foo").Gt("Val", 3)
519 » » » » count, err := ds.Count(q) 549 » » » » count, err := ds.Count(c, q)
520 So(err, ShouldBeNil) 550 So(err, ShouldBeNil)
521 So(count, ShouldEqual, 7) 551 So(count, ShouldEqual, 7)
522 552
523 » » » » So(ds.Delete(ds.MakeKey("Foo", 4)), ShouldBeNil) 553 » » » » So(ds.Delete(c, ds.MakeKey(c, "Foo", 4)), Should BeNil)
524 554
525 » » » » count, err = ds.Count(q) 555 » » » » count, err = ds.Count(c, q)
526 So(err, ShouldBeNil) 556 So(err, ShouldBeNil)
527 So(count, ShouldEqual, 6) 557 So(count, ShouldEqual, 6)
528 }) 558 })
529 }) 559 })
530 560
531 Convey("Testable.DisableSpecialEntities", func() { 561 Convey("Testable.DisableSpecialEntities", func() {
532 » » » ds.Testable().DisableSpecialEntities(true) 562 » » » ds.GetTestable(c).DisableSpecialEntities(true)
533 563
534 » » » So(ds.Put(&Foo{}), ShouldErrLike, "allocateIDs is disabl ed") 564 » » » So(ds.Put(c, &Foo{}), ShouldErrLike, "allocateIDs is dis abled")
535 565
536 » » » So(ds.Put(&Foo{ID: 1}), ShouldBeNil) 566 » » » So(ds.Put(c, &Foo{ID: 1}), ShouldBeNil)
537 567
538 » » » ds.Testable().CatchupIndexes() 568 » » » ds.GetTestable(c).CatchupIndexes()
539 569
540 » » » count, err := ds.Count(dsS.NewQuery("")) 570 » » » count, err := ds.Count(c, ds.NewQuery(""))
541 So(err, ShouldBeNil) 571 So(err, ShouldBeNil)
542 So(count, ShouldEqual, 1) // normally this would include __entity_group__ 572 So(count, ShouldEqual, 1) // normally this would include __entity_group__
543 }) 573 })
544 574
545 Convey("Datastore namespace interaction", func() { 575 Convey("Datastore namespace interaction", func() {
546 run := func(rc context.Context, txn bool) (putErr, getEr r, queryErr, countErr error) { 576 run := func(rc context.Context, txn bool) (putErr, getEr r, queryErr, countErr error) {
547 var foo Foo 577 var foo Foo
548 578
549 putFunc := func(doC context.Context) error { 579 putFunc := func(doC context.Context) error {
550 » » » » » return dsS.Get(doC).Put(&foo) 580 » » » » » return ds.Put(doC, &foo)
551 } 581 }
552 582
553 doFunc := func(doC context.Context) { 583 doFunc := func(doC context.Context) {
554 » » » » » ds := dsS.Get(doC) 584 » » » » » getErr = ds.Get(doC, &foo)
555 » » » » » getErr = ds.Get(&foo)
556 585
557 » » » » » q := dsS.NewQuery("Foo").Ancestor(ds.Key ForObj(&foo)) 586 » » » » » q := ds.NewQuery("Foo").Ancestor(ds.KeyF orObj(doC, &foo))
558 » » » » » queryErr = ds.Run(q, func(f *Foo) error { return nil }) 587 » » » » » queryErr = ds.Run(doC, q, func(f *Foo) e rror { return nil })
559 » » » » » _, countErr = ds.Count(q) 588 » » » » » _, countErr = ds.Count(doC, q)
560 } 589 }
561 590
562 if txn { 591 if txn {
563 » » » » » putErr = dsS.Get(rc).RunInTransaction(fu nc(ic context.Context) error { 592 » » » » » putErr = ds.RunInTransaction(rc, func(ic context.Context) error {
564 return putFunc(ic) 593 return putFunc(ic)
565 }, nil) 594 }, nil)
566 if putErr != nil { 595 if putErr != nil {
567 return 596 return
568 } 597 }
569 598
570 » » » » » dsS.Get(rc).Testable().CatchupIndexes() 599 » » » » » ds.GetTestable(rc).CatchupIndexes()
571 » » » » » dsS.Get(rc).RunInTransaction(func(ic con text.Context) error { 600 » » » » » ds.RunInTransaction(rc, func(ic context. Context) error {
572 doFunc(ic) 601 doFunc(ic)
573 return nil 602 return nil
574 }, nil) 603 }, nil)
575 } else { 604 } else {
576 putErr = putFunc(rc) 605 putErr = putFunc(rc)
577 if putErr != nil { 606 if putErr != nil {
578 return 607 return
579 } 608 }
580 » » » » » dsS.Get(rc).Testable().CatchupIndexes() 609 » » » » » ds.GetTestable(rc).CatchupIndexes()
581 doFunc(rc) 610 doFunc(rc)
582 } 611 }
583 return 612 return
584 } 613 }
585 614
586 for _, txn := range []bool{false, true} { 615 for _, txn := range []bool{false, true} {
587 Convey(fmt.Sprintf("In transaction? %v", txn), f unc() { 616 Convey(fmt.Sprintf("In transaction? %v", txn), f unc() {
588 Convey("With no namespace installed, can Put, Get, Query, and Count.", func() { 617 Convey("With no namespace installed, can Put, Get, Query, and Count.", func() {
589 » » » » » » _, has := infoS.Get(c).GetNamesp ace() 618 » » » » » » So(infoS.GetNamespace(c), Should Equal, "")
590 » » » » » » So(has, ShouldBeFalse)
591 619
592 putErr, getErr, queryErr, countE rr := run(c, txn) 620 putErr, getErr, queryErr, countE rr := run(c, txn)
593 So(putErr, ShouldBeNil) 621 So(putErr, ShouldBeNil)
594 So(getErr, ShouldBeNil) 622 So(getErr, ShouldBeNil)
595 So(queryErr, ShouldBeNil) 623 So(queryErr, ShouldBeNil)
596 So(countErr, ShouldBeNil) 624 So(countErr, ShouldBeNil)
597 }) 625 })
598 626
599 Convey("With a namespace installed, can Put, Get, Query, and Count.", func() { 627 Convey("With a namespace installed, can Put, Get, Query, and Count.", func() {
600 » » » » » » putErr, getErr, queryErr, countE rr := run(infoS.Get(c).MustNamespace("foo"), txn) 628 » » » » » » putErr, getErr, queryErr, countE rr := run(infoS.MustNamespace(c, "foo"), txn)
601 So(putErr, ShouldBeNil) 629 So(putErr, ShouldBeNil)
602 So(getErr, ShouldBeNil) 630 So(getErr, ShouldBeNil)
603 So(queryErr, ShouldBeNil) 631 So(queryErr, ShouldBeNil)
604 So(countErr, ShouldBeNil) 632 So(countErr, ShouldBeNil)
605 }) 633 })
606 }) 634 })
607 } 635 }
608 }) 636 })
609 }) 637 })
610 } 638 }
611 639
612 func TestCompoundIndexes(t *testing.T) { 640 func TestCompoundIndexes(t *testing.T) {
613 t.Parallel() 641 t.Parallel()
614 642
615 » idxKey := func(def dsS.IndexDefinition) string { 643 » idxKey := func(def ds.IndexDefinition) string {
616 So(def, ShouldNotBeNil) 644 So(def, ShouldNotBeNil)
617 return "idx::" + string(serialize.ToBytes(*def.PrepForIdxTable() )) 645 return "idx::" + string(serialize.ToBytes(*def.PrepForIdxTable() ))
618 } 646 }
619 647
620 numItms := func(c memCollection) uint64 { 648 numItms := func(c memCollection) uint64 {
621 ret, _ := c.GetTotals() 649 ret, _ := c.GetTotals()
622 return ret 650 return ret
623 } 651 }
624 652
625 Convey("Test Compound indexes", t, func() { 653 Convey("Test Compound indexes", t, func() {
626 type Model struct { 654 type Model struct {
627 ID int64 `gae:"$id"` 655 ID int64 `gae:"$id"`
628 656
629 Field1 []string 657 Field1 []string
630 Field2 []int64 658 Field2 []int64
631 } 659 }
632 660
633 c := Use(context.Background()) 661 c := Use(context.Background())
634 » » ds := dsS.Get(c) 662 » » t := ds.GetTestable(c).(*dsImpl)
635 » » t := ds.Testable().(*dsImpl)
636 head := t.data.head 663 head := t.data.head
637 664
638 » » So(ds.Put(&Model{1, []string{"hello", "world"}, []int64{10, 11}} ), ShouldBeNil) 665 » » So(ds.Put(c, &Model{1, []string{"hello", "world"}, []int64{10, 1 1}}), ShouldBeNil)
639 666
640 » » idx := dsS.IndexDefinition{ 667 » » idx := ds.IndexDefinition{
641 Kind: "Model", 668 Kind: "Model",
642 » » » SortBy: []dsS.IndexColumn{ 669 » » » SortBy: []ds.IndexColumn{
643 {Property: "Field2"}, 670 {Property: "Field2"},
644 }, 671 },
645 } 672 }
646 673
647 coll := head.GetCollection(idxKey(idx)) 674 coll := head.GetCollection(idxKey(idx))
648 So(coll, ShouldNotBeNil) 675 So(coll, ShouldNotBeNil)
649 So(numItms(coll), ShouldEqual, 2) 676 So(numItms(coll), ShouldEqual, 2)
650 677
651 idx.SortBy[0].Property = "Field1" 678 idx.SortBy[0].Property = "Field1"
652 coll = head.GetCollection(idxKey(idx)) 679 coll = head.GetCollection(idxKey(idx))
653 So(coll, ShouldNotBeNil) 680 So(coll, ShouldNotBeNil)
654 So(numItms(coll), ShouldEqual, 2) 681 So(numItms(coll), ShouldEqual, 2)
655 682
656 » » idx.SortBy = append(idx.SortBy, dsS.IndexColumn{Property: "Field 1"}) 683 » » idx.SortBy = append(idx.SortBy, ds.IndexColumn{Property: "Field1 "})
657 So(head.GetCollection(idxKey(idx)), ShouldBeNil) 684 So(head.GetCollection(idxKey(idx)), ShouldBeNil)
658 685
659 t.AddIndexes(&idx) 686 t.AddIndexes(&idx)
660 coll = head.GetCollection(idxKey(idx)) 687 coll = head.GetCollection(idxKey(idx))
661 So(coll, ShouldNotBeNil) 688 So(coll, ShouldNotBeNil)
662 So(numItms(coll), ShouldEqual, 4) 689 So(numItms(coll), ShouldEqual, 4)
663 }) 690 })
664 } 691 }
665 692
666 // High level test for regression in how zero time is stored, 693 // High level test for regression in how zero time is stored,
667 // see https://codereview.chromium.org/1334043003/ 694 // see https://codereview.chromium.org/1334043003/
668 func TestDefaultTimeField(t *testing.T) { 695 func TestDefaultTimeField(t *testing.T) {
669 t.Parallel() 696 t.Parallel()
670 697
671 Convey("Default time.Time{} can be stored", t, func() { 698 Convey("Default time.Time{} can be stored", t, func() {
672 type Model struct { 699 type Model struct {
673 ID int64 `gae:"$id"` 700 ID int64 `gae:"$id"`
674 Time time.Time 701 Time time.Time
675 } 702 }
676 » » ds := dsS.Get(Use(context.Background())) 703 » » c := Use(context.Background())
677 m := Model{ID: 1} 704 m := Model{ID: 1}
678 » » So(ds.Put(&m), ShouldBeNil) 705 » » So(ds.Put(c, &m), ShouldBeNil)
679 706
680 // Reset to something non zero to ensure zero is fetched. 707 // Reset to something non zero to ensure zero is fetched.
681 m.Time = time.Now().UTC() 708 m.Time = time.Now().UTC()
682 » » So(ds.Get(&m), ShouldBeNil) 709 » » So(ds.Get(c, &m), ShouldBeNil)
683 So(m.Time.IsZero(), ShouldBeTrue) 710 So(m.Time.IsZero(), ShouldBeTrue)
684 }) 711 })
685 } 712 }
686 713
687 func TestNewDatastore(t *testing.T) { 714 func TestNewDatastore(t *testing.T) {
688 t.Parallel() 715 t.Parallel()
689 716
690 Convey("Can get and use a NewDatastore", t, func() { 717 Convey("Can get and use a NewDatastore", t, func() {
691 c := UseWithAppID(context.Background(), "dev~aid") 718 c := UseWithAppID(context.Background(), "dev~aid")
692 » » c = infoS.Get(c).MustNamespace("ns") 719 » » c = infoS.MustNamespace(c, "ns")
693 » » ds := NewDatastore(infoS.Get(c))
694 720
695 » » k := ds.MakeKey("Something", 1) 721 » » dsInst := NewDatastore(c, infoS.Raw(c))
722 » » c = ds.SetRaw(c, dsInst)
723
724 » » k := ds.MakeKey(c, "Something", 1)
696 So(k.AppID(), ShouldEqual, "dev~aid") 725 So(k.AppID(), ShouldEqual, "dev~aid")
697 So(k.Namespace(), ShouldEqual, "ns") 726 So(k.Namespace(), ShouldEqual, "ns")
698 727
699 type Model struct { 728 type Model struct {
700 ID int64 `gae:"$id"` 729 ID int64 `gae:"$id"`
701 Value []int64 730 Value []int64
702 } 731 }
703 » » So(ds.Put(&Model{ID: 1, Value: []int64{20, 30}}), ShouldBeNil) 732 » » So(ds.Put(c, &Model{ID: 1, Value: []int64{20, 30}}), ShouldBeNil )
704 733
705 » » vals := []dsS.PropertyMap{} 734 » » vals := []ds.PropertyMap{}
706 » » So(ds.GetAll(dsS.NewQuery("Model").Project("Value"), &vals), Sho uldBeNil) 735 » » So(ds.GetAll(c, ds.NewQuery("Model").Project("Value"), &vals), S houldBeNil)
707 So(len(vals), ShouldEqual, 2) 736 So(len(vals), ShouldEqual, 2)
708 737
709 So(vals[0]["Value"][0].Value(), ShouldEqual, 20) 738 So(vals[0]["Value"][0].Value(), ShouldEqual, 20)
710 So(vals[1]["Value"][0].Value(), ShouldEqual, 30) 739 So(vals[1]["Value"][0].Value(), ShouldEqual, 30)
711 }) 740 })
712 } 741 }
713 742
714 func TestAddIndexes(t *testing.T) { 743 func TestAddIndexes(t *testing.T) {
715 t.Parallel() 744 t.Parallel()
716 745
717 Convey("Test Testable.AddIndexes", t, func() { 746 Convey("Test Testable.AddIndexes", t, func() {
718 ctx := UseWithAppID(context.Background(), "aid") 747 ctx := UseWithAppID(context.Background(), "aid")
719 namespaces := []string{"", "good", "news", "everyone"} 748 namespaces := []string{"", "good", "news", "everyone"}
720 749
721 Convey("After adding datastore entries, can query against indexe s in various namespaces", func() { 750 Convey("After adding datastore entries, can query against indexe s in various namespaces", func() {
722 foos := []*Foo{ 751 foos := []*Foo{
723 {ID: 1, Val: 1, Name: "foo"}, 752 {ID: 1, Val: 1, Name: "foo"},
724 {ID: 2, Val: 2, Name: "bar"}, 753 {ID: 2, Val: 2, Name: "bar"},
725 {ID: 3, Val: 2, Name: "baz"}, 754 {ID: 3, Val: 2, Name: "baz"},
726 } 755 }
727 for _, ns := range namespaces { 756 for _, ns := range namespaces {
728 » » » » So(dsS.Get(infoS.Get(ctx).MustNamespace(ns)).Put Multi(foos), ShouldBeNil) 757 » » » » So(ds.Put(infoS.MustNamespace(ctx, ns), foos), S houldBeNil)
729 } 758 }
730 759
731 // Initial query, no indexes, will fail. 760 // Initial query, no indexes, will fail.
732 » » » dsS.Get(ctx).Testable().CatchupIndexes() 761 » » » ds.GetTestable(ctx).CatchupIndexes()
733 762
734 var results []*Foo 763 var results []*Foo
735 » » » q := dsS.NewQuery("Foo").Eq("Val", 2).Gte("Name", "bar") 764 » » » q := ds.NewQuery("Foo").Eq("Val", 2).Gte("Name", "bar")
736 » » » So(dsS.Get(ctx).GetAll(q, &results), ShouldErrLike, "Ins ufficient indexes") 765 » » » So(ds.GetAll(ctx, q, &results), ShouldErrLike, "Insuffic ient indexes")
737 766
738 // Add index for default namespace. 767 // Add index for default namespace.
739 » » » dsS.Get(ctx).Testable().AddIndexes(&dsS.IndexDefinition{ 768 » » » ds.GetTestable(ctx).AddIndexes(&ds.IndexDefinition{
740 Kind: "Foo", 769 Kind: "Foo",
741 » » » » SortBy: []dsS.IndexColumn{ 770 » » » » SortBy: []ds.IndexColumn{
742 {Property: "Val"}, 771 {Property: "Val"},
743 {Property: "Name"}, 772 {Property: "Name"},
744 }, 773 },
745 }) 774 })
746 » » » dsS.Get(ctx).Testable().CatchupIndexes() 775 » » » ds.GetTestable(ctx).CatchupIndexes()
747 776
748 for _, ns := range namespaces { 777 for _, ns := range namespaces {
749 if ns == "" { 778 if ns == "" {
750 // Skip query test for empty namespace, as this is invalid. 779 // Skip query test for empty namespace, as this is invalid.
751 continue 780 continue
752 } 781 }
753 782
754 results = nil 783 results = nil
755 » » » » So(dsS.Get(infoS.Get(ctx).MustNamespace(ns)).Get All(q, &results), ShouldBeNil) 784 » » » » So(ds.GetAll(infoS.MustNamespace(ctx, ns), q, &r esults), ShouldBeNil)
756 So(len(results), ShouldEqual, 2) 785 So(len(results), ShouldEqual, 2)
757 } 786 }
758 787
759 // Add "foos" to a new namespace, then confirm that it g ets indexed. 788 // Add "foos" to a new namespace, then confirm that it g ets indexed.
760 » » » So(dsS.Get(infoS.Get(ctx).MustNamespace("qux")).PutMulti (foos), ShouldBeNil) 789 » » » So(ds.Put(infoS.MustNamespace(ctx, "qux"), foos), Should BeNil)
761 » » » dsS.Get(ctx).Testable().CatchupIndexes() 790 » » » ds.GetTestable(ctx).CatchupIndexes()
762 791
763 results = nil 792 results = nil
764 » » » So(dsS.Get(infoS.Get(ctx).MustNamespace("qux")).GetAll(q , &results), ShouldBeNil) 793 » » » So(ds.GetAll(infoS.MustNamespace(ctx, "qux"), q, &result s), ShouldBeNil)
765 So(len(results), ShouldEqual, 2) 794 So(len(results), ShouldEqual, 2)
766 }) 795 })
767 }) 796 })
768 } 797 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698