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