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