OLD | NEW |
1 // Copyright 2016 The LUCI Authors. All rights reserved. | 1 // Copyright 2016 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 cloud | 5 package cloud |
6 | 6 |
7 import ( | 7 import ( |
8 "crypto/rand" | 8 "crypto/rand" |
9 "encoding/hex" | 9 "encoding/hex" |
10 "fmt" | 10 "fmt" |
11 "os" | 11 "os" |
12 "testing" | 12 "testing" |
13 "time" | 13 "time" |
14 | 14 |
15 ds "github.com/luci/gae/service/datastore" | 15 ds "github.com/luci/gae/service/datastore" |
16 "github.com/luci/gae/service/info" | 16 "github.com/luci/gae/service/info" |
17 | 17 |
18 "cloud.google.com/go/datastore" | 18 "cloud.google.com/go/datastore" |
19 "github.com/luci/luci-go/common/errors" | 19 "github.com/luci/luci-go/common/errors" |
20 "golang.org/x/net/context" | 20 "golang.org/x/net/context" |
21 | 21 |
22 . "github.com/smartystreets/goconvey/convey" | 22 . "github.com/smartystreets/goconvey/convey" |
23 ) | 23 ) |
24 | 24 |
25 func mkProperties(index bool, vals ...interface{}) []ds.Property { | 25 func mkProperties(index bool, forceMulti bool, vals ...interface{}) ds.PropertyD
ata { |
26 indexSetting := ds.ShouldIndex | 26 indexSetting := ds.ShouldIndex |
27 if !index { | 27 if !index { |
28 indexSetting = ds.NoIndex | 28 indexSetting = ds.NoIndex |
29 } | 29 } |
30 | 30 |
31 » result := make([]ds.Property, len(vals)) | 31 » if len(vals) == 1 && !forceMulti { |
| 32 » » var prop ds.Property |
| 33 » » prop.SetValue(vals[0], indexSetting) |
| 34 » » return prop |
| 35 » } |
| 36 |
| 37 » result := make(ds.PropertySlice, len(vals)) |
32 for i, v := range vals { | 38 for i, v := range vals { |
33 result[i].SetValue(v, indexSetting) | 39 result[i].SetValue(v, indexSetting) |
34 } | 40 } |
35 return result | 41 return result |
36 } | 42 } |
37 | 43 |
38 func mkp(vals ...interface{}) []ds.Property { return mkProperties(true, vals..
.) } | 44 func mkp(vals ...interface{}) ds.PropertyData { return mkProperties(true, fals
e, vals...) } |
39 func mkpNI(vals ...interface{}) []ds.Property { return mkProperties(false, vals.
..) } | 45 func mkpNI(vals ...interface{}) ds.PropertyData { return mkProperties(false, fal
se, vals...) } |
40 | 46 |
41 // TestDatastore tests the cloud datastore implementation. | 47 // TestDatastore tests the cloud datastore implementation. |
42 // | 48 // |
43 // This test uses the gcloud datastore emulator. Like the Go datastore package, | 49 // This test uses the gcloud datastore emulator. Like the Go datastore package, |
44 // the emulator must use the gRPC interface. At the time of writing, the | 50 // the emulator must use the gRPC interface. At the time of writing, the |
45 // emulator included with the "gcloud" tool is an older emulator that does NOT | 51 // emulator included with the "gcloud" tool is an older emulator that does NOT |
46 // support gRPC. | 52 // support gRPC. |
47 // | 53 // |
48 // Download the emulator linked here: | 54 // Download the emulator linked here: |
49 // https://code.google.com/p/google-cloud-sdk/issues/detail?id=719#c3 | 55 // https://code.google.com/p/google-cloud-sdk/issues/detail?id=719#c3 |
(...skipping 17 matching lines...) Expand all Loading... |
67 return | 73 return |
68 } | 74 } |
69 | 75 |
70 Convey(fmt.Sprintf(`A cloud installation using datastore emulator %q`, e
mulatorHost), t, func() { | 76 Convey(fmt.Sprintf(`A cloud installation using datastore emulator %q`, e
mulatorHost), t, func() { |
71 c := context.Background() | 77 c := context.Background() |
72 client, err := datastore.NewClient(c, "luci-gae-test") | 78 client, err := datastore.NewClient(c, "luci-gae-test") |
73 So(err, ShouldBeNil) | 79 So(err, ShouldBeNil) |
74 defer client.Close() | 80 defer client.Close() |
75 | 81 |
76 testTime := ds.RoundTime(time.Date(2016, 1, 1, 0, 0, 0, 0, time.
UTC)) | 82 testTime := ds.RoundTime(time.Date(2016, 1, 1, 0, 0, 0, 0, time.
UTC)) |
| 83 _ = testTime |
77 | 84 |
78 c = Use(c, client) | 85 c = Use(c, client) |
79 | 86 |
80 Convey(`Supports namespaces`, func() { | 87 Convey(`Supports namespaces`, func() { |
81 namespaces := []string{"foo", "bar", "baz"} | 88 namespaces := []string{"foo", "bar", "baz"} |
82 | 89 |
83 // Clear all used entities from all namespaces. | 90 // Clear all used entities from all namespaces. |
84 for _, ns := range namespaces { | 91 for _, ns := range namespaces { |
85 nsCtx := info.Get(c).MustNamespace(ns) | 92 nsCtx := info.Get(c).MustNamespace(ns) |
86 di := ds.Get(nsCtx) | 93 di := ds.Get(nsCtx) |
(...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
184 So(merr[1], ShouldEqual, ds.ErrNoSuchEntity) | 191 So(merr[1], ShouldEqual, ds.ErrNoSuchEntity) |
185 So(merr[2], ShouldBeNil) | 192 So(merr[2], ShouldBeNil) |
186 | 193 |
187 // put[1] will not be retrieved (delete) | 194 // put[1] will not be retrieved (delete) |
188 put[1] = get[1] | 195 put[1] = get[1] |
189 So(get, ShouldResemble, put) | 196 So(get, ShouldResemble, put) |
190 }) | 197 }) |
191 | 198 |
192 Convey(`Can put and get all supported entity fields.`, f
unc() { | 199 Convey(`Can put and get all supported entity fields.`, f
unc() { |
193 put := ds.PropertyMap{ | 200 put := ds.PropertyMap{ |
194 » » » » » "$id": mkpNI("foo"), | 201 » » » » » "$id": mkpNI("foo"), |
195 » » » » » "$kind": mkpNI("FooType"), | 202 » » » » » "$kind": mkpNI("FooType"), |
196 » » » » » "Number": mkp(1337), | 203 |
197 » » » » » "String": mkpNI("hello"), | 204 » » » » » "Number": mkp(1337), |
198 » » » » » "Bytes": mkp([]byte("world")), | 205 » » » » » "String": mkpNI("hello"), |
199 » » » » » "Time": mkp(testTime), | 206 » » » » » "Bytes": mkp([]byte("world")), |
200 » » » » » "Float": mkpNI(3.14), | 207 » » » » » "Time": mkp(testTime), |
201 » » » » » "Key": mkp(di.MakeKey("Parent", "Pare
ntID", "Child", 1337)), | 208 » » » » » "Float": mkpNI(3.14), |
| 209 » » » » » "Key": mkp(di.MakeKey("Parent", "P
arentID", "Child", 1337)), |
| 210 » » » » » "Null": mkp(nil), |
| 211 » » » » » "NullSlice": mkp(nil, nil), |
202 | 212 |
203 "ComplexSlice": mkp(1337, "string", []by
te("bytes"), testTime, float32(3.14), | 213 "ComplexSlice": mkp(1337, "string", []by
te("bytes"), testTime, float32(3.14), |
204 » » » » » » float64(2.71), true, di.MakeKey(
"SomeKey", "SomeID")), | 214 » » » » » » float64(2.71), true, nil, di.Mak
eKey("SomeKey", "SomeID")), |
| 215 |
| 216 » » » » » "Single": mkp("single"), |
| 217 » » » » » "SingleSlice": mkProperties(true, true,
"single"), // Force a single "multi" value. |
| 218 » » » » » "EmptySlice": ds.PropertySlice(nil), |
205 } | 219 } |
206 So(di.Put(put), ShouldBeNil) | 220 So(di.Put(put), ShouldBeNil) |
207 delete(put, "$key") | 221 delete(put, "$key") |
208 | 222 |
209 get := ds.PropertyMap{ | 223 get := ds.PropertyMap{ |
210 "$id": mkpNI("foo"), | 224 "$id": mkpNI("foo"), |
211 "$kind": mkpNI("FooType"), | 225 "$kind": mkpNI("FooType"), |
212 } | 226 } |
213 So(di.Get(get), ShouldBeNil) | 227 So(di.Get(get), ShouldBeNil) |
214 So(get, ShouldResemble, put) | 228 So(get, ShouldResemble, put) |
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
285 pmap := ds.PropertyMap{"$kind": mkp("Tes
t"), "$id": mkp("quux")} | 299 pmap := ds.PropertyMap{"$kind": mkp("Tes
t"), "$id": mkp("quux")} |
286 err = di.RunInTransaction(func(c context
.Context) error { | 300 err = di.RunInTransaction(func(c context
.Context) error { |
287 return ds.Get(c).Get(pmap) | 301 return ds.Get(c).Get(pmap) |
288 }, nil) | 302 }, nil) |
289 So(err, ShouldEqual, ds.ErrNoSuchEntity) | 303 So(err, ShouldEqual, ds.ErrNoSuchEntity) |
290 }) | 304 }) |
291 }) | 305 }) |
292 }) | 306 }) |
293 }) | 307 }) |
294 } | 308 } |
OLD | NEW |