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 // +build appengine | 5 // +build appengine |
6 | 6 |
7 package prod | 7 package prod |
8 | 8 |
9 import ( | 9 import ( |
10 "testing" | 10 "testing" |
11 "time" | 11 "time" |
12 | 12 |
13 "github.com/luci/gae/service/blobstore" | 13 "github.com/luci/gae/service/blobstore" |
14 » "github.com/luci/gae/service/datastore" | 14 » ds "github.com/luci/gae/service/datastore" |
15 "github.com/luci/gae/service/info" | 15 "github.com/luci/gae/service/info" |
16 » "github.com/luci/gae/service/memcache" | 16 » mc "github.com/luci/gae/service/memcache" |
| 17 |
17 "github.com/luci/luci-go/common/logging" | 18 "github.com/luci/luci-go/common/logging" |
18 » . "github.com/smartystreets/goconvey/convey" | 19 |
19 "golang.org/x/net/context" | 20 "golang.org/x/net/context" |
20 "google.golang.org/appengine/aetest" | 21 "google.golang.org/appengine/aetest" |
| 22 |
| 23 . "github.com/smartystreets/goconvey/convey" |
21 ) | 24 ) |
22 | 25 |
23 var ( | 26 var ( |
24 » mp = datastore.MkProperty | 27 » mp = ds.MkProperty |
25 » mpNI = datastore.MkPropertyNI | 28 » mpNI = ds.MkPropertyNI |
26 ) | 29 ) |
27 | 30 |
28 type TestStruct struct { | 31 type TestStruct struct { |
29 ID int64 `gae:"$id"` | 32 ID int64 `gae:"$id"` |
30 | 33 |
31 ValueI []int64 | 34 ValueI []int64 |
32 ValueB []bool | 35 ValueB []bool |
33 ValueS []string | 36 ValueS []string |
34 ValueF []float64 | 37 ValueF []float64 |
35 ValueBS [][]byte // "ByteString" | 38 ValueBS [][]byte // "ByteString" |
(...skipping 11 matching lines...) Expand all Loading... |
47 inst, err := aetest.NewInstance(&aetest.Options{ | 50 inst, err := aetest.NewInstance(&aetest.Options{ |
48 StronglyConsistentDatastore: true, | 51 StronglyConsistentDatastore: true, |
49 }) | 52 }) |
50 So(err, ShouldBeNil) | 53 So(err, ShouldBeNil) |
51 defer inst.Close() | 54 defer inst.Close() |
52 | 55 |
53 req, err := inst.NewRequest("GET", "/", nil) | 56 req, err := inst.NewRequest("GET", "/", nil) |
54 So(err, ShouldBeNil) | 57 So(err, ShouldBeNil) |
55 | 58 |
56 ctx := Use(context.Background(), req) | 59 ctx := Use(context.Background(), req) |
57 ds := datastore.Get(ctx) | |
58 mc := memcache.Get(ctx) | |
59 inf := info.Get(ctx) | |
60 | 60 |
61 Convey("logging allows you to tweak the level", func() { | 61 Convey("logging allows you to tweak the level", func() { |
62 // You have to visually confirm that this actually happe
ns in the stdout | 62 // You have to visually confirm that this actually happe
ns in the stdout |
63 // of the test... yeah I know. | 63 // of the test... yeah I know. |
64 logging.Debugf(ctx, "SHOULD NOT SEE") | 64 logging.Debugf(ctx, "SHOULD NOT SEE") |
65 logging.Infof(ctx, "SHOULD SEE") | 65 logging.Infof(ctx, "SHOULD SEE") |
66 | 66 |
67 ctx = logging.SetLevel(ctx, logging.Debug) | 67 ctx = logging.SetLevel(ctx, logging.Debug) |
68 logging.Debugf(ctx, "SHOULD SEE") | 68 logging.Debugf(ctx, "SHOULD SEE") |
69 logging.Infof(ctx, "SHOULD SEE (2)") | 69 logging.Infof(ctx, "SHOULD SEE (2)") |
70 }) | 70 }) |
71 | 71 |
72 Convey("Can probe/change Namespace", func() { | 72 Convey("Can probe/change Namespace", func() { |
73 » » » ns, has := inf.GetNamespace() | 73 » » » So(info.GetNamespace(ctx), ShouldEqual, "") |
74 » » » So(ns, ShouldEqual, "") | |
75 » » » So(has, ShouldBeFalse) | |
76 | 74 |
77 » » » ctx, err = inf.Namespace("wat") | 75 » » » ctx, err = info.Namespace(ctx, "wat") |
78 So(err, ShouldBeNil) | 76 So(err, ShouldBeNil) |
79 inf = info.Get(ctx) | |
80 | 77 |
81 » » » ns, has = inf.GetNamespace() | 78 » » » So(info.GetNamespace(ctx), ShouldEqual, "wat") |
82 » » » So(ns, ShouldEqual, "wat") | 79 » » » So(ds.MakeKey(ctx, "Hello", "world").Namespace(), Should
Equal, "wat") |
83 » » » So(has, ShouldBeTrue) | |
84 | |
85 » » » ds = datastore.Get(ctx) | |
86 » » » So(ds.MakeKey("Hello", "world").Namespace(), ShouldEqual
, "wat") | |
87 }) | 80 }) |
88 | 81 |
89 Convey("Can get non-transactional context", func() { | 82 Convey("Can get non-transactional context", func() { |
90 » » » ctx, err := inf.Namespace("foo") | 83 » » » ctx, err := info.Namespace(ctx, "foo") |
91 So(err, ShouldBeNil) | 84 So(err, ShouldBeNil) |
92 ds = datastore.Get(ctx) | |
93 inf = info.Get(ctx) | |
94 | 85 |
95 » » » ds.RunInTransaction(func(ctx context.Context) error { | 86 » » » So(ds.CurrentTransaction(ctx), ShouldBeNil) |
96 » » » » So(ds.MakeKey("Foo", "bar").Namespace(), ShouldE
qual, "foo") | |
97 | 87 |
98 » » » » So(ds.Put(&TestStruct{ValueI: []int64{100}}), Sh
ouldBeNil) | 88 » » » ds.RunInTransaction(ctx, func(ctx context.Context) error
{ |
| 89 » » » » So(ds.CurrentTransaction(ctx), ShouldNotBeNil) |
| 90 » » » » So(ds.MakeKey(ctx, "Foo", "bar").Namespace(), Sh
ouldEqual, "foo") |
99 | 91 |
100 » » » » err = datastore.GetNoTxn(ctx).RunInTransaction(f
unc(ctx context.Context) error { | 92 » » » » So(ds.Put(ctx, &TestStruct{ValueI: []int64{100}}
), ShouldBeNil) |
101 » » » » » ds = datastore.Get(ctx) | 93 |
102 » » » » » So(ds.MakeKey("Foo", "bar").Namespace(),
ShouldEqual, "foo") | 94 » » » » noTxnCtx := ds.WithoutTransaction(ctx) |
103 » » » » » So(ds.Put(&TestStruct{ValueI: []int64{10
0}}), ShouldBeNil) | 95 » » » » So(ds.CurrentTransaction(noTxnCtx), ShouldBeNil) |
| 96 |
| 97 » » » » err = ds.RunInTransaction(noTxnCtx, func(ctx con
text.Context) error { |
| 98 » » » » » So(ds.CurrentTransaction(ctx), ShouldNot
BeNil) |
| 99 » » » » » So(ds.MakeKey(ctx, "Foo", "bar").Namespa
ce(), ShouldEqual, "foo") |
| 100 » » » » » So(ds.Put(ctx, &TestStruct{ValueI: []int
64{100}}), ShouldBeNil) |
104 return nil | 101 return nil |
105 }, nil) | 102 }, nil) |
106 So(err, ShouldBeNil) | 103 So(err, ShouldBeNil) |
107 | 104 |
108 return nil | 105 return nil |
109 }, nil) | 106 }, nil) |
110 }) | 107 }) |
111 | 108 |
112 Convey("Can Put/Get", func() { | 109 Convey("Can Put/Get", func() { |
113 orig := TestStruct{ | 110 orig := TestStruct{ |
114 ValueI: []int64{1, 7, 946688461000000, 996688461
000000}, | 111 ValueI: []int64{1, 7, 946688461000000, 996688461
000000}, |
115 ValueB: []bool{true, false}, | 112 ValueB: []bool{true, false}, |
116 ValueS: []string{"hello", "world"}, | 113 ValueS: []string{"hello", "world"}, |
117 ValueF: []float64{1.0, 7.0, 946688461000000.0, 9
96688461000000.0}, | 114 ValueF: []float64{1.0, 7.0, 946688461000000.0, 9
96688461000000.0}, |
118 ValueBS: [][]byte{ | 115 ValueBS: [][]byte{ |
119 []byte("allo"), | 116 []byte("allo"), |
120 []byte("hello"), | 117 []byte("hello"), |
121 []byte("world"), | 118 []byte("world"), |
122 []byte("zurple"), | 119 []byte("zurple"), |
123 }, | 120 }, |
124 » » » » ValueK: []*datastore.Key{ | 121 » » » » ValueK: []*ds.Key{ |
125 » » » » » ds.NewKey("Something", "Cool", 0, nil), | 122 » » » » » ds.NewKey(ctx, "Something", "Cool", 0, n
il), |
126 » » » » » ds.NewKey("Something", "", 1, nil), | 123 » » » » » ds.NewKey(ctx, "Something", "", 1, nil), |
127 » » » » » ds.NewKey("Something", "Recursive", 0, | 124 » » » » » ds.NewKey(ctx, "Something", "Recursive",
0, |
128 » » » » » » ds.NewKey("Parent", "", 2, nil))
, | 125 » » » » » » ds.NewKey(ctx, "Parent", "", 2,
nil)), |
129 }, | 126 }, |
130 ValueBK: []blobstore.Key{"bellow", "hello"}, | 127 ValueBK: []blobstore.Key{"bellow", "hello"}, |
131 » » » » ValueGP: []datastore.GeoPoint{ | 128 » » » » ValueGP: []ds.GeoPoint{ |
132 {Lat: 120.7, Lng: 95.5}, | 129 {Lat: 120.7, Lng: 95.5}, |
133 }, | 130 }, |
134 ValueSingle: "ohai", | 131 ValueSingle: "ohai", |
135 ValueSingleSlice: []string{"kthxbye"}, | 132 ValueSingleSlice: []string{"kthxbye"}, |
136 } | 133 } |
137 » » » So(ds.Put(&orig), ShouldBeNil) | 134 » » » So(ds.Put(ctx, &orig), ShouldBeNil) |
138 | 135 |
139 ret := TestStruct{ID: orig.ID} | 136 ret := TestStruct{ID: orig.ID} |
140 » » » So(ds.Get(&ret), ShouldBeNil) | 137 » » » So(ds.Get(ctx, &ret), ShouldBeNil) |
141 So(ret, ShouldResemble, orig) | 138 So(ret, ShouldResemble, orig) |
142 | 139 |
143 // make sure single- and multi- properties are preserved
. | 140 // make sure single- and multi- properties are preserved
. |
144 pmap := datastore.PropertyMap{ | 141 pmap := datastore.PropertyMap{ |
145 "$id": mpNI(orig.ID), | 142 "$id": mpNI(orig.ID), |
146 "$kind": mpNI("TestStruct"), | 143 "$kind": mpNI("TestStruct"), |
147 } | 144 } |
148 So(ds.Get(pmap), ShouldBeNil) | 145 So(ds.Get(pmap), ShouldBeNil) |
149 So(pmap["ValueSingle"], ShouldHaveSameTypeAs, datastore.
Property{}) | 146 So(pmap["ValueSingle"], ShouldHaveSameTypeAs, datastore.
Property{}) |
150 So(pmap["ValueSingleSlice"], ShouldHaveSameTypeAs, datas
tore.PropertySlice(nil)) | 147 So(pmap["ValueSingleSlice"], ShouldHaveSameTypeAs, datas
tore.PropertySlice(nil)) |
151 | 148 |
152 // can't be sure the indexes have caught up... so sleep | 149 // can't be sure the indexes have caught up... so sleep |
153 time.Sleep(time.Second) | 150 time.Sleep(time.Second) |
154 | 151 |
155 Convey("Can query", func() { | 152 Convey("Can query", func() { |
156 » » » » q := datastore.NewQuery("TestStruct") | 153 » » » » q := ds.NewQuery("TestStruct") |
157 » » » » ds.Run(q, func(ts *TestStruct) { | 154 » » » » ds.Run(ctx, q, func(ts *TestStruct) { |
158 So(*ts, ShouldResemble, orig) | 155 So(*ts, ShouldResemble, orig) |
159 }) | 156 }) |
160 » » » » count, err := ds.Count(q) | 157 » » » » count, err := ds.Count(ctx, q) |
161 So(err, ShouldBeNil) | 158 So(err, ShouldBeNil) |
162 So(count, ShouldEqual, 1) | 159 So(count, ShouldEqual, 1) |
163 }) | 160 }) |
164 | 161 |
165 Convey("Can project", func() { | 162 Convey("Can project", func() { |
166 » » » » q := datastore.NewQuery("TestStruct").Project("V
alueS") | 163 » » » » q := ds.NewQuery("TestStruct").Project("ValueS") |
167 » » » » rslts := []datastore.PropertyMap{} | 164 » » » » rslts := []ds.PropertyMap{} |
168 » » » » So(ds.GetAll(q, &rslts), ShouldBeNil) | 165 » » » » So(ds.GetAll(ctx, q, &rslts), ShouldBeNil) |
169 » » » » So(rslts, ShouldResemble, []datastore.PropertyMa
p{ | 166 » » » » So(rslts, ShouldResemble, []ds.PropertyMap{ |
170 { | 167 { |
171 » » » » » » "$key": mpNI(ds.KeyForObj(&ori
g)), | 168 » » » » » » "$key": mpNI(ds.KeyForObj(ctx,
&orig)), |
172 "ValueS": mp("hello"), | 169 "ValueS": mp("hello"), |
173 }, | 170 }, |
174 { | 171 { |
175 » » » » » » "$key": mpNI(ds.KeyForObj(&ori
g)), | 172 » » » » » » "$key": mpNI(ds.KeyForObj(ctx,
&orig)), |
176 "ValueS": mp("world"), | 173 "ValueS": mp("world"), |
177 }, | 174 }, |
178 }) | 175 }) |
179 | 176 |
180 » » » » q = datastore.NewQuery("TestStruct").Project("Va
lueBS") | 177 » » » » q = ds.NewQuery("TestStruct").Project("ValueBS") |
181 » » » » rslts = []datastore.PropertyMap{} | 178 » » » » rslts = []ds.PropertyMap{} |
182 » » » » So(ds.GetAll(q, &rslts), ShouldBeNil) | 179 » » » » So(ds.GetAll(ctx, q, &rslts), ShouldBeNil) |
183 » » » » So(rslts, ShouldResemble, []datastore.PropertyMa
p{ | 180 » » » » So(rslts, ShouldResemble, []ds.PropertyMap{ |
184 { | 181 { |
185 » » » » » » "$key": mpNI(ds.KeyForObj(&or
ig)), | 182 » » » » » » "$key": mpNI(ds.KeyForObj(ctx
, &orig)), |
186 "ValueBS": mp("allo"), | 183 "ValueBS": mp("allo"), |
187 }, | 184 }, |
188 { | 185 { |
189 » » » » » » "$key": mpNI(ds.KeyForObj(&or
ig)), | 186 » » » » » » "$key": mpNI(ds.KeyForObj(ctx
, &orig)), |
190 "ValueBS": mp("hello"), | 187 "ValueBS": mp("hello"), |
191 }, | 188 }, |
192 { | 189 { |
193 » » » » » » "$key": mpNI(ds.KeyForObj(&or
ig)), | 190 » » » » » » "$key": mpNI(ds.KeyForObj(ctx
, &orig)), |
194 "ValueBS": mp("world"), | 191 "ValueBS": mp("world"), |
195 }, | 192 }, |
196 { | 193 { |
197 » » » » » » "$key": mpNI(ds.KeyForObj(&or
ig)), | 194 » » » » » » "$key": mpNI(ds.KeyForObj(ctx
, &orig)), |
198 "ValueBS": mp("zurple"), | 195 "ValueBS": mp("zurple"), |
199 }, | 196 }, |
200 }) | 197 }) |
201 | 198 |
202 » » » » count, err := ds.Count(q) | 199 » » » » count, err := ds.Count(ctx, q) |
203 So(err, ShouldBeNil) | 200 So(err, ShouldBeNil) |
204 So(count, ShouldEqual, 4) | 201 So(count, ShouldEqual, 4) |
205 | 202 |
206 » » » » q = datastore.NewQuery("TestStruct").Lte("ValueI
", 7).Project("ValueS").Distinct(true) | 203 » » » » q = ds.NewQuery("TestStruct").Lte("ValueI", 7).P
roject("ValueS").Distinct(true) |
207 » » » » rslts = []datastore.PropertyMap{} | 204 » » » » rslts = []ds.PropertyMap{} |
208 » » » » So(ds.GetAll(q, &rslts), ShouldBeNil) | 205 » » » » So(ds.GetAll(ctx, q, &rslts), ShouldBeNil) |
209 » » » » So(rslts, ShouldResemble, []datastore.PropertyMa
p{ | 206 » » » » So(rslts, ShouldResemble, []ds.PropertyMap{ |
210 { | 207 { |
211 » » » » » » "$key": mpNI(ds.KeyForObj(&ori
g)), | 208 » » » » » » "$key": mpNI(ds.KeyForObj(ctx,
&orig)), |
212 "ValueI": mp(1), | 209 "ValueI": mp(1), |
213 "ValueS": mp("hello"), | 210 "ValueS": mp("hello"), |
214 }, | 211 }, |
215 { | 212 { |
216 » » » » » » "$key": mpNI(ds.KeyForObj(&ori
g)), | 213 » » » » » » "$key": mpNI(ds.KeyForObj(ctx,
&orig)), |
217 "ValueI": mp(1), | 214 "ValueI": mp(1), |
218 "ValueS": mp("world"), | 215 "ValueS": mp("world"), |
219 }, | 216 }, |
220 { | 217 { |
221 » » » » » » "$key": mpNI(ds.KeyForObj(&ori
g)), | 218 » » » » » » "$key": mpNI(ds.KeyForObj(ctx,
&orig)), |
222 "ValueI": mp(7), | 219 "ValueI": mp(7), |
223 "ValueS": mp("hello"), | 220 "ValueS": mp("hello"), |
224 }, | 221 }, |
225 { | 222 { |
226 » » » » » » "$key": mpNI(ds.KeyForObj(&ori
g)), | 223 » » » » » » "$key": mpNI(ds.KeyForObj(ctx,
&orig)), |
227 "ValueI": mp(7), | 224 "ValueI": mp(7), |
228 "ValueS": mp("world"), | 225 "ValueS": mp("world"), |
229 }, | 226 }, |
230 }) | 227 }) |
231 | 228 |
232 » » » » count, err = ds.Count(q) | 229 » » » » count, err = ds.Count(ctx, q) |
233 So(err, ShouldBeNil) | 230 So(err, ShouldBeNil) |
234 So(count, ShouldEqual, 4) | 231 So(count, ShouldEqual, 4) |
235 }) | 232 }) |
236 }) | 233 }) |
237 | 234 |
238 Convey("Can Put/Get (time)", func() { | 235 Convey("Can Put/Get (time)", func() { |
239 // time comparisons in Go are wonky, so this is pulled o
ut | 236 // time comparisons in Go are wonky, so this is pulled o
ut |
240 pm := datastore.PropertyMap{ | 237 pm := datastore.PropertyMap{ |
241 » » » » "$key": mpNI(ds.NewKey("Something", "value", 0,
nil)), | 238 » » » » "$key": mpNI(ds.NewKey(ctx, "Something", "value"
, 0, nil)), |
242 "Time": datastore.PropertySlice{ | 239 "Time": datastore.PropertySlice{ |
243 mp(time.Date(1938, time.January, 1, 1, 1
, 1, 1, time.UTC)), | 240 mp(time.Date(1938, time.January, 1, 1, 1
, 1, 1, time.UTC)), |
244 mp(time.Time{}), | 241 mp(time.Time{}), |
245 }, | 242 }, |
246 } | 243 } |
247 » » » So(ds.Put(&pm), ShouldBeNil) | 244 » » » So(ds.Put(ctx, &pm), ShouldBeNil) |
248 | 245 |
249 » » » rslt := datastore.PropertyMap{} | 246 » » » rslt := ds.PropertyMap{} |
250 » » » rslt.SetMeta("key", ds.KeyForObj(pm)) | 247 » » » rslt.SetMeta("key", ds.KeyForObj(ctx, pm)) |
251 » » » So(ds.Get(&rslt), ShouldBeNil) | 248 » » » So(ds.Get(ctx, &rslt), ShouldBeNil) |
252 | 249 |
253 So(pm.Slice("Time")[0].Value(), ShouldResemble, rslt.Sli
ce("Time")[0].Value()) | 250 So(pm.Slice("Time")[0].Value(), ShouldResemble, rslt.Sli
ce("Time")[0].Value()) |
254 | 251 |
255 » » » q := datastore.NewQuery("Something").Project("Time") | 252 » » » q := ds.NewQuery("Something").Project("Time") |
256 » » » all := []datastore.PropertyMap{} | 253 » » » all := []ds.PropertyMap{} |
257 » » » So(ds.GetAll(q, &all), ShouldBeNil) | 254 » » » So(ds.GetAll(ctx, q, &all), ShouldBeNil) |
258 So(len(all), ShouldEqual, 2) | 255 So(len(all), ShouldEqual, 2) |
259 prop := all[0].Slice("Time")[0] | 256 prop := all[0].Slice("Time")[0] |
260 So(prop.Type(), ShouldEqual, datastore.PTInt) | 257 So(prop.Type(), ShouldEqual, datastore.PTInt) |
261 | 258 |
262 » » » tval, err := prop.Project(datastore.PTTime) | 259 » » » tval, err := prop.Project(ds.PTTime) |
263 So(err, ShouldBeNil) | 260 So(err, ShouldBeNil) |
264 So(tval, ShouldResemble, time.Time{}.UTC()) | 261 So(tval, ShouldResemble, time.Time{}.UTC()) |
265 | 262 |
266 tval, err = all[1].Slice("Time")[0].Project(datastore.PT
Time) | 263 tval, err = all[1].Slice("Time")[0].Project(datastore.PT
Time) |
267 So(err, ShouldBeNil) | 264 So(err, ShouldBeNil) |
268 So(tval, ShouldResemble, pm.Slice("Time")[0].Value()) | 265 So(tval, ShouldResemble, pm.Slice("Time")[0].Value()) |
269 | 266 |
270 ent := datastore.PropertyMap{ | 267 ent := datastore.PropertyMap{ |
271 » » » » "$key": mpNI(ds.MakeKey("Something", "value")), | 268 » » » » "$key": mpNI(ds.MakeKey(ctx, "Something", "value
")), |
272 } | 269 } |
273 » » » So(ds.Get(&ent), ShouldBeNil) | 270 » » » So(ds.Get(ctx, &ent), ShouldBeNil) |
274 So(ent["Time"], ShouldResemble, pm["Time"]) | 271 So(ent["Time"], ShouldResemble, pm["Time"]) |
275 }) | 272 }) |
276 | 273 |
277 Convey("memcache: Set (nil) is the same as Set ([]byte{})", func
() { | 274 Convey("memcache: Set (nil) is the same as Set ([]byte{})", func
() { |
278 » » » So(mc.Set(mc.NewItem("bob")), ShouldBeNil) // normally w
ould panic because Value is nil | 275 » » » So(mc.Set(ctx, mc.NewItem(ctx, "bob")), ShouldBeNil) //
normally would panic because Value is nil |
279 | 276 |
280 » » » bob, err := mc.Get("bob") | 277 » » » bob, err := mc.GetKey(ctx, "bob") |
281 So(err, ShouldBeNil) | 278 So(err, ShouldBeNil) |
282 So(bob.Value(), ShouldResemble, []byte{}) | 279 So(bob.Value(), ShouldResemble, []byte{}) |
283 }) | 280 }) |
284 }) | 281 }) |
285 } | 282 } |
OLD | NEW |