OLD | NEW |
---|---|
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 } |
OLD | NEW |